Исключение ограничения в UIPageViewController, только если для стиля установлено значение «Прокрутка» - PullRequest
0 голосов
/ 08 июля 2019

Я использую UIPageView, чтобы позволить пользователям прокручивать коллекцию изображений, и получаю ошибку ограничения (без общего предка), когда UIPageViewController установлен на стиль перехода Scroll, но не когда это установить на Page Curl.

До сих пор я пытался изменить привязки ограничений, выбрасывая ошибки (я точно определил, кто из них является виновником), на разные вещи (пробовал Safe Area и Superview), но ничего не изменило , Я относительно новичок в Swift и мало знаю о том, как работает UIPageViewController, поэтому есть хороший шанс, что я просто называю вещи в неправильном порядке и не осознаю этого. Я немного смущен, хотя, почему ошибка only происходит со стилем перехода Scroll, а не Page Curl, так как мне кажется, что они должны работать аналогичным образом ... Также запутался, почему якоря ограничений не находятся в той же иерархии представления, когда они привязаны к своему собственному суперпредставлению?

Вот делегат UIPageViewController:

class ImageDetailPageViewController: UIPageViewController {

    // MARK: Properties

    var images: [UIImage]!
    var index: Int!

    var navbarHeight: CGFloat!

    fileprivate lazy var pages: [UIViewController] = {
        return getPages()
    }()

    // MARK: Methods

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self
        self.delegate = self

        self.view.translatesAutoresizingMaskIntoConstraints = false

        setViewControllers([pages[index]], direction: .forward, animated: true, completion: nil)
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    // MARK: Methods

    fileprivate func getPages() -> [ImageView] {
        var pages = [ImageView]()

        let storyboard = UIStoryboard(name: "ImageDetail", bundle: nil)

        for image in images {
            let imageDetail = storyboard.instantiateViewController(withIdentifier: "ImageDetail") as! ImageView
            imageDetail.image = image

            pages.append(imageDetail)
        }

        return pages
    }
}

// MARK: Extensions

extension ImageDetailPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
            return nil
        }

        let previousIndex = viewControllerIndex - 1

        guard previousIndex >= 0 else {
            return pages.last
        }

        guard pages.count > previousIndex else {
            return nil
        }

        return pages[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {       
        guard let viewControllerIndex = pages.firstIndex(of: viewController) else {
            return nil
        }

        let nextIndex = viewControllerIndex + 1

        guard nextIndex < pages.count else {
            return pages.first
        }

        guard pages.count > nextIndex else {
            return nil
        }

        return pages[nextIndex]
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return pages.count
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return index
    }
}

extension ImageDetailPageViewController: UIPageViewControllerDelegate { }

Контроллер представления в представлении страницы (ImageDetail) довольно прост. Он состоит из UIScrollView, привязанного к Safe Area, с UIImageView, привязанного к UIScrollView.

Приложение вылетает, как только я пытаюсь перейти к следующей странице в представлении страницы, выдавая следующую ошибку:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x600001b72400 "UIImageView:0x7ff44c56caf0.top"> and <NSLayoutYAxisAnchor:0x600001b77e40 "UIScrollView:0x7ff44d164800.top"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal.'

При установке на Page Curl работает отлично. Контроллер представления также прекрасно воспроизводится, когда загружается сам по себе, за пределами представления страницы.

EDIT: Вот код для ImageDetail вида:

class ImageView: UIViewController, UIScrollViewDelegate {

    // MARK: Properties

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet var zoomGestureRecognizer: UITapGestureRecognizer!

    // imageView contraints
    @IBOutlet var imageViewBottomConstraint: NSLayoutConstraint!
    @IBOutlet var imageViewLeadingConstraint: NSLayoutConstraint!
    @IBOutlet var imageViewTopConstraint: NSLayoutConstraint!
    @IBOutlet var imageViewTrailingConstraint: NSLayoutConstraint!

    var image: UIImage!

    // MARK: Overrides

    override func viewDidLoad() {
        super.viewDidLoad()

        // set the image
        if let image = image {
            imageView.image = image
        } else {
            // log error
            os_log("Error. Image Detail opened without an image loaded.", log: OSLog.default, type: .error)

            // dismiss view
            self.dismiss(animated: false, completion: nil)
        }

        // assume scrollView delegate
        scrollView.delegate = self
        scrollView.maximumZoomScale = 2

        // set imageView snapshot mode
        imageView.snapshotView(afterScreenUpdates: true)
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        updateMinZoomScaleForSize(view.compatibleSafeAreaLayoutGuide.layoutFrame.size)
        updateConstraintsForSize(view.compatibleSafeAreaLayoutGuide.layoutFrame.size)
        scrollView.setZoomScale(scrollView.minimumZoomScale, animated: false)
    }

    // MARK: Methods

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        updateConstraintsForSize(view.compatibleSafeAreaLayoutGuide.layoutFrame.size)
    }

    fileprivate func updateConstraintsForSize(_ size: CGSize) {
        let yOffset = max(0, (view.compatibleSafeAreaLayoutGuide.layoutFrame.height - imageView.frame.height) / 2)
        imageViewTopConstraint.constant = yOffset
        imageViewBottomConstraint.constant = yOffset

        let xOffset = max(0, (view.compatibleSafeAreaLayoutGuide.layoutFrame.width - imageView.frame.width) / 2)
        imageViewLeadingConstraint.constant = xOffset
        imageViewTrailingConstraint.constant = xOffset

        // activate constraints
        imageViewTopConstraint.isActive = true
        imageViewBottomConstraint.isActive = true
        imageViewLeadingConstraint.isActive = true
        imageViewTrailingConstraint.isActive = true

        view.layoutIfNeeded()
    }

    fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {
        let widthScale = view.compatibleSafeAreaLayoutGuide.layoutFrame.width / imageView.bounds.width
        let heightScale = view.compatibleSafeAreaLayoutGuide.layoutFrame.height / imageView.bounds.height
        let minScale = min(widthScale, heightScale)

        scrollView.minimumZoomScale = minScale
        scrollView.zoomScale = minScale
    }

    fileprivate func zoomRectForScale(_ scale: CGFloat, center: CGPoint) -> CGRect {
        // create and size view window
        var zoomRect = CGRect.zero
        zoomRect.size.height = imageView.frame.size.height
        zoomRect.size.width = imageView.frame.size.width

        // center on tapped point
        let newCenter = imageView.convert(center, from: view)
        zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
        zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)

        // return rect
        return zoomRect
    }

    // MARK: Actions

    @IBAction func tapToZoom(_ sender: UITapGestureRecognizer) {
        if scrollView.zoomScale == scrollView.minimumZoomScale {
            scrollView.zoom(to: zoomRectForScale(scrollView.maximumZoomScale, center: zoomGestureRecognizer.location(in: zoomGestureRecognizer.view)), animated: true)
        } else {
            scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
        }
    }
}

И соответствующая раскадровка: Скриншот раскадровки

1 Ответ

0 голосов
/ 10 июля 2019

Узнал, что эта строка imageView.snapshotView(afterScreenUpdates: true) явно указывает на визуализацию вида UIImageView, и в случае сбоя снимок может не иметь видимого содержимого.

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

Для получения более подробной информации ознакомьтесь с официальной документацией .

...