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

时间:2016-09-14 12:09:42

标签: ios swift constraints iboutlet uicontainerview

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

2 个答案:

答案 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代码以某种方式互连。该模型代表了您的真实来源。当模型更改时,您的两个视图控制器应该会收到通知,并相应地更新自己及其视图。如果你没有表示影响这两个视图的抽象的模型,那么你应该创建一个。