Referring to my view controller in another class (scene.swift) - all variables turn to null? Swift 3

喜你入骨 提交于 2020-01-04 07:54:34

问题


I am trying to call a method from my view controller in my Scene.swift class - which I am able to do. The method is called after the touchesBegan method is called when I click on an SKLabel node, which are set up in my view controller.

The problem is that when I click on an SKLabelNode, control passes to the Scene.swift class and within the touchesBegan method, the method I want to call is called, so the control is passed back to the view controller, when I get back here it seems that all my variables are set to nil, as if its a completely different instance of the controller?

The error occurs in the checkIfValidTime method when I try to set the text property of a label in the ArViewController. - I highlighted these lines with **.

Error:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

How do I reference the same instance of the view controller so that the variables don't reset when I declare it in the Scene.Swift? Or is there a way I can implement the touchesBegan method in the view controller so that I don't have to instantiate the ARViewController?

I would appreciate any help on this matter as I have been stuck for a while now on it and I am new to iOS and swift app design.

I have tried to limit the code to what is necessary for me to explain this problem. Any questions just ask. Thanks

ARViewController:

    public var receivedCallback : Bool = false

    class ARViewController: UIViewController, ARSKViewDelegate, URLSessionDelegate {
        // MARK: - IBOutlets
        @IBOutlet weak var sceneView: ARSKView!
        @IBOutlet weak var guideLabel: UILabel!
        @IBOutlet weak var testLbl: UILabel!

 var scene : Scene?
        static var dateToUse : Date?
        var aRLocalDate : Date?

        var button: SKSpriteNode?

        override func viewDidLoad() {
            super.viewDidLoad()
        }

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            /*
             Start the view's AR session with a configuration that uses the rear camera,
             device position and orientation tracking, and plane detection..
             */
            let configuration = ARWorldTrackingConfiguration()

            guard ARWorldTrackingConfiguration.isSupported else {
                fatalError(""
                    ARKit is not available on this device."")
            }
            sceneView.session.run(configuration)
            sceneView.delegate = self
            if let scene = SKScene(fileNamed: "Scene"){
                self.scene = scene as! Scene
                sceneView.presentScene(self.scene)
            } else {
                print("Error: scene initalisation failed")
            }
            let overflow = ((aRLocalDate?.debugDescription.count)! - 11)
            let endIndex = aRLocalDate?.debugDescription.index((aRLocalDate?.debugDescription.endIndex)!, offsetBy: -overflow)
            if let truncatedDate = aRLocalDate?.debugDescription.substring(to: endIndex!){
                DateLabel.text = truncatedDate
            }  
        }

        **/// - Tag: PlaceARContent**
        func view(_ view : ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
            if self.Generated == false{
                self.guideLabel.alpha = 0
                parentNode = SKShapeNode(rectOf: CGSize(width: 400, height: 720))
                var count = 1;
                for time in timesArray {
                    **//add a SKSpriteNode and assign image to it**
                    **let labelNode : SKLabelNode = SKLabelNode(text: time)**
                    labelNode.name = "booklabel" + String(count)
                    labelNode.isUserInteractionEnabled = false;
                    parentNode?.addChild(labelNode)
                    posy -= 60
                    count += 1
                }
                parentNode?.alpha = 0.6
                self.Generated = true

                drawEventNodes()
                return parentNode
            }
            else {
                return nil
            }
        }

        //check if the booking is not in the past
        func checkIfValidTime(bookingTime: String, startDateTimeDate: Date) -> Bool {
            thisDate = ARViewController.dateToUse;
            let date = Date()
            let currentHour = Calendar.current.component(.hour, from: date)
            if (startDateTimeDate > date) {
                print("Start time is greater than the current date. = valid")
               **self.guideLabel.text = "Test"**
                return true;
            }
            else {
                print("Start time is not valid - before current time")
                **self.guideLabel.text = "Test"**
                return false;
            }
        }

        func doPost(bookingTime: String) {
            print("Start of post method")
            thisDate = ARViewController.dateToUse;
            roomToBook = globalVariables.roomDictionary[globalVariables.userString]!
            let name = globalVariables.userString;
            let date = Date()
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd"
            let displayName = globalVariables.userString
            let startDateStr = dateFormatter.string(from: thisDate!)
            let startHourString = bookingTime
            print("StartDateStr:", startDateStr)
            dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
            let startDateTimeString = "\(startDateStr)T\(startHourString)"
            let startDateTimeDate = dateFormatter.date(from: startDateTimeString)
            let endDateTimeDate = startDateTimeDate?.addingTimeInterval(3600)//3600 = 1 hour
            let endDateTimeString = dateFormatter.string(from: endDateTimeDate!)
            print("Start Date Time String", startDateTimeString)
            print("End date time string", endDateTimeString)
            print ("room to book: ",roomToBook)

            let valid = checkIfValidTime(bookingTime: bookingTime, startDateTimeDate: startDateTimeDate!)

            if (valid == true) {
                let jsonObject: [String: Any] =
                    [
                        "subject": "Booking",
                        "body":[
                            "contentType": "HTML",
                            "content": "Test Booking"
                        ],
                        "start":[
                            "dateTime": startDateTimeString,
                            "timeZone": "UTC"
                        ],
                        "end": [
                            "dateTime": endDateTimeString,
                            "timeZone": "UTC"
                        ],
                        "location":[
                            "displayName": displayName
                        ],
                        "attendees":[[
                            "emailAddress": [
                                "address": roomToBook,
                                "name": displayName
                            ],

                            "type": "required"
                            ]]
                ]

                //let valid = JSONSerialization.isValidJSONObject(jsonObject) // true
                let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject)

                // create post request
                let url = URL(string: "https://graph.microsoft.com/v1.0/me/events")!

                var request = URLRequest(url: url)
                request.setValue("Bearer \(globalVariables.accessToken)", forHTTPHeaderField: "Authorization")
                request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
                request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")        // the expected response is also JSON
                request.httpMethod = "POST"

                // insert json data to the request
                request.httpBody = jsonData

                let task = URLSession.shared.dataTask(with: request) { data, response, error in
                    guard let data = data, error == nil else {
                        print(error?.localizedDescription ?? "No data")
                        return
                    }
                    let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
                    if let responseJSON = responseJSON as? [String: Any] {
                        print(responseJSON)
                    }
                }

                task.resume()
                print("Post Done")
                print("Refreshing now")
                //code to refresh?

            }
            else {
                print("Invalid booking time - it is in the past.")
            }
        }    

