简体   繁体   English

如何在Swift中将数据从父视图控制器多次传递到Container VC Child?

[英]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.

相关问题 如何将数据从子视图传递给父视图控制器? 在迅速 - How to pass data from child to parent view controller? in swift 如何将数据从父视图控制器传递到子容器视图控制器 - How to pass data from parent view controller to child container view controller 将数据从Child Modal VC传递到父视图控制器的最佳方法? - Best way to pass data from Child Modal VC to the Parent View Controller? 将数据从父 ViewController 传递到没有故事板的子 VC - Swift 5 - Pass data from a Parent ViewController to Child VC without Storyboards - Swift 5 如何将数据从视图 Controller 传递到 swift 中的容器视图 - How to pass data from View Controller to Container View in swift iOS-将数据从父视图多次传递到容器视图的视图 - iOS - Pass Data Multiple Times From Parent View Into Container View's View 将数据从父视图控制器传递到子视图控制器Swift 4 - Passing Data From Parent View Controller to Child View Controller Swift 4 如何快速将数据从第二个VC传递到第一个VC中的容器内的VC - How to pass data from a 2nd VC to a VC inside a container in 1st VC in swift Swift 将数据从子视图发送到父视图控制器 - Swift sending data from child view to parent view controller 在Swift中将数据从View Controller传递到Child Controller - Pass data from View Controller to Child Controller in Swift
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM