我在ViewControllerA(Parent)上有一个按钮,我想更新ViewControllerB(Child)中的变量。 ViewControllerB是ViewControllerA内部的容器视图。
这是ViewControllerB中的变量我想从Parent ViewControllerA按钮按下多次更新:
@IBOutlet weak var childViewHeight: NSLayoutConstraint!
因为子视图ViewControllerB是通过嵌入segue连接的,所以我似乎只能通过prepareForSegue
方法将数据从ViewControllerA传递到ViewControllerB一次。 performSegue
方法会导致程序因SIGABRT
错误而崩溃。
我知道尝试从单独的类或视图控制器更新IBOutlet
通常被认为是不好的做法,但我需要一种按钮按下ViewControllerA以同时更改ViewControllerA和ViewControllerB的高度约束
如果在我目前的方法中这是不可能的,请给我另一个建议,说明如何重新设计我的应用程序以实现这一目标。
更新 - 这是导致崩溃的代码:
@IBAction func button(_ sender: AnyObject) {
performSegue(withIdentifier: "seg", sender: self)
}
更新 - 这是我输入" bt"在调试控制台中:
* thread #1: tid = 0x1fcdd, 0x000000010d9b1f06 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x000000010d9b1f06 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x000000010dad24ec libsystem_pthread.dylib`pthread_kill + 90
frame #2: 0x000000010d7040b3 libsystem_c.dylib`abort + 129
frame #3: 0x000000010d9d043a libc++abi.dylib`abort_message + 266
frame #4: 0x000000010d9f4a9f libc++abi.dylib`default_terminate_handler() + 267
frame #5: 0x000000010c7b559f libobjc.A.dylib`_objc_terminate() + 103
frame #6: 0x000000010d9f1c09 libc++abi.dylib`std::__terminate(void (*)()) + 8
frame #7: 0x000000010d9f1894 libc++abi.dylib`__cxa_rethrow + 99
frame #8: 0x000000010c7b54b7 libobjc.A.dylib`objc_exception_rethrow + 40
frame #9: 0x000000010a2eebf1 CoreFoundation`CFRunLoopRunSpecific + 433
frame #10: 0x000000010f6d7a48 GraphicsServices`GSEventRunModal + 161
frame #11: 0x000000010ad27e8b UIKit`UIApplicationMain + 159
* frame #12: 0x000000010a1c60cf ContainerVC2`main + 111 at AppDelegate.swift:12
frame #13: 0x000000010d6586bd libdyld.dylib`start + 1
更新 - 这是" bt"具有异常断点的控制台输出:
* thread #1: tid = 0x219bd, 0x000000010afca2ee libobjc.A.dylib`objc_exception_throw, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010afca2ee libobjc.A.dylib`objc_exception_throw
frame #1: 0x0000000108b7dec2 CoreFoundation`+[NSException raise:format:arguments:] + 98
frame #2: 0x0000000109079455 Foundation`-[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
frame #3: 0x0000000109f65309 UIKit`__67-[UIStoryboardEmbedSegueTemplate newDefaultPerformHandlerForSegue:]_block_invoke + 438
frame #4: 0x0000000109ce05e4 UIKit`-[UIStoryboardSegueTemplate _performWithDestinationViewController:sender:] + 453
frame #5: 0x0000000109ce03ee UIKit`-[UIStoryboardSegueTemplate _perform:] + 82
frame #6: 0x00000001096dc45b UIKit`-[UIViewController performSegueWithIdentifier:sender:] + 99
* frame #7: 0x00000001089d99b3 ContainerVC2`ViewController1.button(sender=0x00007fff57224658, self=0x00007fcddb707cb0) -> () + 131 at ViewController.swift:9
frame #8: 0x00000001089d9a26 ContainerVC2`@objc ViewController1.button(AnyObject) -> () + 54 at ViewController.swift:0
frame #9: 0x000000010953eb6f UIKit`-[UIApplication sendAction:to:from:forEvent:] + 83
frame #10: 0x00000001096bf927 UIKit`-[UIControl sendAction:to:forEvent:] + 67
frame #11: 0x00000001096bfc08 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 388
frame #12: 0x00000001096be6aa UIKit`-[UIControl touchesBegan:withEvent:] + 414
frame #13: 0x00000001095aabbd UIKit`-[UIWindow _sendTouchesForEvent:] + 1188
frame #14: 0x00000001095ac8d6 UIKit`-[UIWindow sendEvent:] + 3984
frame #15: 0x000000010955a1e1 UIKit`-[UIApplication sendEvent:] + 281
frame #16: 0x0000000109d1502f UIKit`__dispatchPreprocessedEventFromEventQueue + 3314
frame #17: 0x0000000109d0dc4e UIKit`__handleEventQueue + 4879
frame #18: 0x0000000108b1fcb1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #19: 0x0000000108b04c6c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #20: 0x0000000108b04156 CoreFoundation`__CFRunLoopRun + 918
frame #21: 0x0000000108b03b5d CoreFoundation`CFRunLoopRunSpecific + 285
frame #22: 0x000000010deeca48 GraphicsServices`GSEventRunModal + 161
frame #23: 0x000000010953ce8b UIKit`UIApplicationMain + 159
frame #24: 0x00000001089db0cf ContainerVC2`main + 111 at AppDelegate.swift:12
frame #25: 0x000000010be6d6bd libdyld.dylib`start + 1
这是" bt"我点击"继续执行程序"按钮一次:
* thread #1: tid = 0x219bd, 0x000000010c206607 libc++abi.dylib`__cxa_throw, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
frame #0: 0x000000010c206607 libc++abi.dylib`__cxa_throw
frame #1: 0x000000010afca443 libobjc.A.dylib`objc_exception_throw + 341
frame #2: 0x0000000108b7dec2 CoreFoundation`+[NSException raise:format:arguments:] + 98
frame #3: 0x0000000109079455 Foundation`-[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
frame #4: 0x0000000109f65309 UIKit`__67-[UIStoryboardEmbedSegueTemplate newDefaultPerformHandlerForSegue:]_block_invoke + 438
frame #5: 0x0000000109ce05e4 UIKit`-[UIStoryboardSegueTemplate _performWithDestinationViewController:sender:] + 453
frame #6: 0x0000000109ce03ee UIKit`-[UIStoryboardSegueTemplate _perform:] + 82
frame #7: 0x00000001096dc45b UIKit`-[UIViewController performSegueWithIdentifier:sender:] + 99
* frame #8: 0x00000001089d99b3 ContainerVC2`ViewController1.button(sender=0x00007fff57224658, self=0x00007fcddb707cb0) -> () + 131 at ViewController.swift:9
frame #9: 0x00000001089d9a26 ContainerVC2`@objc ViewController1.button(AnyObject) -> () + 54 at ViewController.swift:0
frame #10: 0x000000010953eb6f UIKit`-[UIApplication sendAction:to:from:forEvent:] + 83
frame #11: 0x00000001096bf927 UIKit`-[UIControl sendAction:to:forEvent:] + 67
frame #12: 0x00000001096bfc08 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 388
frame #13: 0x00000001096be6aa UIKit`-[UIControl touchesBegan:withEvent:] + 414
frame #14: 0x00000001095aabbd UIKit`-[UIWindow _sendTouchesForEvent:] + 1188
frame #15: 0x00000001095ac8d6 UIKit`-[UIWindow sendEvent:] + 3984
frame #16: 0x000000010955a1e1 UIKit`-[UIApplication sendEvent:] + 281
frame #17: 0x0000000109d1502f UIKit`__dispatchPreprocessedEventFromEventQueue + 3314
frame #18: 0x0000000109d0dc4e UIKit`__handleEventQueue + 4879
frame #19: 0x0000000108b1fcb1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #20: 0x0000000108b04c6c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #21: 0x0000000108b04156 CoreFoundation`__CFRunLoopRun + 918
frame #22: 0x0000000108b03b5d CoreFoundation`CFRunLoopRunSpecific + 285
frame #23: 0x000000010deeca48 GraphicsServices`GSEventRunModal + 161
frame #24: 0x000000010953ce8b UIKit`UIApplicationMain + 159
frame #25: 0x00000001089db0cf ContainerVC2`main + 111 at AppDelegate.swift:12
frame #26: 0x000000010be6d6bd libdyld.dylib`start + 1
更新 - prepareforsegue
代码:
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "seg" {
var vcB: ViewControllerB?
vcB = segue.desinationViewController as? ViewControllerB
}
答案 0 :(得分:1)
您无需为嵌入segue调用performSegue
。从故事板加载包含视图控制器时,将自动触发嵌入segue。
您可以在包含视图控制器中使用prepareForSegue
来获取对包含的视图控制器的引用(它将是segue中的destinationViewController
)。获得引用后,您可以将其存储在属性中并使用它与其进行交互。我建议您在视图控制器上调用更新其约束的函数,而不是直接更新约束:
class ViewControllerA: UIViewController {
var viewControllerB: ViewControllerB?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "seg" {
self.viewControllerB = segue.destinationViewController as? ViewControllerB
}
}
@IBAction func button(_ sender: AnyObject) {
self.viewControllerB?.doSomethingWithHeight(newHeight)
}
}
class ViewControllerB: UIViewController {
@IBOutlet weak var childViewHeight: NSLayoutConstraint!
func doSomethingWithHeight(newHeight: CGFloat) {
self.childViewHeight.constant = newHeight
}
}
答案 1 :(得分:0)
在调用segue准备时,ViewController B的出口尚未设置。它们都是零,所以试图访问它会导致崩溃。
对于您要做的事情,最脏的方法是通过视图控制器A的childViewControllers
数组访问,从视图控制器A内部更改layoutContraint的常量。您将不得不进行强制转换数组中的第一个元素(假设您只有一个子元素)到视图控制器B类的对象中。
以上是一种非常肮脏的方法,但它会完成工作,直到你可以学到更多。
@IBAction buttonAction(sender: UIButton) {
if let viewControllerB = childViewControllers[0] as? ViewControllerB {
viewControllerB.childViewHeight.constant = 25.0
}
}
同样,这是一种非常脏的方法,但是当你正确地学习时,它将完成工作。
我被问到为什么上面这么糟糕......
想象一下,您没有编写有问题的应用程序,并且您的任务是修复约束常量的错误。你需要做的第一件事就是找到修改bug的所有地方。
在理想的世界中,该常量只会在一个地方发生变异(我的ViewController类中有一个名为updateUI
的方法可以处理所有IBOutlet变异。)如果是这种情况,那么修复bug只是理解常量变化的一个函数。
下一个最好的情况是只有一个类可以改变约束的常量。然后你需要做的就是修复bug,理解这个类是如何工作的。理解单个功能的更大任务,但可行。
但是,使用上面的代码意味着有多个类可以调整约束的常量,如果有两个类正在执行它,那么说谁还有更多?在这种情况下,修复bug需要了解整个应用程序。
更好的方法......
据推测,您在屏幕上有多个视图控制器,它们通过您的Model代码以某种方式互连。该模型代表了您的真实来源。当模型更改时,您的两个视图控制器应该会收到通知,并相应地更新自己及其视图。如果你没有表示影响这两个视图的抽象的模型,那么你应该创建一个。