Непредсказуемая задержка до появления UIPopoverController под iOS 8.1

Эта проблема возникает с SDK 8.1 при работе под iOS 8.1, но не при работе под iOS 7. Это только для iPad. Проблема возникает как с симулятором, так и на аппаратном устройстве.

В приведенном ниже коде показан контроллер представления, содержащий UITableView с 1 ​​строкой, а ниже - UIButton. При нажатии на кнопку или в строке появится всплывающее окно. Это очень хорошо работает при нажатии кнопки, но при нажатии на строку таблицы просмотра появляется сообщение с некоторой задержкой. В моем тестировании, в первый раз, когда я нажимаю строку, popover обычно появляется с небольшой задержкой или без нее, но во второй раз я нажимаю на строку, может потребоваться много секунд, прежде чем появится popover, и часто она не появляется, пока я не коснусь вид. Однако задержка может произойти даже при первом нажатии на строку (особенно при тестировании на аппаратном обеспечении).

Код, который я показываю, был максимально сжат, сохраняя при этом проблему. Мне кажется, что UITableView почему-то критично, чтобы вызвать эту задержку.

Я знаю, что некоторые из кода UIPopoverController устарели под iOS 8. Я попытался использовать UIPopoverPresentationController для iOS 8, и результат тот же: иногда очень длинная задержка до появления popover. Я еще не очень хорошо знаком с этим новым подходом, поэтому я могу ошибаться, но в любом случае код iOS 8 можно протестировать, установив макрос USE_TRADITIONAL_METHOD равным 0 вместо 1.

Приветствуются любые предложения о том, как исправить или обойти это (при использовании таблицы).

#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

Ответы

Ответ 1

Я обнаружил, что отмена выбора строки, прежде чем пытаться показать popover, кажется, устраняет проблему. Это может быть полезно для работы, но я все еще ищу лучший ответ, так как это может быть ненадежным.

-(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]];
}

Ответ 2

У меня была такая же проблема, и отмена выбора ячейки перед отображением popover не помогла решить мою проблему. То, что работало для меня, было отправкой кода popover в основную очередь с незначительной задержкой. Это приводит к постоянному отображению popover И позволяет мне сохранить ячейку SELECTED, которая является ключом к моему пользовательскому интерфейсу:

Ожидаемый пользовательский интерфейс

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
    }
}

Ответ 3

Большое спасибо за этот трюк. У меня есть TableView, который отображает popover, когда я нажимаю на строку. Мой popover показывался только после задержки или даже требовал двойного щелчка по строке, чтобы наконец показать. Добавив указанную строку ([tableView deselectRowAtIndexPath: indexPath animated: NO]), всплывающее окно отображается сразу же без двойного щелчка.