ios presentViewController:animated:YES 视图不会出现,直到用户再次点击
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21075540/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
presentViewController:animated:YES view will not appear until user taps again
提问by HalfNormalled
I'm getting some strange behaviour with presentViewController:animated:completion
. What I'm making is essentially a guessing game.
我遇到了一些奇怪的行为presentViewController:animated:completion
。我所做的本质上是一个猜谜游戏。
I have a UIViewController
(frequencyViewController) containing a UITableView
(frequencyTableView). When the user taps on the row in questionTableView containing the correct answer, a view (correctViewController) should be instantiate and its view should slide up from the bottom of the screen, as a modal view. This tells the user they have a correct answer and resets the frequencyViewController behind it ready for the next question. correctViewController is dismissed on a button press to reveal the next question.
我有一个UIViewController
(frequencyViewController) 包含一个UITableView
(frequencyTableView)。当用户点击 questionTableView 中包含正确答案的行时,应该实例化一个视图 (correctViewController) 并且它的视图应该从屏幕底部向上滑动,作为一个模态视图。这告诉用户他们有一个正确的答案并重置它后面的 frequencyViewController 为下一个问题做好准备。rightViewController 在按下按钮时被解除以显示下一个问题。
This all works correctly every time, and the correctViewController's view appear instantly as long as presentViewController:animated:completion
has animated:NO
.
这一切每次都正常工作,并且只要presentViewController:animated:completion
有animated:NO
.
If I set animated:YES
, correctViewController is initialized and makes calls to viewDidLoad
. However viewWillAppear
, viewDidAppear
, and the completion block from presentViewController:animated:completion
are not called. The app just sits there still showing frequencyViewController until I make a second tap. Now, viewWillAppear, viewDidAppear and the completion block are called.
如果我设置了animated:YES
,则正确的视图控制器被初始化并调用viewDidLoad
. 但是viewWillAppear
,viewDidAppear
, 和 from 的完成块presentViewController:animated:completion
不会被调用。该应用程序只是坐在那里仍然显示 frequencyViewController 直到我再次点击。现在,viewWillAppear、viewDidAppear 和完成块被调用。
I investigated a bit more, and it's not just another tap that will cause it to continue. It seems if I tilt or shake my iPhone this can also cause it to trigger the viewWillLoad etc. It's like it's waiting to any other bit of user input before it will progress. This happens on a real iPhone and in the simulator, which I proved by sending the shake command to the simulator.
我进行了更多调查,这不仅仅是另一个水龙头会导致它继续。似乎如果我倾斜或摇晃我的 iPhone,这也会导致它触发 viewWillLoad 等。这就像它在继续之前等待任何其他用户输入一样。这发生在真正的 iPhone 和模拟器中,我通过向模拟器发送抖动命令来证明这一点。
I'm really at a loss as to what to do about this... I'd really appreciate any help anyone can provide.
我真的不知道该怎么做……我真的很感激任何人都可以提供的任何帮助。
Thanks
谢谢
Here's my code. It's pretty simple...
这是我的代码。这很简单...
This is code in questionViewController that acts as the delegate to the questionTableView
这是 questionViewController 中的代码,它充当 questionTableView 的委托
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row != [self.frequencyModel currentFrequencyIndex])
{
// If guess was wrong, then mark the selection as incorrect
NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]];
}
else
{
// If guess was correct, show correct view
NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
self.correctViewController = [[HFBCorrectViewController alloc] init];
self.correctViewController.delegate = self;
[self presentViewController:self.correctViewController animated:YES completion:^(void){
NSLog(@"Completed Presenting correctViewController");
[self setUpViewForNextQuestion];
}];
}
}
This is the whole of the correctViewController
这是整个正确的ViewController
@implementation HFBCorrectViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
NSLog(@"[HFBCorrectViewController initWithNibName:bundle:]");
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSLog(@"[HFBCorrectViewController viewDidLoad]");
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSLog(@"[HFBCorrectViewController viewDidAppear]");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)close:(id)sender
{
NSLog(@"[HFBCorrectViewController close:sender:]");
[self.delegate didDismissCorrectViewController];
}
@end
Edit:
编辑:
I found this question earlier: UITableView and presentViewController takes 2 clicks to display
我之前发现了这个问题:UITableView 和 presentViewController 需要 2 次点击才能显示
And if I change my didSelectRow
code to this, it works very time with animation... But it's messy and doesn't make sense as to why it doesn't work in the first place. So I don't count that as an answer...
如果我把我的didSelectRow
代码改成这样,它就可以很长时间地与动画一起工作……但是它很混乱,而且为什么它首先不起作用是没有意义的。所以我不认为这是一个答案......
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row != [self.frequencyModel currentFrequencyIndex])
{
// If guess was wrong, then mark the selection as incorrect
NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]];
// [cell setAccessoryType:(UITableViewCellAccessoryType)]
}
else
{
// If guess was correct, show correct view
NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
////////////////////////////
// BELOW HERE ARE THE CHANGES
[self performSelector:@selector(showCorrectViewController:) withObject:nil afterDelay:0];
}
}
-(void)showCorrectViewController:(id)sender
{
self.correctViewController = [[HFBCorrectViewController alloc] init];
self.correctViewController.delegate = self;
self.correctViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:self.correctViewController animated:YES completion:^(void){
NSLog(@"Completed Presenting correctViewController");
[self setUpViewForNextQuestion];
}];
}
回答by Tamás Zahola
I've encountered the same issue today. I dug into the topic and it seems that it's related to the main runloop being asleep.
我今天遇到了同样的问题。我深入研究了这个话题,它似乎与主 runloop 处于睡眠状态有关。
Actually it's a very subtle bug, because if you have the slightest feedback animation, timers, etc. in your code this issue won't surface because the runloop will be kept alive by these sources. I've found the issue by using a UITableViewCell
which had its selectionStyle
set to UITableViewCellSelectionStyleNone
, so that no selection animation triggered the runloop after the row selection handler ran.
实际上,这是一个非常微妙的错误,因为如果您的代码中有最轻微的反馈动画、计时器等,则不会出现此问题,因为这些源将保持 runloop 处于活动状态。我已经通过使用发现问题UITableViewCell
有它selectionStyle
设置为UITableViewCellSelectionStyleNone
,所以没有选择动画触发行选择处理程序追着runloop。
To fix it (until Apple does something) you can trigger the main runloop by several means:
要修复它(直到 Apple 做某事),您可以通过多种方式触发主 runloop:
The least intrusive solution is to call CFRunLoopWakeUp
:
干扰最少的解决方案是调用CFRunLoopWakeUp
:
[self presentViewController:vc animated:YES completion:nil];
CFRunLoopWakeUp(CFRunLoopGetCurrent());
Or you can enqueue an empty block to the main queue:
或者您可以将一个空块加入主队列:
[self presentViewController:vc animated:YES completion:nil];
dispatch_async(dispatch_get_main_queue(), ^{});
It's funny, but if you shake the device, it'll also trigger the main loop (it has to process the motion events). Same thing with taps, but that's included in the original question :) Also, if the system updates the status bar (e.g. the clock updates, the WiFi signal strength changes etc.) that'll also wake up the main loop and present the view controller.
这很有趣,但如果你摇动设备,它也会触发主循环(它必须处理运动事件)。与水龙头相同的事情,但这包含在原始问题中:) 此外,如果系统更新状态栏(例如时钟更新、WiFi 信号强度变化等),它也会唤醒主循环并呈现视图控制器。
For anyone interested I wrote a minimal demonstration project of the issue to verify the runloop hypothesis: https://github.com/tzahola/present-bug
对于任何感兴趣的人,我写了一个该问题的最小演示项目来验证 runloop 假设:https: //github.com/tzahola/present-bug
I've also reported the bug to Apple.
我也向苹果报告了这个错误。
回答by AXE
Check this out: https://devforums.apple.com/thread/201431If you don't want to read it all - the solution for some people (including me) was to make the presentViewController
call explicitly on the main thread:
看看这个:https: //devforums.apple.com/thread/201431如果您不想阅读所有内容 - 某些人(包括我)的解决方案是presentViewController
在主线程上明确调用:
Swift 4.2:
斯威夫特 4.2:
DispatchQueue.main.async {
self.present(myVC, animated: true, completion: nil)
}
Objective-C:
目标-C:
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:myVC animated:YES completion:nil];
});
Probably iOS7 is messing up the threads in didSelectRowAtIndexPath
.
可能 iOS7 搞乱了didSelectRowAtIndexPath
.
回答by Mark
I bypassed it in Swift 3.0 by using the following code:
我在 Swift 3.0 中使用以下代码绕过了它:
DispatchQueue.main.async {
self.present(UIViewController(), animated: true, completion: nil)
}
回答by JaganY
Calling [viewController view]
on the view controller being presented did the trick for me.
调用[viewController view]
正在呈现的视图控制器对我来说很有用。
回答by Nick
I'd be curious to see what [self setUpViewForNextQuestion];
does.
我很想知道有什么[self setUpViewForNextQuestion];
作用。
You could try calling [self.correctViewController.view setNeedsDisplay];
at the end of your completion block in presentViewController
.
您可以尝试[self.correctViewController.view setNeedsDisplay];
在presentViewController
.
回答by Sachintha Udara
XCode Vesion : 9.4.1, Swift 4.1
XCode 版本:9.4.1,Swift 4.1
In my case this happen, when I tap cell and move for the another view. I debug into the deeper and it seems that happen inside viewDidAppear
because of contains following code
在我的情况下,当我点击单元格并移动到另一个视图时会发生这种情况。我调试到更深的地方,似乎viewDidAppear
因为包含以下代码而发生在内部
if let indexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
then I added above code segment inside prepare(for segue: UIStoryboardSegue, sender: Any?)
and work perfect.
然后我在里面添加了上面的代码段prepare(for segue: UIStoryboardSegue, sender: Any?)
并且工作完美。
Within my experience, my solution is, If we'll hope to do any new changes(eg. table reload, deselect selected cell etc.) for the tableview when again come back from second view, then use delegate instead of viewDidAppear
and using above tableView.deselectRow
code segment before moving second view controller
根据我的经验,我的解决方案是,如果我们希望在从第二个视图再次返回时对 tableview 进行任何新的更改(例如重新加载表格,取消选择选定的单元格等),然后使用委托而不是viewDidAppear
使用上面的tableView.deselectRow
代码移动第二个视图控制器之前的段
回答by Gladkov_Art
I've wrote extension (category) with method swizzling for UIViewController that solves the issue. Thanks to AXE and NSHipster for implementation hints (swift/objective-c).
我已经为 UIViewController 编写了带有方法 swizzling 的扩展(类别)来解决这个问题。感谢 AX 和 NSHipster 的实现提示(swift/ objective-c)。
Swift
迅速
extension UIViewController {
override public class func initialize() {
struct DispatchToken {
static var token: dispatch_once_t = 0
}
if self != UIViewController.self {
return
}
dispatch_once(&DispatchToken.token) {
let originalSelector = Selector("presentViewController:animated:completion:")
let swizzledSelector = Selector("wrappedPresentViewController:animated:completion:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
func wrappedPresentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
dispatch_async(dispatch_get_main_queue()) {
self.wrappedPresentViewController(viewControllerToPresent, animated: flag, completion: completion)
}
}
}
Objective-C
目标-C
#import <objc/runtime.h>
@implementation UIViewController (Swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(presentViewController:animated:completion:);
SEL swizzledSelector = @selector(wrappedPresentViewController:animated:completion:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)wrappedPresentViewController:(UIViewController *)viewControllerToPresent
animated:(BOOL)flag
completion:(void (^ __nullable)(void))completion {
dispatch_async(dispatch_get_main_queue(),^{
[self wrappedPresentViewController:viewControllerToPresent
animated:flag
completion:completion];
});
}
@end
回答by Mkey
Check if your cell in the storyboard has Selection = none
检查故事板中的单元格是否具有 Selection = none
If so, change it to blue or grey and it should work
如果是这样,请将其更改为蓝色或灰色,它应该可以工作