在另一个 class (scene.swift) 中引用我的视图控制器 - 所有变量都变为空? Swift 3
Referring to my view controller in another class (scene.swift) - all variables turn to null? Swift 3
我正在尝试从我的 Scene.swift class 中的视图控制器调用方法 - 我能够做到。当我点击在我的视图控制器中设置的 SKLabel 节点时调用 touchesBegan 方法后调用该方法。
问题是当我点击一个 SKLabelNode 时,控制传递给 Scene.swift class 并且在 touchesBegan 方法中,调用了我想调用的方法,所以控制被传递回到视图控制器,当我回到这里时,似乎我所有的变量都设置为 nil,好像它是一个完全不同的控制器实例?
当我尝试在 ArViewController 中设置标签的文本 属性 时,checkIfValidTime 方法中出现错误。 - 我用 ** 突出显示了这些行。
错误:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
如何引用视图控制器的相同实例,以便在 Scene.Swift 中声明时变量不会重置?
或者有什么方法可以在视图控制器中实现 touchesBegan 方法,这样我就不必实例化 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.")
}
}
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")
}
}
}
}
试试这个。 (检查更新后的编辑答案:强烈推荐)
ARViewController:在class
之外创建一个实例
weak var arViewControllerInstance = ARViewController()
确保在 ARViewController 内部初始化 class:
override func viewDidLoad() {
super.viewDidLoad()
arViewControllerInstance = self
}
现在您可以调用 Scene.Swift 使用:
arViewControllerInstance?.doPost(bookingTime: "08:00:00")
Edited: Highly recommended
以上方法简单但强烈不推荐用于最佳实践。看看下面使用委托协议的实现。
创建协议
protocol ScenceArViewControllerDelegate {
func doPost(bookingTime: String)
}
在 ARViewController class 中添加 Above 委托,如下所示。
class ARViewController: UIViewController, ScenceArViewControllerDelegate{
func doPost(bookingTime: String){
//Funcion body goes here
}
}
在场景中创建一个委托变量 class 如下 (Scene.swift)
class Scene: SKScene{
weak var delegateARVC: ScenceArViewControllerDelegate?
}
首先实施。现在你已经声明了来自 ARViewController
的 delegateARVC
变量,如下面的代码。 (注意:您可以使用依赖注入来设置值,但下面只是设置对象)
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
}
}
现在一切都很好。现在您的场景 class 知道它与 ARViewController
有一个关系引用,方法是使用 ScenceArViewControllerDelegate
.
调用的 doPost
您可以从场景 class.
中调用 AARViewController
的 doPost
方法,如下所示
guard let delegateARCAvailable = delegateARVC else { return }
delegateARCAvailable.doPost(bookingTime: "08:00:00")
想申请就申请吧。谢谢。
@Liam 有 'chain of ownership'。
"Own" 表示负责保持对象存活。示例库、Reader 和书籍。书上有图书馆的印章,所以它可以参考它,但绝对书不拥有图书馆。 reader 也一样。但是 Library 和 Reader 都声称拥有这本书的所有权。如果没有人拿书去读并声明所有权),并且图书馆被销毁 - 他们的书也被销毁。
在Swift中,它是通过strong
和weak
引用实现的。默认情况下,所有变量都是 strong
。
通常您会查看由应用委托或其他视图控制器(手动或使用故事板)实例化的控制器。任何其他人都可以被视图控制器使用,并且可以知道 viewcontroller 但不会 "own" 它。所以变量必须声明为 weak
。请注意,您不拥有 weak
,因此它必须是可选的并且可以随时取消(如果用户向后导航),因此您必须使用额外的保护措施。
总结一下:
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
}
}
PS:有附加修饰符"unowned"。但这是更先进的技术,可能会导致问题和崩溃。我建议您熟悉 weak
。习惯 guard
保留循环,然后继续。
我正在尝试从我的 Scene.swift class 中的视图控制器调用方法 - 我能够做到。当我点击在我的视图控制器中设置的 SKLabel 节点时调用 touchesBegan 方法后调用该方法。
问题是当我点击一个 SKLabelNode 时,控制传递给 Scene.swift class 并且在 touchesBegan 方法中,调用了我想调用的方法,所以控制被传递回到视图控制器,当我回到这里时,似乎我所有的变量都设置为 nil,好像它是一个完全不同的控制器实例?
当我尝试在 ArViewController 中设置标签的文本 属性 时,checkIfValidTime 方法中出现错误。 - 我用 ** 突出显示了这些行。
错误:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
如何引用视图控制器的相同实例,以便在 Scene.Swift 中声明时变量不会重置? 或者有什么方法可以在视图控制器中实现 touchesBegan 方法,这样我就不必实例化 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.")
}
}
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")
}
}
}
}
试试这个。 (检查更新后的编辑答案:强烈推荐)
ARViewController:在class
之外创建一个实例weak var arViewControllerInstance = ARViewController()
确保在 ARViewController 内部初始化 class:
override func viewDidLoad() {
super.viewDidLoad()
arViewControllerInstance = self
}
现在您可以调用 Scene.Swift 使用:
arViewControllerInstance?.doPost(bookingTime: "08:00:00")
Edited: Highly recommended
以上方法简单但强烈不推荐用于最佳实践。看看下面使用委托协议的实现。
创建协议
protocol ScenceArViewControllerDelegate {
func doPost(bookingTime: String)
}
在 ARViewController class 中添加 Above 委托,如下所示。
class ARViewController: UIViewController, ScenceArViewControllerDelegate{
func doPost(bookingTime: String){
//Funcion body goes here
}
}
在场景中创建一个委托变量 class 如下 (Scene.swift)
class Scene: SKScene{
weak var delegateARVC: ScenceArViewControllerDelegate?
}
首先实施。现在你已经声明了来自 ARViewController
的 delegateARVC
变量,如下面的代码。 (注意:您可以使用依赖注入来设置值,但下面只是设置对象)
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
}
}
现在一切都很好。现在您的场景 class 知道它与 ARViewController
有一个关系引用,方法是使用 ScenceArViewControllerDelegate
.
doPost
您可以从场景 class.
中调用AARViewController
的 doPost
方法,如下所示
guard let delegateARCAvailable = delegateARVC else { return }
delegateARCAvailable.doPost(bookingTime: "08:00:00")
想申请就申请吧。谢谢。
@Liam 有 'chain of ownership'。 "Own" 表示负责保持对象存活。示例库、Reader 和书籍。书上有图书馆的印章,所以它可以参考它,但绝对书不拥有图书馆。 reader 也一样。但是 Library 和 Reader 都声称拥有这本书的所有权。如果没有人拿书去读并声明所有权),并且图书馆被销毁 - 他们的书也被销毁。
在Swift中,它是通过strong
和weak
引用实现的。默认情况下,所有变量都是 strong
。
通常您会查看由应用委托或其他视图控制器(手动或使用故事板)实例化的控制器。任何其他人都可以被视图控制器使用,并且可以知道 viewcontroller 但不会 "own" 它。所以变量必须声明为 weak
。请注意,您不拥有 weak
,因此它必须是可选的并且可以随时取消(如果用户向后导航),因此您必须使用额外的保护措施。
总结一下:
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
}
}
PS:有附加修饰符"unowned"。但这是更先进的技术,可能会导致问题和崩溃。我建议您熟悉 weak
。习惯 guard
保留循环,然后继续。