iOS 8.3 UIAlertController在尝试添加文本字段时崩溃

时间:2015-04-10 22:16:06

标签: ios crash uialertcontroller ios8.3

我有一个iPad应用程序。我正在创建一个UIAlertController并添加一个文本字段。它崩溃了。它只在我添加文本字段时崩溃。

let alert = UIAlertController(title: "Enter Name", message:nil, preferredStyle: UIAlertControllerStyle.Alert);         
alert.addTextFieldWithConfigurationHandler { (textfield:UITextField!) -> Void in
                textfield.placeholder = "Sexy time";

            }
 alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {(action:UIAlertAction!) -> Void in
      //Some action here   
 }));

 self.presentViewController(alert, animated: true, completion: nil);

我得到一个有趣的崩溃告诉我,约束搞砸了。此代码在< 8.3没有警告。即使在一个干净的项目中,除了这个代码之外什么都没有,它崩溃了 - 该项目需要成为iPad上的splitview项目。

这是完整的堆栈跟踪加上奇怪的约束警告,只有在尝试将文本字段添加到alertController后才会出现。

2015-04-10 15:25:07.155 Observation[18235:281813] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7fb66cf9dfc0 UITableView:0x7fb66b855000.left == UIView:0x7fb66fae68e0.left>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2015-04-10 15:25:07.155 Observation[18235:281813] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7fb66cf9e010 UITableView:0x7fb66b855000.right == UIView:0x7fb66fae68e0.right>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2015-04-10 15:25:07.155 Observation[18235:281813] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7fb66fb37f90 UITableView:0x7fb66b855000.top == UIView:0x7fb66fae68e0.top>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2015-04-10 15:25:07.156 Observation[18235:281813] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7fb66fb80580 UITableView:0x7fb66b855000.bottom == UIView:0x7fb66fae68e0.bottom>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2015-04-10 15:25:13.589 Observation[18235:281813] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0x7fb66cf9dfc0 UITableView:0x7fb66b855000.left == UIView:0x7fb66fae68e0.left>
    Container hierarchy: 
<UIView: 0x7fb66fa86e00; frame = (0 0; 0 0); layer = <CALayer: 0x7fb66fadf8e0>>
   | <UIView: 0x7fb66af3e080; frame = (0 0; 0 0); clipsToBounds = YES; layer = <CALayer: 0x7fb66fae32c0>>
   |    | <_UIAlertControllerShadowedScrollView: 0x7fb66fa68c80; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fb66fa38a80>; layer = <CALayer: 0x7fb66fa97560>; contentOffset: {0, 0}; contentSize: {0, 0}>
   |    |    | <UIView: 0x7fb66fa87350; frame = (0 0; 0 0); layer = <CALayer: 0x7fb66fadf810>>
   |    |    |    | <UILabel: 0x7fb66fa88740; frame = (0 0; 0 0); text = 'Enter Name'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fb66fa94ed0>>
   |    |    |    | <UILabel: 0x7fb66fa73710; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fb66cc0ee10>>
   |    |    |    | <UIView: 0x7fb66fae68e0; frame = (0 0; 0 0); clipsToBounds = YES; layer = <CALayer: 0x7fb66fa90160>>
   |    | <UILabel: 0x7fb66fa3ad40; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fb66fa73680>>
   |    | <UICollectionView: 0x7fb66c130200; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fb66faebab0>; layer = <CALayer: 0x7fb66fa3acf0>; contentOffset: {0, 0}; contentSize: {0, 0}> collection view layout: <_UIAlertControllerCollectionViewFlowLayout: 0x7fb66fae0b30>
    View not found in container hierarchy: <UITableView: 0x7fb66b855000; frame = (0 20; 768 1004); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fb66cf79f30>; layer = <CALayer: 0x7fb66cf600a0>; contentOffset: {0, 0}; contentSize: {768, 25}>
    That view's superview: NO SUPERVIEW
