如何防止以模态方式出现活动控制器?
How to prevent present modally an active controller?
我们有 2 个控制器:MainVC 和 ProfileVC。
从 MainVC 我们转到 ProfileVC profileButton
按(导航栏上的左侧项目)。
在个人资料 VC 中,我们在导航栏上有 2 个按钮:返回主界面 (leftItem) 和打开 alertView (rightItem)。
所以有简单的 KIF 测试(只需在导航栏上不断地左右点击):
- (void)testProfileButtons
{
[tester waitForAnimationsToFinishWithTimeout:0.3];
while (true)
{
[tester tapScreenAtPoint:CGPointMake(20, 20)];
[tester waitForTimeInterval:0.1];
[tester tapScreenAtPoint:CGPointMake(380, 20)];
[tester waitForTimeInterval:0.1];
[tester tapScreenAtPoint:CGPointMake(20, 20)];
[tester waitForTimeInterval:0.1];
}
}
它使应用程序崩溃并出现错误:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <UINavigationController: 0x7fe862ef82d0>.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000110944c65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001105dabb7 objc_exception_throw + 45
2 UIKit 0x000000010f44b80d -[UIViewController _presentViewController:withAnimationController:completion:] + 3238
3 UIKit 0x000000010f44d6c1 __62-[UIViewController presentViewController:animated:completion:]_block_invoke + 132
4 UIKit 0x000000010fa5e5ae -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 217
5 UIKit 0x000000010fa5b8e5 -[_UIViewControllerTransitionContext _runAlongsideCompletions] + 123
6 UIKit 0x000000010fa5b670 -[_UIViewControllerTransitionContext completeTransition:] + 126
7 UIKit 0x000000010f2fe8a6 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke93 + 687
8 UIKit 0x000000010f387193 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 326
9 UIKit 0x000000010f36e0f6 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 209
10 UIKit 0x000000010f36e42c -[UIViewAnimationState animationDidStop:finished:] + 76
11 UIKit 0x000000011daf3fdf -[UIViewAnimationStateAccessibility animationDidStop:finished:] + 48
12 QuartzCore 0x000000010f03e892 _ZN2CA5Layer23run_animation_callbacksEPv + 308
13 libdispatch.dylib 0x0000000111c67964 _dispatch_client_callout + 8
14 libdispatch.dylib 0x0000000111c52a59 _dispatch_main_queue_callback_4CF + 704
15 CoreFoundation 0x00000001108ac1f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
16 CoreFoundation 0x000000011086ddcb __CFRunLoopRun + 2043
17 CoreFoundation 0x000000011086d366 CFRunLoopRunSpecific + 470
18 TurboChat UI Tests 0x000000011b73ef69 -[KIFTestActor tryRunningBlock:complete:timeout:error:] + 425
19 TurboChat UI Tests 0x000000011b73f179 -[KIFTestActor runBlock:complete:timeout:] + 137
20 TurboChat UI Tests 0x000000011b73f304 -[KIFTestActor runBlock:timeout:] + 84
21 TurboChat UI Tests 0x000000011b73f73e -[KIFTestActor waitForTimeInterval:] + 174
22 TurboChat UI Tests 0x000000011b73c2ad -[TCProfileButtonsTest testProfileButtons] + 909
23 CoreFoundation 0x000000011083adec __invoking___ + 140
24 CoreFoundation 0x000000011083ac42 -[NSInvocation invoke] + 290
25 XCTest 0x000000011b79217a -[XCTestCase invokeTest] + 253
26 XCTest 0x000000011b792379 -[XCTestCase performTest:] + 150
27 XCTest 0x000000011b79bc35 -[XCTest run] + 260
28 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
29 XCTest 0x000000011b79bc35 -[XCTest run] + 260
30 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
31 XCTest 0x000000011b79bc35 -[XCTest run] + 260
32 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
33 XCTest 0x000000011b79bc35 -[XCTest run] + 260
34 XCTest 0x000000011b78e129 __25-[XCTestDriver _runSuite]_block_invoke + 56
35 XCTest 0x000000011b798edd -[XCTestObservationCenter _observeTestExecutionForBlock:] + 162
36 XCTest 0x000000011b78e060 -[XCTestDriver _runSuite] + 269
37 XCTest 0x000000011b78ea8d -[XCTestDriver _checkForTestManager] + 234
38 XCTest 0x000000011b79eb20 +[XCTestProbe runTests:] + 182
39 Foundation 0x000000010e5f21e5 __NSFireDelayedPerform + 387
40 CoreFoundation 0x00000001108ac174 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
41 CoreFoundation 0x00000001108abd35 __CFRunLoopDoTimer + 1045
42 CoreFoundation 0x000000011086dd3d __CFRunLoopRun + 1901
43 CoreFoundation 0x000000011086d366 CFRunLoopRunSpecific + 470
44 GraphicsServices 0x00000001123a8a3e GSEventRunModal + 161
45 UIKit 0x000000010f30e8c0 UIApplicationMain + 1282
46 TurboChat 0x000000010cd1e75f main + 111
47 libdyld.dylib 0x0000000111c97145 start + 1
)
我在真实设备上测试时也得到了这个:
Presenting view controllers on detached view controllers is discouraged <TCProfileTableViewController: 0x14f6299c0>.
并让 alertView 出现在 MainVC 上,而不是 ProfileVC 上,下面是黑屏。
这就是我们在配置文件中显示警报的方式VC :
- (IBAction)menuButtonPressed:(id)sender
{
[self.navigationController presentViewController:self.menuAlert animated:YES completion:nil];
}
警报视图:
self.menuAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@?", NSLocalizedString(@"PROFILE_BTN_MENU", nil)]
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
有什么建议吗?
我认为你需要先关闭最后一个 viewcontroller,然后再返回当前模式转场,viewcontroller 处于活动状态,因此它会崩溃:
在从 mainVC 调用 segue 或你想返回的东西后使用这个:
[self dismissViewControllerAnimated:NO completion:nil]
或
[[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]
或者使用 push segue,它会自动添加一个自动添加返回功能的返回按钮
我找到了解决方法
- (IBAction)menuButtonPressed:(id)sender
{
[self.parentViewController presentViewController:self.menuAlert animated:YES completion:nil];
}
警报视图有时会出现在 menuVC 上,但它不会使应用程序崩溃并正常工作。
在那里找到了答案:Warning :-Presenting view controllers on detached view controllers is discouraged
可能还有一种情况:
目标 ViewController 是在用户点击按钮后显示的吗?
如果您有上述 ViewController.
的共享实例,则用户可能会点击两次,进行两次操作并打开相同的 ViewController 两次
尝试在操作进行时禁用按钮,显示进度指示器等。
为了防止这个异常。只需关闭当前显示的 ViewController 即可返回到您正在关闭的 ViewController 的调用方:
self.dismiss(animated: true, completion: nil)
您现在要做的就是为问题添加一个计时器。
@IBAction func onBtnMenu(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { [weak sender] timer in
sender?.isUserInteractionEnabled = true
})
}
我们有 2 个控制器:MainVC 和 ProfileVC。
从 MainVC 我们转到 ProfileVC profileButton
按(导航栏上的左侧项目)。
在个人资料 VC 中,我们在导航栏上有 2 个按钮:返回主界面 (leftItem) 和打开 alertView (rightItem)。
所以有简单的 KIF 测试(只需在导航栏上不断地左右点击):
- (void)testProfileButtons
{
[tester waitForAnimationsToFinishWithTimeout:0.3];
while (true)
{
[tester tapScreenAtPoint:CGPointMake(20, 20)];
[tester waitForTimeInterval:0.1];
[tester tapScreenAtPoint:CGPointMake(380, 20)];
[tester waitForTimeInterval:0.1];
[tester tapScreenAtPoint:CGPointMake(20, 20)];
[tester waitForTimeInterval:0.1];
}
}
它使应用程序崩溃并出现错误:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <UINavigationController: 0x7fe862ef82d0>.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000110944c65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001105dabb7 objc_exception_throw + 45
2 UIKit 0x000000010f44b80d -[UIViewController _presentViewController:withAnimationController:completion:] + 3238
3 UIKit 0x000000010f44d6c1 __62-[UIViewController presentViewController:animated:completion:]_block_invoke + 132
4 UIKit 0x000000010fa5e5ae -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 217
5 UIKit 0x000000010fa5b8e5 -[_UIViewControllerTransitionContext _runAlongsideCompletions] + 123
6 UIKit 0x000000010fa5b670 -[_UIViewControllerTransitionContext completeTransition:] + 126
7 UIKit 0x000000010f2fe8a6 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke93 + 687
8 UIKit 0x000000010f387193 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 326
9 UIKit 0x000000010f36e0f6 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 209
10 UIKit 0x000000010f36e42c -[UIViewAnimationState animationDidStop:finished:] + 76
11 UIKit 0x000000011daf3fdf -[UIViewAnimationStateAccessibility animationDidStop:finished:] + 48
12 QuartzCore 0x000000010f03e892 _ZN2CA5Layer23run_animation_callbacksEPv + 308
13 libdispatch.dylib 0x0000000111c67964 _dispatch_client_callout + 8
14 libdispatch.dylib 0x0000000111c52a59 _dispatch_main_queue_callback_4CF + 704
15 CoreFoundation 0x00000001108ac1f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
16 CoreFoundation 0x000000011086ddcb __CFRunLoopRun + 2043
17 CoreFoundation 0x000000011086d366 CFRunLoopRunSpecific + 470
18 TurboChat UI Tests 0x000000011b73ef69 -[KIFTestActor tryRunningBlock:complete:timeout:error:] + 425
19 TurboChat UI Tests 0x000000011b73f179 -[KIFTestActor runBlock:complete:timeout:] + 137
20 TurboChat UI Tests 0x000000011b73f304 -[KIFTestActor runBlock:timeout:] + 84
21 TurboChat UI Tests 0x000000011b73f73e -[KIFTestActor waitForTimeInterval:] + 174
22 TurboChat UI Tests 0x000000011b73c2ad -[TCProfileButtonsTest testProfileButtons] + 909
23 CoreFoundation 0x000000011083adec __invoking___ + 140
24 CoreFoundation 0x000000011083ac42 -[NSInvocation invoke] + 290
25 XCTest 0x000000011b79217a -[XCTestCase invokeTest] + 253
26 XCTest 0x000000011b792379 -[XCTestCase performTest:] + 150
27 XCTest 0x000000011b79bc35 -[XCTest run] + 260
28 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
29 XCTest 0x000000011b79bc35 -[XCTest run] + 260
30 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
31 XCTest 0x000000011b79bc35 -[XCTest run] + 260
32 XCTest 0x000000011b79108b -[XCTestSuite performTest:] + 379
33 XCTest 0x000000011b79bc35 -[XCTest run] + 260
34 XCTest 0x000000011b78e129 __25-[XCTestDriver _runSuite]_block_invoke + 56
35 XCTest 0x000000011b798edd -[XCTestObservationCenter _observeTestExecutionForBlock:] + 162
36 XCTest 0x000000011b78e060 -[XCTestDriver _runSuite] + 269
37 XCTest 0x000000011b78ea8d -[XCTestDriver _checkForTestManager] + 234
38 XCTest 0x000000011b79eb20 +[XCTestProbe runTests:] + 182
39 Foundation 0x000000010e5f21e5 __NSFireDelayedPerform + 387
40 CoreFoundation 0x00000001108ac174 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
41 CoreFoundation 0x00000001108abd35 __CFRunLoopDoTimer + 1045
42 CoreFoundation 0x000000011086dd3d __CFRunLoopRun + 1901
43 CoreFoundation 0x000000011086d366 CFRunLoopRunSpecific + 470
44 GraphicsServices 0x00000001123a8a3e GSEventRunModal + 161
45 UIKit 0x000000010f30e8c0 UIApplicationMain + 1282
46 TurboChat 0x000000010cd1e75f main + 111
47 libdyld.dylib 0x0000000111c97145 start + 1
)
我在真实设备上测试时也得到了这个:
Presenting view controllers on detached view controllers is discouraged <TCProfileTableViewController: 0x14f6299c0>.
并让 alertView 出现在 MainVC 上,而不是 ProfileVC 上,下面是黑屏。
这就是我们在配置文件中显示警报的方式VC :
- (IBAction)menuButtonPressed:(id)sender
{
[self.navigationController presentViewController:self.menuAlert animated:YES completion:nil];
}
警报视图:
self.menuAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@?", NSLocalizedString(@"PROFILE_BTN_MENU", nil)]
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
有什么建议吗?
我认为你需要先关闭最后一个 viewcontroller,然后再返回当前模式转场,viewcontroller 处于活动状态,因此它会崩溃:
在从 mainVC 调用 segue 或你想返回的东西后使用这个:
[self dismissViewControllerAnimated:NO completion:nil]
或
[[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]
或者使用 push segue,它会自动添加一个自动添加返回功能的返回按钮
我找到了解决方法
- (IBAction)menuButtonPressed:(id)sender
{
[self.parentViewController presentViewController:self.menuAlert animated:YES completion:nil];
}
警报视图有时会出现在 menuVC 上,但它不会使应用程序崩溃并正常工作。 在那里找到了答案:Warning :-Presenting view controllers on detached view controllers is discouraged
可能还有一种情况:
目标 ViewController 是在用户点击按钮后显示的吗? 如果您有上述 ViewController.
的共享实例,则用户可能会点击两次,进行两次操作并打开相同的 ViewController 两次尝试在操作进行时禁用按钮,显示进度指示器等。
为了防止这个异常。只需关闭当前显示的 ViewController 即可返回到您正在关闭的 ViewController 的调用方:
self.dismiss(animated: true, completion: nil)
您现在要做的就是为问题添加一个计时器。
@IBAction func onBtnMenu(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { [weak sender] timer in
sender?.isUserInteractionEnabled = true
})
}