代码之家  ›  专栏  ›  技术社区  ›  Liam

在另一个类(scene.swift)中引用我的视图控制器-所有变量都变为null?Swift 3

  •  2
  • Liam  · 技术社区  · 6 年前

    我正在尝试从场景中的视图控制器调用一个方法。swift类-我能做到这一点。当我单击在我的视图控制器中设置的SKLabel节点时,在调用ToucheSbegind方法之后调用该方法。

    问题是,当我单击SKLabelNode时,控件会传递到场景。swift类和touchesbented方法中,我要调用的方法被调用,因此控件被传递回视图控制器,当我回到这里时,我的所有变量似乎都被设置为nil,好像它是控制器的一个完全不同的实例?

    当我尝试在ArViewController中设置标签的文本属性时,checkIfValidTime方法中出现错误。-我用**突出显示了这些行。

    错误:

    Fatal error: Unexpectedly found nil while unwrapping an Optional value
    

    如何引用视图控制器的同一实例,以便在场景中声明时不会重置变量。敏捷的 或者有没有一种方法可以在视图控制器中实现touchesbearth方法,这样就不必实例化ARViewController?

    我非常感谢在这件事上的任何帮助,因为我已经被困了一段时间了,而且我对iOS和swift应用程序设计还不熟悉。

    我试图将代码限制在解释此问题所需的范围内。 任何问题都可以问。谢谢

    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.")
                }
            }    
    

    场景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")
                }
            }
        }
    
    }
    
    2 回复  |  直到 6 年前
        1
  •  4
  •   Pranavan SP    3 年前

    试试这个。( 检查更新的编辑答案:强烈建议 )

    ARViewController:在类外创建实例

    weak var arViewControllerInstance = ARViewController()
    

    确保ARViewController类内的初始化:

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

    现在可以调用场景。Swift使用:

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

    已编辑:强烈建议

    上述方法简单,但强烈不建议用于最佳实践。请查看下面使用委托协议的实现。

    创建协议

    protocol ScenceArViewControllerDelegate {
        func doPost(bookingTime: String)
    }
    

    在ARViewController类中添加上述委托,如下所示。

    class ARViewController: UIViewController, ScenceArViewControllerDelegate{
        func doPost(bookingTime: String){
           //Funcion body goes here
        }
    }
    

    在场景类中创建一个委托变量,如下所示(Scene.swift)

    class Scene: SKScene{
        weak var delegateARVC: ScenceArViewControllerDelegate?
    }
    

    一旦你实现了这一切。现在您已声明 delegateARVC 变量来自 ARViewController 如下代码所示。(注意:您可以对set value使用依赖项注入,但下面只对set对象使用依赖项注入)

    class ARViewController: UIViewController, ScenceArViewControllerDelegate{
    
            var scene: Scene?
     
            override func viewDidLoad() {
                super.viewDidLoad()
                scene = Scene()
                scene.delegateARVC = self
            }
    
            func doPost(bookingTime: String){
               //Funcion body goes here
            }
        }
        
    

    现在一切都好了。现在,场景类知道它与 ARViewController 通过调用的方法 doPost 使用 ScenceArViewControllerDelegate .

    你可以打电话 AARViewController doPost公司 方法,如下所示。

    guard let delegateARCAvailable = delegateARVC else { return }
    delegateARCAvailable.doPost(bookingTime: "08:00:00")
    

    你可以随意申请。谢谢

        2
  •  1
  •   MichaelV    6 年前

    @Liam有“所有权链”。 “Own”是指负责使对象保持活动状态。示例库、读者和书籍。这本书有图书馆的印章,所以它有参考价值,但这本书肯定并没有自己的图书馆。读卡器也一样。但图书馆和读者都声称拥有这本书。若并没有人拿着书去读(声明所有权),那个么图书馆也会被毁——他们的书也会被毁。

    在Swift中,通过 strong weak 参考文献。所有变量均为 坚强的 默认情况下。

    通常,您可以查看由app delegate或其他视图控制器(手动或使用情节提要)实例化的控制器。其他任何人都可以被视图控制器使用,并且可以了解viewcontroller,但不“拥有”它。所以变量必须声明为 虚弱的 . 注意不要拥有 虚弱的 ,所以它必须是可选的,并且可以在任何时候为空(如果用户向后导航),所以您必须使用额外的保护。

    总结如下:

    1: 将场景中的viewController声明为弱:

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

    2: 为场景提供视图控制器。可以在创建场景或设置场景时执行此操作。例如:

     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
        }
     }
    

    附言:还有一个修饰语“unowned”。但这是一种更先进的技术,可能会导致问题和崩溃。我建议你熟悉 虚弱的 . 习惯 guard 保持循环,然后继续。

    推荐文章