2015-04-10 15:25:13.594 Observation[18235:281813] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal. constraint:<NSLayoutConstraint:0x7fb66cf9dfc0 UITableView:0x7fb66b855000.left == UIView:0x7fb66fae68e0.left> view:<UIView: 0x7fb66fa86e00; frame = (0 0; 0 0); layer = <CALayer: 0x7fb66fadf8e0>>'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000102940c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010221dbb7 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000102940b9d +[NSException raise:format:] + 205
    3   Foundation                          0x0000000101daf479 -[NSLayoutConstraint _addToEngine:integralizationAdjustment:mutuallyExclusiveConstraints:] + 187
    4   UIKit                               0x00000001039bca34 __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke_2 + 474
    5   Foundation                          0x0000000101dbd1be -[NSISEngine withBehaviors:performModifications:] + 155
    6   UIKit                               0x00000001039bc83a __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke + 452
    7   UIKit                               0x00000001039bc64d -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 197
    8   UIKit                               0x00000001039bc933 __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke_2 + 217
    9   Foundation                          0x0000000101dbd1be -[NSISEngine withBehaviors:performModifications:] + 155
    10  UIKit                               0x00000001039bc83a __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke + 452
    11  UIKit                               0x00000001039bc64d -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 197
    12  UIKit                               0x00000001039bc933 __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke_2 + 217
    13  Foundation                          0x0000000101dbd1be -[NSISEngine withBehaviors:performModifications:] + 155
    14  UIKit                               0x00000001039bc83a __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke + 452
    15  UIKit                               0x00000001039bc64d -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 197
    16  UIKit                               0x00000001033b5717 __40-[UIView(Hierarchy) layoutBelowIfNeeded]_block_invoke + 39
    17  Foundation                          0x0000000101dbd1be -[NSISEngine withBehaviors:performModifications:] + 155
    18  UIKit                               0x00000001033b5556 -[UIView(Hierarchy) layoutBelowIfNeeded] + 320
    19  UIKit                               0x000000010374a394 -[_UIAlertControllerAnimatedTransitioning animateTransition:] + 470
    20  UIKit                               0x000000010344fa4e __56-[UIPresentationController runTransitionForCurrentState]_block_invoke + 1867
    21  UIKit                               0x000000010336562c _applyBlockToCFArrayCopiedToStack + 314
    22  UIKit                               0x00000001033654a6 _afterCACommitHandler + 533
    23  CoreFoundation                      0x0000000102873ca7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    24  CoreFoundation                      0x0000000102873c00 __CFRunLoopDoObservers + 368
    25  CoreFoundation                      0x0000000102869a33 __CFRunLoopRun + 1123
    26  CoreFoundation                      0x0000000102869366 CFRunLoopRunSpecific + 470
    27  GraphicsServices                    0x0000000106dd6a3e GSEventRunModal + 161
    28  UIKit                               0x0000000103341900 UIApplicationMain + 1282
    29  Observation                         0x0000000101612927 main + 135
    30  libdyld.dylib                       0x0000000104f60145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

12 个答案:

答案 0 :(得分:13)

编辑:此修补程序会根据您是使用Xcode 6还是Xcode 7构建而更改,因此我已为两个版本的Xcode添加了相关信息。


我今天碰到了这个,它说的是它无法将文本字段添加到视图控制器的视图中,或者它无法向其超级视图添加自动布局约束。这似乎是因为它还没有创建超级视图来添加它,所以它都是恐慌和崩溃。

我发现的简单修复是在告诉警报控制器出现后添加文本字段。虽然我不确定它是否会影响像警报一样弹出的文本字段。

使用Xcode 6构建

let alertController = UIAlertController(title: "Enter Name", message:nil, preferredStyle: .Alert)

let okAction = UIAlertAction(title: "Ok", style: .Default) { (_) -> Void in
    // Some action here
}
alertController.addAction(okAction)

presentViewController(alertController, animated: true, completion: nil)

// Add any text fields after presenting the alert controller to fix crash in iOS 8.3
alertController.addTextFieldWithConfigurationHandler { (textfield) -> Void in
    textfield.placeholder = "Name"
}

P.S。作为代码示例的旁注,请记住使用Swift,您不需要在每行的末尾使用;,但如果您这样做则无关紧要。


使用Xcode 7构建

