iOS 8.3 UIAlertController аварийно завершает работу при попытке добавить текстовое поле
у меня есть приложение для 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);
Я получаю забавную аварию, рассказывающую мне, что ограничения перепутались. Этот код отлично работает в
вот полная трассировка стека плюс странные предупреждения об ограничениях, которые появляется только после попытки добавить текстовое поле в 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 ответов
Edit: это исправление изменяется в зависимости от того, если вы строите с Xcode 6 или Xcode 7, поэтому я добавил соответствующую информацию для обеих версий Xcode.
я столкнулся с этим сегодня, и это говорит о том, что он не может добавить текстовое поле в представление контроллера вида или что он не может добавить ограничения автоматической компоновки в свой супервизор. Кажется, это потому, что он еще не создал superview, чтобы добавить его, и поэтому все это паникует и крахи.
простое исправление, которое я нашел, - это добавить текстовое поле после того, как вы скажете контроллеру оповещения представить. Это исправило это для меня, хотя я не уверен, что это повлияет на что-то вроде текстового поля, появляющегося при представлении предупреждения.
построенный с 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)
протестировано в Swift 2.0 и Obj-C с помощью Xcode 7 GM (7A218)
эта ошибка запускается, если представление UIAlertController загружается при вызове -addTextFieldWithConfigurationHandler:
([alert isViewLoaded] == YES
).
при обращении alert.view
перед вызовом -addTextFieldWithConfigurationHandler, переместите все, что вы делаете с alert.view
to после вызова -addTextFieldWithConfigurationHandler:
.
Как упоминалось @Dex, я исправил это, изменив представление моего контроллера после добавления текстового поля. Я изменял цвет вида.
controller.view.tintColor = ...some color...
перемещение этого в "после" добавления текстового поля исправило его для меня. Принятый ответ и другие предложения привели к другим ошибкам для меня. Извините, у меня еще нет представителя, чтобы опубликовать комментарий к ответу @Dex.
Это, похоже, имеет какое-то отношение к стилизации представления UIAlertController, как ускользнуло от @BigShay.
в моем примере я установил tintColor представления перед добавлением UITextField, и я сбой в iOS 8. Обходной путь, данный @Baza207 для добавления UITextField после отображения предупреждения, позволяет избежать сбоя в iOS 8, однако это также не приводит к появлению текстового поля вообще в iOS 9.
Если вы просто переместите стиль после добавить текст поле, оно работает как с iOS 8, так и с iOS 9 (нет сбоя в iOS 8 и нет отсутствующего текстового поля в iOS 9):
сбой в 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)
Кажется, что ошибка в предупреждениях, связанных с 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.'
оповещения без текстовых полей в порядке, но показывает UIAlertView со стилем UIAlertViewStylePlainTextInput или показ UIAlertController с текстовым полем, добавленным через addTextFieldWithConfigurationHandler будет в результате вышеуказанной аварии.
исправление, похоже, должно установить профилактический кадр на UIAlertController перед вызовом show. Этот кадр переопределяется перед 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];
}
Я только что запустил ваш код в моем тестовом проекте - все отлично работало на 8.3.
View not found in container hierarchy:
эта ошибка обычно появляется, если вы испортите дочерние / родительские отношения UIViewController. Есть несколько моментов, чтобы рассмотреть:
- контроллер, который вы используете для представления оповещения, является самым верхним и не представляет ничего другого уже
- родительские контроллеры либо имеют тот, который вы используете в качестве дочернего (
addChildViewController:
) или в состояние представления его через любойpresentViewController:animated:completion:
или в - представление контроллера загружается перед представлением другого ViewController
кроме того, есть определенно что-то не так с кадрами.
У меня была проблема similair, которая также упоминается в комментариях. Пожалуйста, посмотрите это, если вы получаете.
*** прекращение приложения из-за необработанных исключений 'NSInternalInconsistencyException', причина: 'макет ограничений все еще нужно обновить после отправки-updateConstraints в <_uikeyboardlayoutalignmentview:>. _UIKeyboardLayoutAlignmentView или один из его суперклассов может быть переопределен - updateConstraints без вызова супер. Или, что-то может загадили ограничения макета в середине их обновления. Оба ошибки программирования.'
NSInteralInconsistencyException-UIKeyboardLayoutAlignmentView
попробуйте установить оповещение.вид.кадр явно на что-то маленькое. Я думаю, что по умолчанию он может быть таким же большим, как представление приложения, вызывая это в своих подвидах.
у меня такая же проблема с новой iOS 8.3 и Swift 1.2. UIAlertController в UIViewController с вложенным UITableView не работает, как в разделе запуска.
Итак, я сделал настоящую модальную форму.
- создать новую ширину UIViewController, установить вид фон черный с 0.4 непрозрачность
- добавить 300x150 UIView с двумя кнопками, label и UITextField
- добавить ограничения и другой необходимый персонал
- подключение модально другие контроллеры с новым
-
и укажите специальный класс для нового контроллера как "PopupWindow". Затем создайте этот класс:
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 } } } }
вам нужно соединить все IBOutlets с соответствующими элементами в представлении Ctrl + Drag.
-
и теперь вы можете использовать это окно в любом подключенном контроллере. Например:
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) } }
теперь я могу просто добавить segue к контроллеру и написать любой закрытие в методе prepareSegue - и это работает!
Я сделал короткое видео с всплывающим окном в моем приложении -http://youtu.be/JmqAAeUN-XU
Ага, я думаю, на это может ответить jcesarmobile в другом потоке: прокрутка UIAlertController/UIAlertView в iOS 8
обходной путь много людей, добавленных для борьбы с предыдущими ошибками с длинным текстом в UIAlertController, теперь вызывает этот сбой в 8.3. И проблема с длинным текстом исправлена в 8.3, поэтому обходной путь можно сделать условным, чтобы избежать этого в 8.3
это исправило его немедленно (для меня). У меня была та же самая ошибка, которую описал Крис:
NSInteralInconsistencyException-UIKeyboardLayoutAlignmentView
ответ был просто убедиться, что анимированный установлен в " НЕТ " при представлении UIAlertController.
[self presentViewController:alert animated:NO completion:nil];
Мне действительно не понравились решения, которые я видел, и придумал более простой. Сбой происходит (для меня, по крайней мере), когда клавиатура видна и представлено предупреждение с полем ввода.
Я просто отклоняю клавиатуру до представления предупреждения, а затем все в порядке.