[英]How to Pass Data Multiple Times from Parent View Controller to Container VC Child in Swift?
I have a button on ViewControllerA (Parent) that I want to update a variable inside of ViewControllerB (Child). 我在ViewControllerA(父级)上有一个按钮,想要更新ViewControllerB(子级)内的变量。 ViewControllerB is a container view inside of ViewControllerA.
ViewControllerB是ViewControllerA内部的容器视图。
This is the variable in ViewControllerB I want to update multiple times from the Parent ViewControllerA button press: 这是ViewControllerB中的变量,我想通过按父ViewControllerA按钮来多次更新:
@IBOutlet weak var childViewHeight: NSLayoutConstraint!
Because the child view, ViewControllerB, is connected by an embed segue, it seems I can only pass data from ViewControllerA to ViewControllerB once via the prepareForSegue
method. 因为子视图ViewControllerB通过嵌入的segue连接,看来我只能通过
prepareForSegue
方法将数据从ViewControllerA传递到ViewControllerB一次。 The performSegue
method causes the program to crash with a SIGABRT
Error. performSegue
方法导致程序崩溃,并出现SIGABRT
错误。
I know it is generally considered bad practice to try to update an IBOutlet
from a separate class or view controller, but I need a way for a button press on ViewControllerA to change the height constraint for ViewControllerA and ViewControllerB simultaneously. 我知道尝试从单独的类或视图控制器更新
IBOutlet
通常被认为是不好的做法,但是我需要一种方法,可以通过在ViewControllerA上按下按钮来同时更改ViewControllerA和ViewControllerB的高度限制。
If this is impossible in my current approach, please give me another suggestion of how to redesign my app to make this possible. 如果按照我目前的方法是不可能的,请给我另一个建议,说明如何重新设计我的应用程序以使之成为可能。
Update - This is the code that causes the crash: 更新-这是导致崩溃的代码:
@IBAction func button(_ sender: AnyObject) {
performSegue(withIdentifier: "seg", sender: self)
}
Update - Here is the result when I type in "bt" in the debug console: 更新-这是在调试控制台中输入“ 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
Update - Here is the "bt" console output with the exception breakpoint in place: 更新-这是带有异常断点的“ 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
and here is the "bt" output after I hit the "continue program execution" button once: 这是我单击“继续执行程序”按钮后的“ 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
UPDATE - prepareforsegue
code: 更新
prepareforsegue
代码:
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "seg" {
var vcB: ViewControllerB?
vcB = segue.desinationViewController as? ViewControllerB
}
You don't need to call performSegue
for an embed segue. 您无需调用
performSegue
获得嵌入的segue。 The embed segue is fired automatically when the containing view controller is loaded from the storyboard. 从情节提要中加载包含的视图控制器时,将自动触发嵌入segue。
You can use prepareForSegue
in the containing view controller to get a reference to the contained view controller (it will be the destinationViewController
in the segue). 您可以在包含的视图控制器中使用
prepareForSegue
来获取对包含的视图控制器的引用(它将是segue中的destinationViewController
)。 Once you have the reference you can store it in a property and use that to interact with it. 获得引用后,可以将其存储在属性中,并使用该属性与之交互。 Rather than updating the constraint directly, I would suggest that you invoke a function on the view controller that updates its constraint:
建议不要在视图控制器上调用一个函数来更新其约束,而不是直接更新该约束:
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
}
}
At the point that prepare for segue is called, ViewController B's outlets have not been set yet. 在调用segue进行准备时,尚未设置ViewController B的出口。 They are all nil and so trying to access one will cause a crash.
它们都为零,因此尝试访问一个将导致崩溃。
For what you are trying to do, the dirtiest approach is to change the layoutContraint's constant from inside view controller A by accessing it through the childViewControllers
array of view controller A. You will have to cast the first element in the array (assuming you only have one child) into an object of the view controller B class. 对于您想做的事情,最肮脏的方法是通过视图控制器A的
childViewControllers
数组访问它,从而从视图控制器A内部更改layoutContraint的常量。您将必须childViewControllers
数组中的第一个元素(假设您只有一个孩子)放入视图控制器B类的对象。
The above is a very dirty way to do it, but it will get the job done until you can learn some more. 上面的方法很脏,但是可以完成工作,直到您可以学到更多为止。
@IBAction buttonAction(sender: UIButton) {
if let viewControllerB = childViewControllers[0] as? ViewControllerB {
viewControllerB.childViewHeight.constant = 25.0
}
}
Again, this is a very dirty way to do it, but it will get the job done while you are learning the right way. 同样,这是一种非常肮脏的方式,但是当您学习正确的方式时,它将完成工作。
I've been asked why the above is such a bad idea... 我被问到为什么以上是一个坏主意...
Imagine that you didn't write the app in question, and you are tasked with fixing a bug with the constraint's constant. 想象一下,您没有编写有问题的应用程序,而您的任务是使用约束的常量修复错误。 The first thing you would need to do to fix the bug would be to find all the places that mutate that constant.
修复该错误所需要做的第一件事是找到所有使该常量变异的位置。
In an ideal world, that constant would only be mutated in one place (I have a single method in my ViewController class called updateUI
that handles all the IBOutlet mutation.) If that is the case, then fixing the bug will just be a matter of understanding the one function where the constant is getting changed. 在理想的世界中,该常数只会在一个地方发生突变(我的ViewController类中有一个名为
updateUI
,可以处理所有IBOutlet突变。)如果是这种情况,那么解决此问题将是一个问题。了解常数不断变化的一个函数。
The next best situation is that there is only one class that changes the constraint's constant. 其次,最好的情况是只有一个类会更改约束的常量。 Then all you have to do to fix the bug is to understand how the class works.
然后,解决该错误所需要做的就是了解该类的工作原理。 A larger task that understanding a single function, but doable.
理解单个功能但可行的较大任务。
However, using the code above means that there are multiple classes that adjust the constraint's constant, and if there are two classes doing it, who's to say there aren't more? 但是,使用上面的代码意味着有多个类可以调整约束的常量,如果有两个类可以执行该约束,那又意味着谁呢? In this case, fixing the bug requires an understanding of the entire app.
在这种情况下,修复错误需要了解整个应用程序。
A better way to do it... 更好的方法...
Presumably, you have multiple view controllers on the screen that are interconnected in some way through your Model code. 大概您在屏幕上有多个视图控制器,这些视图控制器通过模型代码以某种方式互连。 The model represents your source of truth.
该模型代表了您的真理来源。 Your two view controllers should be getting notified when the model changes and update themselves and their views accordingly.
当模型更改时,应该通知您的两个视图控制器,并相应地更新自身和视图。 If you don't have a model representing the abstraction that is affecting these two views, then you should make one.
如果没有代表影响这两个视图的抽象的模型,则应该制作一个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.