2016-09-14 19 views
0

我在ViewControllerA(Parent)上有一个按钮,我想更新ViewControllerB(Child)中的一个变量。 ViewControllerB是ViewControllerA中的容器视图。如何将数据从父视图控制器多次传递给Swift中的Container VC子项?

这是我想从父ViewControllerA按下按钮更新多次在ViewControllerB变量:

@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)当你说“视图”时,你真的是指“视图控制器”吗? 2)如果A在'prepareForSegue'方法中引用B,它可以保存该引用以便稍后使用。 3)你可以添加崩溃到你的问题的代码? 4)您需要在segue期间更新B中的不同属性,然后在加载B的视图时设置出口。 –

+0

@PhillipMills 1)是的(现在更新澄清)2)你能解释它如何保存一个参考以备后用? 3)它是perfromSegue方法(上面添加的代码)4)有没有办法做到这一点,当在ViewControllerA中按下按钮时,会立即更新childViewHeight变量?你能举一个例子说明这个(或者2)在代码中的样子吗? – PlateReverb

+0

假设你使用Swift 3并且正确定义了segue,那么为什么该行会崩溃没有明显的原因。我建议去Xcode的断点导航器,并设置所有例外的断点。当您的应用程序停止时,在调试控制台中输入“bt”并将结果添加到您的问题中。 –

回答

1

您无需致电performSegue查看嵌入式Segue。当从故事板加载包含的视图控制器时,嵌入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 
    } 
} 
0

在对于赛格瑞准备调用点,视图控制器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要理解常数正在发生变化的一个函数。

下一个最好的情况是,只有一个类改变了约束的常量。然后,你需要做的就是修正这个错误,以了解这个类是如何工作的。理解单一功能的更大任务,但可行。

但是,使用上面的代码意味着有多个类可以调整约束的常量,并且如果有两个类在这样做,谁说没有更多?在这种情况下,修复错误需要了解整个应用程序。


一个更好的办法来做到这一点...

想必,你有通过你的模型代码互连以某种方式在屏幕上的多个视图控制器。该模型代表你的真相来源。您的两个视图控制器应该在模型更改并相应更新自己和视图时得到通知。如果您没有表示影响这两个视图的抽象的模型,那么您应该创建一个。

+0

我想也许我应该再次重述我的问题......我还没有明确表示,我知道如何将数据从'prepareforsegue'传递到视图控制器B中的属性,然后此属性更新视图控制器B中所需的约束'viewdidload'正确。我只想知道如何在'prepareforsegue'之后类似地传递数据,并且在视图控制器A中使用IBAction函数多次传递数据。如果您的上述建议是实现此目的的一种方式,那么我将非常感谢如何以及在何处向在代码中实现'childViewControllers'。谢谢! – PlateReverb

+0

我用一个例子更新了我的答案。 –

+0

这工作!谢谢!!你能解释为什么这是一个“非常肮脏”的方式来做到这一点?通过这种方式开发通用应用程序,这种方式会不会导致我遇到问题?简而言之,你能描述一个更好的方式来做到这一点,我可以研究吗?再次感谢!!我一直在这个问题上停留一天半,终于有了一个可行的解决方案真是太棒了! – PlateReverb

相关问题