Uiscrollview with Embedded Uiimageview; How to Get the Image to Fill the Screen

UIScrollView with Embedded UIImageView; how to get the image to fill the screen

You might find this useful...

It allows you to zoom an image in a scrollView, starting with it centered and maintaining aspect ratio.

Here's a complete implementation. It has two important variables at the top:

// can be .scaleAspectFill or .scaleAspectFit
var fitMode: UIView.ContentMode = .scaleAspectFill

// if fitMode is .scaleAspectFit, allowFullImage is ignored
// if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
// if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
// if allowFullImage is true, image can be "pinched in" to see the full image
var allowFullImage: Bool = true

Everything is done via code - no @IBOutlet or other connections - so just create add a new view controller and assign its custom class to ZoomAspectViewController (and edit the name of the image you want to use):

class ZoomAspectViewController: UIViewController, UIScrollViewDelegate {

var scrollView: UIScrollView!
var imageView: UIImageView!
var imageViewBottomConstraint: NSLayoutConstraint!
var imageViewLeadingConstraint: NSLayoutConstraint!
var imageViewTopConstraint: NSLayoutConstraint!
var imageViewTrailingConstraint: NSLayoutConstraint!

// can be .scaleAspectFill or .scaleAspectFit
var fitMode: UIView.ContentMode = .scaleAspectFit

// if fitMode is .scaleAspectFit, allowFullImage is ignored
// if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
// if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
// if allowFullImage is true, image can be "pinched in" to see the full image
var allowFullImage: Bool = true

override func viewDidLoad() {
super.viewDidLoad()

guard let img = UIImage(named: "myImage") else {
fatalError("Could not load the image!!!")
}

scrollView = UIScrollView()
imageView = UIImageView()

scrollView.translatesAutoresizingMaskIntoConstraints = false
imageView.translatesAutoresizingMaskIntoConstraints = false

imageView.contentMode = .scaleToFill

scrollView.addSubview(imageView)
view.addSubview(scrollView)

// respect safe area
let g = view.safeAreaLayoutGuide

imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: scrollView.topAnchor)
imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)

NSLayoutConstraint.activate([

scrollView.topAnchor.constraint(equalTo: g.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),

imageViewTopConstraint,
imageViewBottomConstraint,
imageViewLeadingConstraint,
imageViewTrailingConstraint,

])

scrollView.delegate = self
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 5.0

imageView.image = img
imageView.frame.size = img.size

}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
self.updateMinZoomScaleForSize(size, shouldSize: (self.scrollView.zoomScale == self.scrollView.minimumZoomScale))
self.updateConstraintsForSize(size)
}, completion: {
_ in
})
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateMinZoomScaleForSize(scrollView.bounds.size)
updateConstraintsForSize(scrollView.bounds.size)

if fitMode == .scaleAspectFill {
centerImageView()
}

}

func updateMinZoomScaleForSize(_ size: CGSize, shouldSize: Bool = true) {
guard let img = imageView.image else {
return
}

var bShouldSize = shouldSize

let widthScale = size.width / img.size.width
let heightScale = size.height / img.size.height

var minScale = min(widthScale, heightScale)
let startScale = max(widthScale, heightScale)

if fitMode == .scaleAspectFill && !allowFullImage {
minScale = startScale
}
if scrollView.zoomScale < minScale {
bShouldSize = true
}
scrollView.minimumZoomScale = minScale
if bShouldSize {
scrollView.zoomScale = fitMode == .scaleAspectFill ? startScale : minScale
}
}

func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateConstraintsForSize(scrollView.bounds.size)
}

func centerImageView() -> Void {
let yOffset = (scrollView.frame.size.height - imageView.frame.size.height) / 2
let xOffset = (scrollView.frame.size.width - imageView.frame.size.width) / 2
scrollView.contentOffset = CGPoint(x: -xOffset, y: -yOffset)
}

func updateConstraintsForSize(_ size: CGSize) {
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()
}

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

}

Edit

As an example, I used this image (2560 x 1440):

Sample Image

