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
編集:この修正は、Xcode6とXcode7のどちらでビルドしているかによって変わるため、両方のバージョンの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に組み込まれている場合、iOS8とiOS9で正しく実行されます。
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でXcode7を使用してテスト済みGM(7A218)
このバグは、-addTextFieldWithConfigurationHandler:
([alert isViewLoaded] == YES
)を呼び出したときにUIAlertControllerのビューがロードされた場合にトリガーされます。
-addTextFieldWithConfigurationHandlerを呼び出す前にalert.view
にアクセスしている場合は、alert.view
で行っていることを-addTextFieldWithConfigurationHandler:
の呼び出し後に移動します。
@Dexが述べたように、テキストフィールドを追加した後、コントローラーのビューを変更することでこれを修正しました。ビューの色合いの色を変更していました。
controller.view.tintColor = ...some color...
これを「後」に移動してテキストフィールドを追加すると、修正されました。受け入れられた答えと他の提案は私にとって他のバグにつながりました。申し訳ありませんが、@ Dexの回答にコメントを投稿する担当者がまだいません。
これは、@ BigShayによって回避されたように、UIAlertControllerのビューのスタイル設定と関係があるようです。
私の例では、UITextFieldを追加する前にビューのtintColorを設定し、iOS 8でクラッシュしました。アラートを表示した後にUITextFieldを追加する@ Baza207によって与えられた回避策は、iOS 8でのクラッシュを回避しますが、テキストフィールドも発生しません。 iOS9ではまったく表示されません。
スタイリングをafterテキストフィールドの追加に移動すると、iOS8とiOS9の両方で機能します(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)
IOS8.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.'
テキストフィールドのないアラートは問題ありませんが、スタイルがIAlertViewStylePlainTextInputのUIAlertViewを表示するか、addTextFieldWithConfigurationHandlerを介してテキストフィールドが追加されたUIAlertControllerを表示すると、上記のクラッシュが発生します。
修正は、showを呼び出す前にUIAlertControllerに予防フレームを設定することのようです。このフレームは表示前にオーバーライドされますが、クラッシュを防ぎます。
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:
などを介してそれを提示する状態にあります。また、フレームに間違いなく問題があります。
コメントにも記載されている同様の問題がありました。取得している場合はこちらをご覧ください。
***キャッチされなかった例外 'NSInternalInconsistencyException'が原因でアプリを終了しています、理由: '-updateConstraintsを<_UIKeyboardLayoutAlignmentView:0x792d28e0に送信した後も、レイアウト制約を更新する必要があります。フレーム=(0 0; 0 0); userInteractionEnabled = NO;レイヤー=>。 _UIKeyboardLayoutAlignmentViewまたはそのスーパークラスの1つが、superを呼び出さずに-updateConstraintsをオーバーライドした可能性があります。または、更新の途中でレイアウトの制約が汚れている可能性があります。どちらもプログラミングエラーです。」
NSInteralInconsistencyException --UIKeyboardLayoutAlignmentView
私は自分が見た解決策が本当に気に入らなかったので、もっと簡単な解決策を思いつきました。クラッシュは(少なくとも私にとっては)キーボードが表示され、入力フィールドを含むアラートが表示されたときに発生します。
アラートを表示する前にキーボードを閉じるだけで、すべて問題ありません。
Alert.view.frameを明示的に小さなものに設定してみてください。デフォルトでは、アプリケーションビューと同じくらいの大きさで、サブビューでこれをトリガーすると思います。
これですぐに修正されました(私にとって)。クリスが説明したのとまったく同じエラーが発生しました:
NSInteralInconsistencyException --UIKeyboardLayoutAlignmentView
答えは、UIAlertControllerを提示するときに、animatedが「NO」に設定されていることを確認することでした。
[self presentViewController:alert animated:NO completion:nil];
新しいiOS8.3とSwift 1.2で同じ問題があります。ネストされたUITableViewを持つUIViewControllerのUIAlertControllerは、開始トピックのように機能しません。
それで、私は本当のモーダルフォームを作りました。
そして、新しいコントローラーの特別なクラスを「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
}
}
}
}
Ctrl +ドラッグで、すべてのIBOutletを対応する要素に接続する必要があります。
これで、接続されている任意のコントローラーでウィンドウを使用できます。例えば:
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)
}
}
これで、コントローラーにセグエを追加し、prepareSegueメソッドでクロージャーを書き込むことができます-そしてそれは機能します!
アプリでポップアップウィンドウを使用して短いビデオを作成しました- http://youtu.be/JmqAAeUN-X
ああ、これは別のスレッドでjcesarmobileによって答えられるかもしれないと思います: iOS8でスクロールするUIAlertController/UIAlertView
UIAlertControllerで長いテキストを使用する以前のバグに対処するために追加された多くの回避策により、8.3でこのクラッシュが発生するようになりました。また、長いテキストの問題は8.3で修正されているため、8.3でこれを回避するために、回避策を条件付きで行うことができます。