Scene.Swift:

class Scene : SKScene{
    var controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ARStoryBoard") as! ARViewController
   // var controller: ARViewController!
    var bookingTime : String?

    override func touchesBegan(_ touches: Set<UITouch>, with event : UIEvent?) {
       // var c =  self.view?.window?.rootViewController as! ARViewController;

        for touch in touches {
            let location = touch.location(in: self)
            let node : SKNode = self.atPoint(location)
            let name = node.name
            switch name {
            case "booklabel1"?:
                controller.doPost(bookingTime: "08:00:00")
            case "booklabel2"?:
                controller.doPost(bookingTime: "09:00:00")
            case "booklabel3"?:
                controller.doPost(bookingTime: "10:00:00")
            case "booklabel4"?:
                controller.doPost(bookingTime: "11:00:00")
            case "booklabel5"?:
                controller.doPost(bookingTime: "12:00:00")
            case "booklabel6"?:
                controller.doPost(bookingTime: "13:00:00")
            case "booklabel7"?:
                controller.doPost(bookingTime: "14:00:00")
            case "booklabel8"?:
                controller.doPost(bookingTime: "15:00:00")
            case "booklabel9"?:
                controller.doPost(bookingTime: "16:00:00")
            case "booklabel10"?:
                controller.doPost(bookingTime: "17:00:00")
            case "booklabel11"?:
                controller.doPost(bookingTime: "18:00:00")
            default:
                print ("No Specific Label Clicked")
            }
        }
    }

}

回答1:


Try This.

ARViewController: Create instance outside the class

weak var arViewControllerInstance = ARViewController()

Make sure initialization inside ARViewController class:

 override func viewDidLoad() {
            super.viewDidLoad()
            arViewControllerInstance = self
        }

Now you can call in Scene.Swift using:

arViewControllerInstance?.doPost(bookingTime: "08:00:00")



回答2:


@Liam There is 'chain of ownership'. "Own" mean take responsibility for keeping object alive. Example Library, Reader and Book. Book had stamp of Library so it has reference to it, but definitely Book do not own Library. Same for reader. But both Library and Reader claims ownership of the book. If nobody take Book to Read declaring ownership) , and library is destroyed - them book is destroyed as well.

In Swift it is implemented via strong and weak references. All variables are strong by default.

Usually you view controllers instantiated by app delegate, or by other view controllers (manually or using storyboard). Anyone else can be used by view controllers, and can be aware about viewcontroller but do not "own" it. So variable had to be declared as weak. Mind that you don't own weak, so it had to be optional and can be nullified at any moment (if user navigate back), so you had to use additional guard.

To summarise:

1: Declare your viewController in the scene as weak:

class Scene : SKScene{
    weak var controller: ARViewController?
    ....*
}

2: Supply your scene with view controller. You can do it when creating Scene, or when setting it. For example:

 class ARViewController: UIViewController, ARSKViewDelegate, URLSessionDelegate {
 //...
 var scene : Scene? {
    didSet{
       //Optional: In case you can change scenes - remove view controller from old scene 
       oldValue?.controller = nil
       //Actually set view controller of any scene it "own"
       scene?.controller = self
    }
 }

PS: there is additional modifier "unowned". But it is more advance technique, can cause issues and crashes. I advice you to make yourself familiar with weak. Get used to guard retain cycles and then proceed.



来源:https://stackoverflow.com/questions/48927609/referring-to-my-view-controller-in-another-class-scene-swift-all-variables-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!