Как настроить UIScrollView, когда контент загружается асинхронно

Моя иерархия просмотров - это

PhotoDetailViewController.swift

    View
      UIScrollView
        UIImageView

Я устанавливаю это с помощью раскадровки и добавляю четыре ограничения (top = 0, bottom = 0, leading = 0, tailing = 0) в UIScrollView, четыре ограничения (top = 0, bottom = 0, leading = 0, tailing = 0) в UIImageView, но есть две ошибки:

"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()
}
}

Ответ 1

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

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

Ответ 2

Вы должны установить еще два ограничения на imageView.

  • Горизонтально в контейнере (или вы можете сказать, что это центр X)
  • Фиксированная высота

Во-вторых, вы можете положить UIView на Scrollview с такими ограничениями, Top,leading,trailing,bottom,Horizontally in container(center x),fixed height).

Затем добавьте ваше изображение в это представление. И может изменить его ограничение после того, как изображение изменит его высоту и ширину.

Вы можете подключить outlet к любому ограничению и можете программно изменить его.

Ответ 3

Xcode UI builder имеет специальный тип ограничения для таких случаев (когда вы можете установить ограничение только во время выполнения). Это так называемое "ограничение-заполнитель", которое будет удалено во время сборки, но поможет устранить ошибки ограничений для разработки.

Ограничение владельцa

Итак, решение

  • Добавьте некоторые примеры ограничений IB и пометьте их как заполнители.
  • Добавить необходимые ограничения во время выполнения

Ответ 4

Когда вы получаете данные, просто добавьте эти строки

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);

Надеюсь, что это поможет