and I get this result on launch:

Sample Image

and maximum zoom in (5.0) scrolled to top-center:

Sample Image


Edit 2

Same image, at launch, with:

var fitMode: UIView.ContentMode = .scaleAspectFill

instead of .scaleAspectFit:

Sample Image

How can I fill image in scroll view to fill screen?

In this case you need to enable the parent view clipsToBounds. Set UIScrollview clipsToBounds property to True.

Programmatically scrollView.clipsToBounds = true

In UIStoryBoard - Click the view->Attributes Inspector

If you would like to see the whole screen, make sure to add the topConstraint of scrollView assigned superView and hide the navigationBar in viewWillAppear,

 override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
  1. Make sure to remove the status bar by

    override var prefersStatusBarHidden: Bool {
    return true
    }
  2. Update the Y position of Image.

    imageView.frame = CGRect(x: x, y: **self.scrollView.frame.minY**, width: self.view.frame.width, height: self.view.frame.height)
  3. Update the scrollView topConstraint by -20.

How do I get the position of the UIImageView in my UIScrollView and only allow it to be panned?

Is it possible to make it so only the image can be panned, not the full scroll view?

Implement - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch in your gesture recogniser's delegate, then test that UITouch's position against that of your image view.

So, probably something like:

CGPoint touchLocation = [touch locationInView:scrollView];
return CGRectContainsPoint(imageView.frame, touchLocation);

How do I get the location the image is at currently?

According to which coordinate space? Assuming you mean relative to a view controller's top-level view, use [viewController.view convertRect:imageView.frame fromView:scrollView].

Scroll views scroll their content by changing their bounds. They don't change anything on the included views. UIView -convertRect:fromView and the others consolidate all the logic for mapping from the internal coordinate space of one view to another, adjusting for transforms and internal scrolling and everything.

UIImage in UIImageView that is in UIScrollView is automatically zoomed in - how to initially view whole UIImage?

You want to set the .minimumZoomScale to the ratio between your image size and the scroll view size, and then set the .zoomScale to that value to start with.

Here's your code, with a few modifications:

class ViewImageViewController: UIViewController, UIScrollViewDelegate {

var imageToPresent: UIImage!

let scrollView: UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

let imageView: UIImageView = {
let imgView = UIImageView()
imgView.backgroundColor = UIColor.black
imgView.contentMode = .scaleAspectFit
imgView.isUserInteractionEnabled = true
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()

override func viewDidLoad() {
super.viewDidLoad()
print("ViewImageViewController.viewDidLoad")

if let img = UIImage(named: "background") {
imageToPresent = img
}

view.backgroundColor = .gray // UIColor.appGrayForLabels
setupViews()
scrollView.delegate = self

}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

let scrollViewFrame = scrollView.frame
let scaleWidth = scrollViewFrame.size.width / imageToPresent.size.width
let scaleHeight = scrollViewFrame.size.height / imageToPresent.size.height

let minScale = min(scaleWidth, scaleHeight)

scrollView.minimumZoomScale = minScale
scrollView.maximumZoomScale = 4.0

scrollView.zoomScale = minScale
}

private func setupViews(){

scrollView.isScrollEnabled = true

view.addSubview(scrollView)

scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true


guard let img = imageToPresent else{return}
imageView.image = img
scrollView.addSubview(imageView)

let g = scrollView.contentLayoutGuide

imageView.topAnchor.constraint(equalTo: g.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: g.bottomAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: g.leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: g.trailingAnchor).isActive = true

imageView.widthAnchor.constraint(equalToConstant: imageToPresent.size.width).isActive = true
imageView.heightAnchor.constraint(equalToConstant: imageToPresent.size.height).isActive = true

}

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

}

Can't scroll UIImageView with AspectFill in UIScrollView and AutoLayout

the child imageview is also pinned to all sides

Instead, turn off auto layout for the child image view (set its translates... to YES). You are already setting the scroll view's contentSize to the image size, so now scrollability will leap into life and will interface correctly with zooming.



Related Topics



Leave a reply



Submit