How to set UIScrollView contensize when content is load asynchronously
Asked Answered
E

4

12

My view hierarchy is this

PhotoDetailViewController.swift

    View
      UIScrollView
        UIImageView

I set this up using storyboard, and add four constraints(top=0, bottom=0, leading=0, tailing=0) to UIScrollView, four constraints(top=0, bottom=0, leading=0, tailing=0) to UIImageView, but there are two error says

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

I understand that this is because I haven't set UIScrollView contentSize, but What I trying to do is load photo from PHAsset asynchronously, so I can only get the photo size at run time. So the question is:

1:Given that photo size can only be get at run time, how to solve the "ambiguous scrollable content" error?

2:In which View's life cycle method should I call PHImageManager.requestImageForAsset? because I think I should set UIScrollView contentSize programmatically, but when?

update with 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()
}
}
Ell answered 3/8, 2016 at 5:47 Comment(0)
C
7

Your existing constraints are enough to set the content size, it's just that it's based on the image view intrinsic content size and that doesn't really exist until the image view has an image.

You can add a width and height constraint to the image view with default values and deactivate those constraints when the image is set to the view. Or you could use a placeholder image and avoid those extra constraints because you'd always have an intrinsic content size for the image view.

Chaffee answered 9/8, 2016 at 9:43 Comment(0)
T
3

You should set two more constraint to your imageView.

  1. Horizontally in Container (or you can say it center X)
  2. Fixed Height

Second thing you can put UIView on Scrollview with Constraints like, Top,leading,trailing,bottom,Horizontally in container(center x),fixed height).

Then add your imageview to that view. And can change it's constraint after getting image to resize it's height and width.

You can connect outlet of any constraint and can change it's constant programmatically.

Terrorism answered 3/8, 2016 at 6:1 Comment(3)
Do you mean add ImageView programmatically?Ell
You can add programmatically or from interface builder. If you are adding it from interface builder then you should take outlet of your required constraints that you want to change to resize it after getting image from library or database or else!Terrorism
the reason I am using scroll view is because I want to achieve photo zoom effect, so I do connect each imageview constrains to controller and update the constrain value when photo loaded, bu that seems not working please check out the updateEll
B
3

Xcode UI builder has special type of constraint for such cases (when you can setup constraint only in runtime). It's so called "placeholder constraint" which will be removed at build time but helps to remove constraints errors for developing.

Placeholder constraint

So solution is

  1. Add some sample constraints IB and mark them as placeholders
  2. Add needed constraints in runtime
Barra answered 12/8, 2016 at 10:43 Comment(0)
S
2

When you get the data, just add these lines

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

Hope this helps

Sastruga answered 3/8, 2016 at 5:52 Comment(3)
How about the storyboard error? just leave it there?Ell
You are using autolayout?Sastruga
The errors can be solved by this link "#19036728"Sastruga

© 2022 - 2024 — McMap. All rights reserved.