我正在使用UIPageView来允许用户滚动浏览图像集合,并且在UIPageViewController
设置为Scroll
过渡样式时遇到了约束错误(没有共同祖先),但是设置为Page Curl
时不会。
到目前为止,我已经尝试过将约束的锚点更改为引发错误的错误(我准确地确定了导致错误的罪魁祸首)到不同的事物(尝试过Safe Area
和Superview
),但是什么也没有有所作为。我对Swift还是比较陌生,对UIPageViewController
的工作方式并不了解,所以很有可能我只是以错误的顺序打电话而没有意识到。虽然为什么错误仅发生在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
时,它可以完美运行。在页面视图之外单独加载视图控制器时,视图控制器也可以完美呈现。
编辑:
以下是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)
}
}
}
以及相应的故事板: Storyboard screenshot
答案 0 :(得分:0)
知道这一行imageView.snapshotView(afterScreenUpdates: true)
明确告诉渲染UIImageView
的外观,如果失败,则快照可能没有可见的内容。
我不确定您要达到的目标,但是注释掉该行肯定会解决您的问题。请让我知道是否需要任何帮助来呈现所需内容或其他内容的用户界面。
阅读官方的documentation了解更多详情。