UIPopoverController出现在iOS 8.1下的不可预知的延迟

时间:2014-11-29 14:27:16

标签: ios objective-c uitableview uipopovercontroller ios8.1

在iOS 8.1下运行时,SDK 8.1会出现此问题,而在iOS 7下运行时则不会出现此问题。仅适用于iPad。该问题同时出现在模拟器和硬件设备上。

下面的代码演示了一个视图控制器,它包含一行UITableView,其中包含一行UIButton。点击按钮或行将导致弹出窗口出现。点击按钮时这很好用,但是当点击表视图的行时,弹出窗口会有一些延迟。在我的测试中,我第一次点击行时弹出窗口通常会出现很少或没有延迟,但是第二次点击该行时,弹出窗口可能需要几秒钟才会出现,并且通常直到我点击其他地方才出现风景。但是,即使在行上的第一次敲击时也会发生延迟(特别是在硬件上进行测试时)。

我展示的代码尽可能地降低了,同时保留了问题。在我看来,UITableView在某种程度上是导致这种延迟发生的关键。

我知道在iOS 8中不推荐使用某些UIPopoverController代码。我试图在iOS 8中使用UIPopoverPresentationController,结果是相同的:在弹出窗口出现之前有时会有很长的延迟。我对这种新方法还不是很熟悉,所以我可能会犯错误,但无论如何都可以通过将USE_TRADITIONAL_METHOD宏设置为0而不是1来测试iOS 8代码。

有关如何修复或绕过此问题的任何建议(仍然使用tableview)将不胜感激。



#import "MyViewController.h"

@interface MyViewController ()

@property (nonatomic) UIPopoverController* myPopoverController;

@end

@implementation MyViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // setup table view
     UITableView* tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 500, 500) style:UITableViewStyleGrouped];
    [self.view addSubview:tableView];
    tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    tableView.dataSource = self;
    tableView.delegate = self;
    
    // setup button
    UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.view addSubview:button];
    button.frame = CGRectMake(20, 550, 100, 20);
    [button setTitle:@"Show popover" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(showPopover:) forControlEvents:UIControlEventTouchUpInside];
}


- (IBAction)showPopover:(id)sender
{
    UIView* senderView = (UIView*)sender;
    
    UIViewController* contentViewController = [[UIViewController alloc] init];
    contentViewController.view.backgroundColor = [UIColor purpleColor];
    
#define USE_TRADITIONAL_METHOD 1

#if USE_TRADITIONAL_METHOD
    self.myPopoverController = [[UIPopoverController alloc] initWithContentViewController:contentViewController];
    [self.myPopoverController presentPopoverFromRect:senderView.frame inView:senderView.superview permittedArrowDirections:UIPopoverArrowDirectionLeft animated:NO];
#else
    contentViewController.modalPresentationStyle = UIModalPresentationPopover;

    [self presentViewController:contentViewController animated:NO completion:^{
        NSLog(@"Present completion");       // As expected, this is executed once the popover is actually displayed (which can take a while)
    }];

    // do configuration *after* call to present, as explained in Apple documentation:
    UIPopoverPresentationController* popoverController = contentViewController.popoverPresentationController;
    popoverController.sourceRect = senderView.frame;
    popoverController.sourceView = senderView;
    popoverController.permittedArrowDirections = UIPopoverArrowDirectionLeft;
    
#endif
    NSLog(@"Popover is visible: %d", self.myPopoverController.isPopoverVisible);    // This shows "1" (visible) for USE_TRADITIONAL_METHOD, under both iOS 7 and iOS 8
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    cell.textLabel.text = @"Show popover";
    return cell;
}

#pragma mark - UITableViewDelegate

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self showPopover:[tableView cellForRowAtIndexPath:indexPath]];
}


@end




3 个答案:

答案 0 :(得分:39)

我发现在尝试显示弹出窗口之前取消选择行似乎可以解决问题。这可能是一个有用的解决方法,但我仍然在寻找更好的答案,因为这可能不是很强大。

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:NO]; // adding this line appears to fix the problem
    [self showPopover:[tableView cellForRowAtIndexPath:indexPath]];
}

请注意,正如Yasir Ali所说,要使此解决方法正常工作,取消选择动画必须关闭。在原帖发布近4年之后,这种行为仍然会影响到iOS 12,而且苹果公司的漏洞报告依旧开放 - 叹息。

答案 1 :(得分:2)

我遇到了同样的问题,在显示popover之前取消选择单元格并没有解决我的问题。对我有用的是将弹出代码调度到主队列,但延迟时间很短。这会产生popover的一致显示,并允许我保持单元格SELECTED,这是我的UI的关键:

Expected UI

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    switch actions[indexPath.row] {
    case .rename:
          .... 
    case .duplicate:

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
            let copyController = UIStoryboard.init(name: "Actions", bundle: nil).instantiateViewController(withIdentifier: "copyToNavController")

            copyController.modalPresentationStyle = .popover

            let cell = tableView.cellForRow(at: indexPath)
            copyController.popoverPresentationController?.sourceView = cell
            if let frame = cell?.frame {
                copyController.popoverPresentationController?.sourceRect = frame
            }
            copyController.popoverPresentationController?.permittedArrowDirections = .left

            self.popoverPresenter = copyController.popoverPresentationController
            self.present(copyController, animated: true, completion: nil)
        })
    default:
        break
    }
}

答案 2 :(得分:0)

非常感谢这个伎俩。 我有一个TableView,当我点击一行时会显示一个弹出窗口。我的popover只是在延迟后显示,或者甚至需要双击该行才能最终显示。通过添加指示的行([tableView deselectRowAtIndexPath:indexPath animated:NO]),弹出窗口立即显示,而不必再双击。