当您使用Xcode 7构建应用程序时,Apple似乎已经解决了这个问题。使用上面显示的方法将不再在iOS 9中显示文本字段(即使它仍然在iOS 8中正确显示)。

下面是代码片段,它应该是一直以来的样子,当在Xcode 7中内置时,它在iOS 8和iOS 9中正确运行。

let alertController = UIAlertController(title: "Enter Name", message:nil, preferredStyle: .Alert)

alertController.addTextFieldWithConfigurationHandler { (textfield) -> Void in
    textfield.placeholder = "Name"
}

let okAction = UIAlertAction(title: "Ok", style: .Default) { (_) -> Void in
    // Some action here
}
alertController.addAction(okAction)

presentViewController(alertController, animated: true, completion: nil)

使用Xcode 7 GM(7A218)在Swift 2.0和Obj-C中测试

答案 1 :(得分:6)

如果在调用-addTextFieldWithConfigurationHandler:[alert isViewLoaded] == YES)时加载了UIAlertController视图,则会触发此错误。

如果您在调用-addTextFieldWithConfigurationHandler之前访问alert.view,请在调用alert.view之后移动您对-addTextFieldWithConfigurationHandler:执行的操作。

答案 2 :(得分:4)

正如@Dex所提到的,我通过在添加文本字段后修改控制器的视图来修复此问题。我正在修改视图的色调。

controller.view.tintColor = ...some color...

在&#34;之后移动到&#34;添加文本字段为我修复了它。接受的答案和其他建议导致我的其他错误。对不起,我还没有让代表在@ Dex的答案上发表评论。

答案 3 :(得分:3)

这似乎与UIAlertController视图的样式有关,正如@BigShay所暗示的那样。

在我的示例中,我在添加UITextField之前设置了视图的tintColor,并且我在iOS 8中崩溃。@ Baza207在显示警报后添加UITextField的解决方法避免了iOS 8中的崩溃,但是它也导致了iOS 9中根本没有出现任何文本字段。

如果您只是在添加文本字段后将样式移动到,则它适用于iOS 8和iOS 9(iOS 8中没有崩溃,iOS 9中没有丢失的文本字段):< / p>

iOS 8崩溃

适用于iOS 9

alertController.view.tintColor = UIColor.blueColor()

alertController.addTextFieldWithConfigurationHandler { textField in
}

presentViewController(alertController, animated: true, completion: nil)

适用于iOS 8

iOS 9中没有文字文字字段

alertController.view.tintColor = UIColor.blueColor()

presentViewController(alertController, animated: true, completion: nil)

alertController.addTextFieldWithConfigurationHandler { textField in
}

适用于iOS 8

适用于iOS 9

alertController.addTextFieldWithConfigurationHandler { textField in
}

alertController.view.tintColor = UIColor.blueColor()

presentViewController(alertController, animated: true, completion: nil)

答案 4 :(得分:2)

iOS 8.3相关警报中似乎存在一个错误。它表现在(已弃用的)UIAlertView和仅支持iOS8的UIAlertController上。当我尝试将文本字段添加到这些控制器中的任何一个时,我得到以下崩溃:

 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'The layout constraints still need update after sending -updateConstraints to <_UIKeyboardLayoutAlignmentView: 0x792d28e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x792d2ab0>>.
_UIKeyboardLayoutAlignmentView or one of its superclasses may have overridden -updateConstraints without calling super. Or, something may have dirtied layout constraints in the middle of updating them.  Both are programming errors.'

没有文本字段的警报是正常的,但显示样式为 UIAlertViewStylePlainTextInput 的UIAlertView或显示带有通过 addTextFieldWithConfigurationHandler 添加的文本字段的UIAlertController将导致上述崩溃。

修复似乎是在调用show之前在UIAlertController上设置一个预防框架。在show之前覆盖此框架,但可以防止崩溃。

