How to make a UIImage Scrollable inside a UIScrollView?
Solution 1. Updating Frames
Firstly, make sure the views hierarchy is correctly setup (the imageView
is added to the scrollView
):
scrollView.addSubview(imageView)
Then I'd suggest rewriting this code as something like:
let image = UIImage(named: "myImg")
imageView.image = image // setup image to imageView
imageView.frame.size = image?.size ?? .zero // setup image size or zero to imageView
scrollView.contentSize = image.size ?? .zero // setup image size or zero as a content size
Solution 2. Using constraints
There is also another solution using constraints instead of manually setting up the sizes. This takes a bit more code but doesn't require sizes recomputing when you change the image.
scrollView.addSubview(imageView) // Setup the views hierarchy
imageView.translatesAutoresizingMaskIntoConstraints = false // Make sure constraints won't interfere with autoresizing mask
NSLayoutConstraint.activate(
[
imageView.leftAnchor.constraint(equalTo: scrollView.leftAnchor), // attaching to the left
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor), // attaching to the top
imageView.rightAnchor.constraint(equalTo: scrollView.rightAnchor), // attaching to the right
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) // attaching to the bottom
]
)
Then we replace existing image with a new one like this:
imageView.image = UIImage(named: "myImg")
Both approaches work for me in Playground. So, if these still won't work for you, please share more information about how you set up the hierarchy and additional parameters of the scroll view.
Adding UIImage to new Scroll View
You need to add the UIImageView
containing the image of your lights to the UIView
that is the contentView of the UIScrollView
.
An easy way to do this is to create an @IBOutlet
for the UIView
in your scrollView. control-drag from the View
under the ScrollView
in the Document Outline to your ViewController
code. Give the @IBOutlet
a name such as:
@IBOutlet weak var scrollViewsView: UIView!
Then, use it to add your adornments:
@IBAction func btnAddColorfulLights1(_ sender: Any) {
let image = UIImage(named: "ColorfulLights.png")!
let imageView = UIImageView(image: image)
self.scrollViewsView.addSubview(imageView)
imageView.tag = 10
imageView.frame = CGRect(x: 67, y: 39, width: 240, height: 338)
}
Scrolling a UIImage using UIScrollView
The issue here was that scrollView.contentSize was not large enough so when attempting to scroll it wasn't picking up the movements.
Changing the values to fill the frame as well as implementing the delegate protocol allowed me to use the scroll view.
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
UIImageView cuts inside UIScrollView
You want to make use of the scroll view's Content and Frame Layout Guides...
- constrain all 4 sides of the stack view to the scroll view's Content Layout Guide
- constrain the stack view's Height to the scroll view's Frame Layout Guide
for each image view you add to the stack view:
- constrain the image view's Width to the scroll view's Frame Layout Guide
Here is a complete example:
class ViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// just trying to include what you've shown
let textSV = UILabel()
textSV.backgroundColor = .yellow
textSV.text = "textSV"
textSV.textAlignment = .center
let anotherView = UILabel()
anotherView.backgroundColor = .cyan
anotherView.text = "anotherView"
anotherView.textAlignment = .center
[textSV, anotherView].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
// respect safe area
let safeG = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textSV.topAnchor.constraint(equalTo: safeG.topAnchor),
textSV.leadingAnchor.constraint(equalTo: safeG.leadingAnchor),
textSV.trailingAnchor.constraint(equalTo: safeG.trailingAnchor),
textSV.heightAnchor.constraint(equalToConstant: 60.0),
anotherView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor),
anotherView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor),
anotherView.bottomAnchor.constraint(equalTo: safeG.bottomAnchor),
anotherView.heightAnchor.constraint(equalToConstant: 60.0),
])
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .green
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
view.addSubview(scrollView)
// use a stack view to hold and arrange the scrollView's subviews
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
// add the stackView to the scrollView
scrollView.addSubview(stackView)
// use scrollView's Content Layout Guide to define scrollable content
let layoutG = scrollView.contentLayoutGuide
// use scrollView's Frame Layout Guide to define content height (since you want horizontal scrolling)
let frameG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView Top to textSV Bottom
scrollView.topAnchor.constraint(equalTo: textSV.bottomAnchor),
// constrain scrollView Leading/Trailing to safe area
scrollView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor),
// constrain scrollView Bottom to anotherView Top
scrollView.bottomAnchor.constraint(equalTo: anotherView.topAnchor),
// constrain all 4 sides of the stackView to scrollView's Content Layout Guide
stackView.topAnchor.constraint(equalTo: layoutG.topAnchor),
stackView.bottomAnchor.constraint(equalTo: layoutG.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: layoutG.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: layoutG.trailingAnchor),
// constrain stackView's height to scrollView's Frame Layout Guide height
stackView.heightAnchor.constraint(equalTo: frameG.heightAnchor),
])
// add imageViews to the stack view
for _ in 1...8 {
let pageView = UIImageView(image: UIImage(named: "iphone12mockup"))
//let pageView = UIImageView(image: UIImage(named: "sample"))
// set image view background color so you can
// see its frame (since the image will be aspect-fit scaled)
pageView.backgroundColor = .systemYellow
pageView.contentMode = .scaleAspectFit
// add it to the stack view
stackView.addArrangedSubview(pageView)
// constrain its Width to scrollView's Frame Layout Guide Width
pageView.widthAnchor.constraint(equalTo: frameG.widthAnchor).isActive = true
}
}
}
It will look like this on startup (on an iPhone 8):
and after scrolling a little to the right:
Note that since you want the image view set to Aspect Fit
, I gave the "pageView" image views a background color of .systemYellow
so you can see that the imageView frame fills the scroll view frame width and height.
Edit -- if you want the images to be proportional to their height, without "empty space on the sides," you need to set the image view width constraint proportional to its height, based on the image size.
Replace the "add image views" loop with this:
// add imageViews to the stack view
for _ in 1...8 {
guard let img = UIImage(named: "iphone12mockup") else {
fatalError("Could not load image!")
}
let pageView = UIImageView()
pageView.image = img
pageView.contentMode = .scaleToFill
// add it to the stack view
stackView.addArrangedSubview(pageView)
// constrain its Width proportional to the image height
pageView.widthAnchor.constraint(equalTo: pageView.heightAnchor, multiplier: img.size.width / img.size.height).isActive = true
}
and the output will be:
and after scrolling a little to the right:
Ever scrolling UIScrollView with UIImage
I've never seen the “Behance” app, but I guess you're asking how to animate a seamlessly tiled background image across the screen indefinitely, like this:
(Pattern image by Evan Eckard.)
I used an animation duration of 1 second for the demo, but you probably want a much longer duration in a real app.
You shouldn't use a timer for this. Core Animation can perform the animation for you, and letting it perform the animation smoother and more efficient. (You might think Core Animation is performing your animation since you're using UIView
animation, but I believe animating a scroll view's contentOffset
does not use Core Animation because the scroll view has to call its delegate's scrollViewDidScroll
on every animation step.)
You also shouldn't use a scroll view for this. UIScrollView
exists to allow the user to scroll. Since you're not letting the user scroll, you shouldn't use UIScrollView
.
Here's how you should set up your background:
Create two identical image views (numbered 0 and 1), showing the same image. Make sure the image views are each big enough to fill the screen.
Put the left edge of image view 0 at the left edge of your root view. Put the left edge of image view 1 at the right edge of image view 0. Since each image view is big enough to fill the screen, image view 1 will start out entirely off the right edge of the screen.
Animate image view 0's
transform.translation.x
from 0 to-imageView.bounds.size.width
. This will make it slide to the left by precisely its own width, so when the animation reaches its end, image view 0's right edge is at the left edge of the screen (and thus image view 0 is entirely off the left edge of the screen). Set the animation'srepeatCount
to.infinity
.Add the same animation to image view 1. Thus image view 1 comes onto the screen as image view 0 is leaving it, exactly covering the pixels revealed by image view 0's animation.
The two animations end at exactly the same time. When they end, image view 1 is exactly where image view 0 was at the start. Since both animations are set to repeat infinitely, they both immediately start over. When image view 0's animation starts over, image view 0 instantly jumps back to its starting position, which is where image view 1 ended up. Since both image views show the same image, the pixels on screen don't change. This makes the animation loop seamless.
Here's my code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
for imageView in imageViews {
imageView.image = patternImage
imageView.contentMode = .scaleToFill
view.addSubview(imageView)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bounds = view.bounds
let patternSize = patternImage.size
// Scale the image up if necessary to be at least as big as the screen on both axes.
// But make sure scale is at least 1 so I don't shrink the image if it's larger than the screen.
let scale = max(1 as CGFloat, bounds.size.width / patternSize.width, bounds.size.height / patternSize.height)
let imageFrame = CGRect(x: 0, y: 0, width: scale * patternSize.width, height: scale * patternSize.height)
for (i, imageView) in imageViews.enumerated() {
imageView.frame = imageFrame.offsetBy(dx: CGFloat(i) * imageFrame.size.width, dy: 0)
let animation = CABasicAnimation(keyPath: "transform.translation.x")
animation.fromValue = 0
animation.toValue = -imageFrame.size.width
animation.duration = 1
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: .linear)
// The following line prevents iOS from removing the animation when the app goes to the background.
animation.isRemovedOnCompletion = false
imageView.layer.add(animation, forKey: animation.keyPath)
}
}
private let imageViews: [UIImageView] = [.init(), .init()]
private let patternImage = #imageLiteral(resourceName: "pattern")
}
UIImageView and UIScrollView zooming
All you need to do is add your UIImageView
(or any view you want to zoom) inside your UIScrollView
.
Set your maximumZoomScale
on your UIScrollView
to any value higher than 1.0f.
Set yourself as the delegate of your UIScrollView
and return the UIImageView
in the viewForZooming delegate method.
That's it. No pinch gesture needed, no nothing. UIScrollView
handles pinch zooming for you.
IOS: add imageview in a scrollview to have zoom
- Set your view controller up as a
<UIScrollViewDelegate>
- Draw your
UIScrollView
the size you want for the rectangle at the center of the view. Set the max zoom in the inspector to something bigger than 1. Like 4 or 10. - Right click on the scroll view and connect the delegate to your view controller.
- Draw your
UIImageView
in theUIScrollView
and set it up with whatever image you want. Make it the same size as theUIScrollView
. - Ctrl + drag form you
UIImageView
to the.h
of your View controller to create anIBOutlet
for theUIImageView
, call it something clever likeimageView
. Add this code:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}Run the app and pinch and pan til your heart's content.
Related Topics
Handle Multiple File (Image) Uploads to Aws S3 Swift
Please Specify a Platform for This Target in Your Podfile
Xcode6/Swift: Unrecognized Selector Sent to Instance
Auto Focus and Auto Exposure in Avfoundation on Custom Camera Layer
Setting Custom Http Headers in Alamofire in iOS 7 Not Working
Unusernotificationcenter.Current().Getdeliverednotifications Only Ever Returns an Empty Array
Get Center Coordinates from Mapkit and Display in Uilabel
Cocoa Singleton and Shared Instances
Replaykit Startrecording Sometimes Never Enters Completion Handler
How to Crop a 4*3 Image in Photolibrary or Camera in Swift
How to Convert an Int into Nsdata in Swift
Changing the Shape of a Uiview Border
How to Programmatically Check and Open an Existing App in iOS 8
How to Make a Popup Window with an Image Swift
Using Cloud Code with the Parse Server and Heroku