当内容异步加载时如何设置UIScrollView的内容大小

12

我的视图层次结构是这样的

PhotoDetailViewController.swift

    View
      UIScrollView
        UIImageView

我使用了Storyboard来设置这个内容,并对UIScrollView添加了四个约束(上=0、下=0、左=0、右=0),对UIImageView也添加了四个约束(上=0、下=0、左=0、右=0),但是出现了两个错误提示:

"ScrollView has ambiguous scrollable content width"
"ScrollView has ambiguous scrollable content height"

我知道这是因为我没有设置UIScrollView的contentSize,但是我想要做的是异步地从PHAsset加载照片,所以我只能在运行时获取照片大小。所以问题是:

1:考虑到照片大小只能在运行时获取,如何解决“模糊可滚动内容”错误?

2:我应该在哪个视图的生命周期方法中调用PHImageManager.requestImageForAsset?因为我认为我应该通过编程方式设置UIScrollView的contentSize,但是何时呢?

使用PhotoDetailViewController.swift进行更新。

import UIKit
import Photos

class PhotoDetailViewController : UIViewController {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
@IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!

var devicePhotosAsset : PHFetchResult!
var index = 0
var photo : UIImage!
var imgManager:PHImageManager!

@IBOutlet weak var imageView : UIImageView!

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
}

override func awakeFromNib() {
    self.imgManager = PHImageManager()
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.displayPhoto()
}

override func viewWillLayoutSubviews() {
    super.viewDidLayoutSubviews()
    updateMinZoomScaleForSize()
    updateConstraintsForSize()
}

func displayPhoto () {
    _ = self.imgManager.requestImageForAsset(self.devicePhotosAsset[self.index] as! PHAsset, targetSize: PHImageManagerMaximumSize, contentMode: .AspectFit, options: nil, resultHandler: {(result, info) -> Void in
            NSOperationQueue.mainQueue().addOperationWithBlock(){
                self.imageView.image = result
            }

        })
}

private func targetSize() -> CGSize {
    let scale = UIScreen.mainScreen().scale
    let targetSize = CGSizeMake(CGRectGetWidth(self.imageView.bounds)*scale, CGRectGetHeight(self.imageView.bounds)*scale)

    return targetSize
}

private func updateMinZoomScaleForSize() {
    let size = scrollView.bounds.size
    let widthScale = size.width / imageView.bounds.width
    let heightScale = size.height / imageView.bounds.height
    let minScale = min(widthScale, heightScale)

    scrollView.minimumZoomScale = minScale

    scrollView.zoomScale = minScale
}

func recenterImage(){
    let scrollViewSize = scrollView.bounds.size
    let imageSize = imageView.frame.size

    let horizontalSpace = imageSize.width < scrollViewSize.width ? (scrollViewSize.width - imageSize.width)/2 : 0
    let verticalSpace = imageSize.height < scrollViewSize.height ? (scrollViewSize.height - imageSize.height)/2 : 0
    scrollView.contentInset = UIEdgeInsets(top: verticalSpace, left: horizontalSpace, bottom: verticalSpace, right: horizontalSpace)
}

private func updateConstraintsForSize() {
    let size = scrollView.bounds.size
    let yOffset = max(0, (size.height - imageView.frame.height) / 2)
    imageViewTopConstraint.constant = yOffset
    imageViewBottomConstraint.constant = yOffset

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

    view.layoutIfNeeded()
}
}

extension PhotoDetailViewController: UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return imageView
}

func scrollViewDidZoom(scrollView: UIScrollView) {
    updateConstraintsForSize()
}
}
4个回答

7

您现有的约束已足以设置内容大小,只是它基于图像视图的内在内容大小,而在图像视图具有图像之前,该大小实际上并不存在。

您可以添加一个带有默认值的宽度和高度约束到图像视图中,并在将图像设置到视图时取消这些约束。或者,您可以使用占位符图像并避免这些额外的约束,因为您始终会有图像视图的内在内容大小。


3
Xcode UI构建器有一种特殊类型的约束,用于在运行时仅设置约束的情况。这被称为“占位符约束”,它将在构建时被删除,但有助于消除开发中的约束错误。

Placeholder constraint

所以解决方案是:
  1. 添加一些示例约束 IB 并将它们标记为占位符
  2. 在运行时添加所需的约束

3
你需要给你的imageView设置两个约束条件:
  1. 水平居中(或称为中心X)
  2. 固定高度
其次,你可以按照以下约束条件,在ScrollView上放置UIViewTop,leading,trailing,bottom,Horizontally in container(center x),fixed height。然后将你的imageView添加到该视图中。获取图片后,可以更改其约束条件来调整其高度和宽度。
你可以连接任何一个约束条件的outlet并通过编程方式更改它的常量。

您的意思是通过编程方式添加 ImageView 吗? - paynestrike
您可以通过编程或界面构建器添加约束。如果您是从界面构建器中添加它,则应该获取所需约束的输出,以便在从库或数据库获取图像后调整其大小,否则! - Ketan Parmar
我使用滚动视图的原因是为了实现照片缩放效果,所以我将每个ImageView的约束连接到控制器,并在加载照片时更新约束值,但似乎并没有起作用,请查看更新。 - paynestrike

2
当您获取数据时,只需添加以下代码行:
float sizeOfContent = 0;
UIView *lLast = [yourscrollview.subviews lastObject];
NSInteger wd = lLast.frame.origin.y;
NSInteger ht = lLast.frame.size.height;
sizeOfContent = wd+ht;
yourscrollview.contentSize = CGSizeMake(yourscrollview.frame.size.width, sizeOfContent);

希望这能帮到你。

故事板错误怎么办?就这样放着吗? - paynestrike
你正在使用自动布局吗? - gurmandeep
错误可以通过此链接解决 "https://dev59.com/x2Mk5IYBdhLWcg3wtQFd"。 - gurmandeep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接