iOS 8 — UIPopoverPresentationController перемещает всплывающее окно

#popover #ios8

#всплывающее окно #ios8

Вопрос:

Я ищу эффективный способ изменить положение всплывающего окна, используя новый uipopoverpresentationcontroller. Я успешно представил всплывающее окно, и теперь я хочу переместить его, не отклоняя и не представляя снова. У меня возникли проблемы с использованием функции:

 (void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController
      willRepositionPopoverToRect:(inout CGRect *)rect
                           inView:(inout UIView **)view
 

Я знаю, что это только начало игры, но у кого-нибудь есть пример того, как сделать это эффективно, я был бы признателен, если бы вы поделились им со мной. Заранее спасибо.

Комментарии:

1. У меня та же проблема. Мне также трудно изменить размер всплывающего окна (похоже, он игнорирует popoverLayoutMargins и popoverContentSize). Я получаю нераспознанный селектор при реализации (void) popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController willRepositionPopoverToRect:(inout CGRect )rect inView: (inout UIView *)view Я установил делегат в соответствии с документами, по-прежнему безуспешно.

2. Я понял это: установите размер в viewWillAppear

3. Хороший звонок. моя калибровка все еще работает над этим методом делегирования…

4. @mehinger Вы когда-нибудь выясняли, как перемещать uipopoverpresentationcontroller?

5. @Amendale я никогда этого не делал. Сдался и вместо этого использовал пользовательский UIView

Ответ №1:

К сожалению, это хакерское решение — единственное решение, которое я нашел:

 [vc.popoverPresentationController setSourceRect:newSourceRect];
[vc setPreferredContentSize:CGRectInset(vc.view.frame, -0.01, 0.0).size];
 

Это временно изменяет размер содержимого представленного представления, в результате чего всплывающее окно и стрелка будут перемещены. Временное изменение размера не видно.

Похоже, это проблема, которую Apple необходимо исправить — изменение свойств sourceView or sourceRect UIPopoverPresentationController ничего не делает, когда оно уже представляет всплывающее окно (без этого обходного пути).

Надеюсь, это сработает и для вас!

Комментарии:

1. Здесь та же проблема, НО в iOS7 нет probelm!

2. Похоже, что это все еще (2016, iOS 9) единственный способ заставить это работать. Вы можете preferredContentSize вернуть его к исходному значению, но сначала вам нужно его изменить, а это невозможно CGSizeZero .

3. Все еще требуется (2017, цель развертывания iOS 9, SDKROOT iOS 11.2, Xcode 9.2).

4. и что еще хуже, в iOS12 они сделали preferredContentSize неизменяемым, поэтому вы даже не сможете использовать этот хак, если захотите.

Ответ №2:

Мне повезло с использованием containerView?.setNeedsLayout() и containerView?.layoutIfNeeded() после изменения sourceRect popoverPresentationController , вот так:

 func movePopoverTo(_ newRect: CGRect) {
    let popover = self.presentedViewController as? MyPopoverViewController {
        popover.popoverPresentationController?.sourceRect = newRect
        popover.popoverPresentationController?.containerView?.setNeedsLayout()
        popover.popoverPresentationController?.containerView?.layoutIfNeeded()
    }
}
 

И даже для того, чтобы всплывающее окно следовало за ячейкой TableView без необходимости что-либо менять:

 class MyTableViewController: UITableViewController {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "MyPopoverSegue" {
            guard let controller = segue.destination as? MyPopoverViewController else { fatalError("Expected destination controller to be a 'MyPopoverViewController'!") }
            guard let popoverPresentationController = controller.popoverPresentationController else { fatalError("No popoverPresentationController!") }
            guard let rowIndexPath = sender as? IndexPath else { fatalError("Expected sender to be an 'IndexPath'!") }
            guard myData.count > rowIndexPath.row else { fatalError("Index ((rowIndexPath.row)) Out Of Bounds for array (count: (myData.count))!") }
            if self.presentedViewController is MyPopoverViewController {
                self.presentedViewController?.dismiss(animated: false)
            }
            popoverPresentationController.sourceView = self.tableView
            popoverPresentationController.sourceRect = self.tableView.rectForRow(at: rowIndexPath)
            popoverPresentationController.passthroughViews = [self.tableView]
            controller.configure(myData[rowIndexPath.row])
        }
        super.prepare(for: segue, sender: sender)
    }
}

// MARK: - UIScrollViewDelegate

extension MyTableViewController {
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if let popover = self.presentedViewController as? MyPopoverViewController {
            popover.popoverPresentationController?.containerView?.setNeedsLayout()
            popover.popoverPresentationController?.containerView?.layoutIfNeeded()
        }
    }
}
 

Комментарии:

1. спасибо, setneedslayout и layoutifneeded работали как шарм… вы обладаете хорошими знаниями

Ответ №3:

Я использовал тот же метод, что и в другом ответе @Rowan_Jones, однако я не хотел, чтобы размер всплывающего окна действительно менялся. Даже на доли точки. Я понял, что вы можете установить preferredContentSize несколько раз подряд, но визуально его размер изменится только в соответствии с последним значением.

 [vc.popoverPresentationController setSourceRect:newSourceRect];
CGSize finalDesiredSize = CGSizeMake(320, 480);
CGSize tempSize = CGSizeMake(finalDesiredSize.width, finalDesiredSize.height   1);
[vc setPreferredContentSize:tempSize];
[vc setPreferredContentSize:finalDesiredSize];
 

Таким образом, даже если finalDesiredSize оно совпадает с вашим исходным preferredContentSize , это приведет к обновлению всплывающего окна, даже если его размер фактически не изменится.

Ответ №4:

Вот пример того, как повторно ввести всплывающее окно:

 - (void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController willRepositionPopoverToRect:(inout CGRect *)rect     inView:(inout UIView **)view {

     *rect = CGRectMake((CGRectGetWidth((*view).bounds)-2)*0.5f,(CGRectGetHeight((*view).bounds)-2)*0.5f, 2, 2);
 

Я также использовал этот метод, чтобы убедиться, что всплывающее окно переместилось в правильное место после перемещения, установив * rect и * view на исходные sourceRect и sourceView.

В качестве дополнительного примечания я не верю, что этот метод вызывается, когда источник всплывающего окна устанавливается с помощью элемента кнопки панели.

Ответ №5:

Я публикую это, потому что у меня недостаточно баллов для голосования или комментариев. 🙂 Ответ @ turbs сработал для меня отлично. Это должен быть принятый ответ.

Установка *rect на rect, который вам нужен в методе делегирования:

 (void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController
    willRepositionPopoverToRect:(inout CGRect *)rect     
        inView:(inout UIView **)view
 

Ответ №6:

iOS 12.3

[vc.popoverPresentationController устанавливает исходный код:newSourceRect]; [vc.popoverPresentationController.containerView устанавливает необходимый список];