if (NSClassFromString(@"UIAlertController")) {
    // iOS8
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert"
                                                                   message:@"Be alert, not alarmed"
                                                            preferredStyle:UIAlertControllerStyleAlert];
    [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.keyboardType = UIKeyboardTypeEmailAddress;

    }];
    alert.view.frame = CGRectMake(0.0, 0.0, 320.0, 400.0); // Workaround iOS8.3 bug - set this to larger than you'll need

    [self presentViewController:alert animated:YES completion:^{
        [alert.textFields[0] becomeFirstResponder];
    }];
} else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert"
                                                    message:@"Be alert, not alarmed"
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField *emailField = [alert textFieldAtIndex:0];
    emailField.keyboardType = UIKeyboardTypeEmailAddress;

    [alert show];
}

答案 5 :(得分:1)

我刚刚在我的测试项目中运行了你的代码 - 一切都在8.3上工作正常。

  

View not found in container hierarchy:

如果您搞砸了UIViewController子/父关系,通常会出现此错误。有几点需要考虑:

  • 您用来呈现警报的控制器是最顶层的,并且没有呈现任何其他内容
  • 父控制器要么具有您作为孩子使用的那个(addChildViewController:),要么处于通过presentViewController:animated:completion:或其类似物呈现它的状态
  • 在呈现另一个ViewController
  • 之前加载了控制器的视图

此外,框架确实存在问题。

答案 6 :(得分:1)

我有一个类似问题,也在评论中提到。 如果你到了,请看这个。

  

***由于未捕获的异常'NSInternalInconsistencyException'终止应用程序,原因:'布局限制   发送-updateConstraints后仍需要更新   &lt; _UIKeyboardLayoutAlignmentView:0x792d28e0; frame =(0 0; 0 0);   userInteractionEnabled = NO; layer =&gt;。   _UIKeyboardLayoutAlignmentView或其中一个超类可能已经重写-updateConstraints而不调用super。或者,有些事可能   在更新它们的过程中弄脏了布局约束。都   编程错误。'

NSInteralInconsistencyException - UIKeyboardLayoutAlignmentView

答案 7 :(得分:0)

尝试将alert.view.frame显式设置为小的。我认为默认情况下它可能与应用程序视图一样大,在其子视图上触发它。

答案 8 :(得分:0)

我对新的iOS 8.3和Swift 1.2也有同样的问题。 UIViewController中的UIAlertController与嵌套的UITableView在开始主题中不起作用。

所以,我做了一个真正的模态形式。

  1. 创建新的UIViewController宽度,将视图背景设置为黑色,不透明度为0.4
  2. 使用两个按钮添加300x150 UIView,label和UITextField
  3. 添加约束和其他所需人员 You must see something like this
  4. 将模块化其他控制器与新控制器连接 enter image description here
  5. 并为新控制器指定特殊类作为&#34; PopupWindow&#34;。然后创建这个类:

    import UIKit
    
    class PopupWindow: UIViewController, UITextFieldDelegate {
    
        @IBOutlet var wrapper: UIView!
        @IBOutlet var btnCancel: UIButton!
        @IBOutlet var btnSave: UIButton!
        @IBOutlet var textField: UITextField!
        @IBOutlet var labelField: UILabel!
    
        var keyboardHeight: CGFloat = 0
        var data = [:]
        var closure: ((textField: UITextField!) -> Void)?
        var tmpText = ""
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.labelField.text = self.data["title"] as? String
            self.textField.text = self.data["text"] as? String
    
            self.btnCancel.setTitle("cancel", forState: UIControlState.Normal)
            var tmp = self.data["save"] != nil
                ? self.data["save"] as! String
                : "save"
            self.btnSave.setTitle(tmp, forState: UIControlState.Normal)
            self.btnSave.enabled = false
    
            self.wrapper.layer.cornerRadius = 5.0 as CGFloat
            self.textField.becomeFirstResponder()
        }
    
        @IBAction func onBtnCancel() {
            self.dismissViewControllerAnimated(true, completion: nil)
        }
    
        @IBAction func onBtnSave() {
            if self.closure != nil {
                self.dismissViewControllerAnimated(true, completion: {
                    _ in
                    self.closure!(textField: self.textField)
                })
            }
    
        }
    
        override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
            self.fixTopOffset()
        }
    
        override func viewWillAppear(animated: Bool) {
            super.viewWillAppear(animated)
    
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "textFieldDidChange:", name: UITextFieldTextDidChangeNotification, object: self.textField)
    
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "onKeyboadWillShow:", name: UIKeyboardWillShowNotification, object: nil)
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "onKeyboadWillShow:", name: UIKeyboardWillChangeFrameNotification, object: nil)
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "onKeyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
        }
    
        override func viewWillDisappear(animated: Bool) {
            super.viewWillDisappear(animated)
    
            NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: nil)
    
            NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
            NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillChangeFrameNotification, object: nil)
            NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
        }
    
        func onKeyboadWillShow(notification: NSNotification) {
            self.fixTopOffset()
            let info: NSDictionary = notification.userInfo!
            if let rectValue = info[UIKeyboardFrameBeginUserInfoKey] as? NSValue {
                let kbSize: CGRect = rectValue.CGRectValue()
                if self.keyboardHeight != kbSize.size.height {
                    self.keyboardHeight = kbSize.size.height
                    self.fixTopOffset()
                }
            }
        }
    
        func onKeyboardWillHide(notification: NSNotification) {
            self.fixTopOffset()
            if self.keyboardHeight != 0 {
                self.keyboardHeight = 0
                self.fixTopOffset()
            }
        }
    
        func fixTopOffset(landscape: Bool = true) {
            let height1: CGFloat = self.view.frame.height / 2
            let height2: CGFloat = height1 - self.keyboardHeight / 2
            let margin: CGFloat = height1 - height2
            for tmp in self.view.constraints() {
                if let constraint = tmp as? NSLayoutConstraint {
                    if constraint.firstAttribute == NSLayoutAttribute.CenterY {
                        if constraint.constant != margin {
                            constraint.constant = margin
                        }
                    }
                }
    
            }
        }
    
        func textFieldDidChange(notification: NSNotification) {
            if notification.object != nil {
                if let textField = notification.object as? UITextField {
                    self.btnSave.enabled = textField.text != self.tmpText
                }
            }
        }
    
    }
    
  6. 您需要通过Ctrl + Drag连接所有IBOutlet和相应的元素。

    1. 现在您可以在任何连接的控制器中使用它。例如:

      import UIKit
      
      class ExampleView: UIViewController {
      
          override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {       
              if segue.identifier == "showPopup" {
                  if let controller = segue.destinationViewController as? PopupWindow {
                      controller.data = sender as! NSDictionary
                      // This action will be called after form submission
                      // We just print text in console
                      controller.closure = {
                          (textField: UITextField!) in
                          println(textField)
                      }
                  }
              }
          }
      
          func showPopupWindow() {
              let data = [
                      "title": "My Window",
                      "save": "Save",
                      "text": "Do you realy want to save this text?",
              ]
              self.performSegueWithIdentifier("showPopup", sender: data)
          }
      }
      
    2. 现在我可以在控制器中添加一个segue并在prepareSegue方法中编写任何闭包 - 它可以工作!

      我在我的应用中使用弹出窗口制作了短视频 - http://youtu.be/JmqAAeUN-XU

答案 9 :(得分:0)

啊哈,我想这可能是jcesarmobile在另一个帖子中回答的: UIAlertController/UIAlertView scrolling on iOS 8

许多人为了处理UIAlertController中长文本的先前错误而添加的解决方法现在导致8.3中的崩溃。并且长文本问题在8.3中已修复,因此可以使解决方法成为有条件的,以避免在8.3

中出现这种情况

答案 10 :(得分:0)

立即修复它(对我而言)。我有完全相同的错误,克里斯描述:

NSInteralInconsistencyException - UIKeyboardLayoutAlignmentView

答案只是为了确保动画设置为&#34; NO&#34;在展示UIAlertController时。

[self presentViewController:alert animated:NO completion:nil];

答案 11 :(得分:0)

我真的不喜欢我看到的解决方案,想出了一个更简单的解决方案。当键盘可见并且出现带有输入字段的警报时,崩溃(至少对我而言)发生。

我只是在提示警报之前解除键盘,然后一切都很好。