Change particular color in image including shades - swift

I would like to specifically change the UIImage color to any color (excluding the gray borders and lines and preserving the shading effect), using an extension method as shown below:
extension UIImage {
func replaceFillColorTo(_ color: UIColor) {
// what should I do in here?
}
}
I would like the result to be as shown below:
The white shirt is the input and each colored shirt is the output using different colors.

The most straight-forward way to do this is to generate your "template" image with transparency / alpha levels.
For example:
I used this image - had to do a little editing so it's close to your image... (if you download it you can see the transparency matches the above):
Then we can change the background color of the imageView, and get these results:
and this example code to produce the screen-caps above:
class ViewController: UIViewController {
let imgView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
guard let img = UIImage(named: "vShirt")
else { return }
imgView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imgView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
imgView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: img.size.height / img.size.width)
])
imgView.image = img
imgView.backgroundColor = .white
let colors: [UIColor] = [
.white,
.systemGreen,
.systemBlue,
.systemPink,
.yellow,
]
let stackView = UIStackView()
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
stackView.heightAnchor.constraint(equalToConstant: 50.0),
])
colors.forEach { c in
let v = UIView()
v.backgroundColor = c
v.layer.borderColor = UIColor.black.cgColor
v.layer.borderWidth = 1
stackView.addArrangedSubview(v)
let t = UITapGestureRecognizer(target: self, action: #selector(colorTap(_:)))
v.addGestureRecognizer(t)
}
}
#objc func colorTap(_ g: UITapGestureRecognizer) {
guard let v = g.view else { return }
imgView.backgroundColor = v.backgroundColor
}
}

let image = UIImage(named: "Swift")?.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: image)
imageView.tintColor = .systemPink
You are not able to change the colour of a UIImage(). You are able to change the TINT.
Here is a resource for you to review.
https://sarunw.com/posts/how-to-change-uiimage-color-in-swift/
extension UIImage {
func maskWithColor(color: UIColor) -> UIImage? {
let maskImage = cgImage!
let width = size.width
let height = size.height
let bounds = CGRect(x: 0, y: 0, width: width, height: height)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
context.clip(to: bounds, mask: maskImage)
context.setFillColor(color.cgColor)
context.fill(bounds)
if let cgImage = context.makeImage() {
let coloredImage = UIImage(cgImage: cgImage)
return coloredImage
} else {
return nil
}
}
}
Perhaps this function will also help you.

Related

How to make a round SF symbol shaped exactly in Non-SwiftUI environment

Sometimes SF Symbols are rounded, but in opposite to SwiftUI's clipShape() procedure I can't reach a solution in Storyboard/Normal Swift.
I've tried with
func setCorner(radius: CGFloat) {
layer.cornerRadius = radius
clipsToBounds = true
}
func circleCorner() {
superview?.layoutIfNeeded()
setCorner(radius: frame.height / 2)
}
but an ugly red ring remains like this:
SF Symbols are designed to be used more like a font character - with "padding" on the sides - so we need to "remove" that padding.
Here is one approach by sub-classing UIImageView:
class FlagImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
let nm = "flag.circle.fill"
// create SF Symbol image with point size equal to bounds height
let cfg = UIImage.SymbolConfiguration(pointSize: bounds.height)
guard let imgA = UIImage(systemName: nm, withConfiguration: cfg) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
// get a cgRef from imgA
guard let cgRef = imgA.cgImage else {
fatalError("Could not get cgImage!")
}
// create imgB from the cgRef
// this will remove the "padding"
let imgB = UIImage(cgImage: cgRef, scale: imgA.scale, orientation: imgA.imageOrientation)
.withTintColor(.white, renderingMode: .alwaysOriginal)
self.image = imgB
// add a round mask, inset by 1.0 so we don't see the anti-aliased edge
let msk = CAShapeLayer()
msk.path = UIBezierPath(ovalIn: bounds.insetBy(dx: 1.0, dy: 1.0)).cgPath
layer.mask = msk
}
}
Example view controller:
class FlagVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
let greenView = UIView()
greenView.backgroundColor = .systemGreen
let flagView = FlagImageView(frame: .zero)
flagView.backgroundColor = .red
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)
flagView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(flagView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
greenView.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
greenView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 100.0),
greenView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
greenView.heightAnchor.constraint(equalToConstant: 80.0),
flagView.topAnchor.constraint(equalTo: greenView.topAnchor, constant: 8.0),
flagView.leadingAnchor.constraint(equalTo: greenView.leadingAnchor, constant: -12.0),
flagView.widthAnchor.constraint(equalToConstant: 40.0),
flagView.heightAnchor.constraint(equalTo: flagView.widthAnchor),
])
greenView.layer.cornerRadius = 40.0
}
}
Result:
There is a detailed explanation about SF Symbol usage here: https://stackoverflow.com/a/71743787/6257435

stackView content (SF-Symbols) distorted

I want to replicate the Apple Map App Buttons as seen here:
This is the code so far:
class CustomView: UIImageView {
init(frame: CGRect, corners: CACornerMask, systemName: String) {
super.init(frame: frame)
self.createBorders(corners: corners)
self.createImage(systemName: systemName)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createBorders(corners: CACornerMask) {
self.contentMode = .scaleAspectFill
self.clipsToBounds = false
self.layer.cornerRadius = 20
self.layer.maskedCorners = corners
self.layer.masksToBounds = false
self.layer.shadowOffset = .zero
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowRadius = 20
self.layer.shadowOpacity = 0.2
self.backgroundColor = .white
let shadowAmount: CGFloat = 2
let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
self.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
func createImage(systemName: String) {
let image = UIImage(systemName: systemName)!
let renderer = UIGraphicsImageRenderer(bounds: self.frame)
let renderedImage = renderer.image { (_) in
image.draw(in: frame.insetBy(dx: 30, dy: 30))
}
And use it like this:
let size = CGSize(width: 100, height: 100)
let frame = CGRect(origin: CGPoint(x: 100, y: 100), size: size)
let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
let locationView = CustomView(frame: frame, corners: [], systemName: "location")
let twoDView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "view.2d")
let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")
let stackView = UIStackView(arrangedSubviews: [infoView, locationView, twoDView, binocularsView])
stackView.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 100, height: 400))
stackView.distribution = .fillEqually
stackView.axis = .vertical
stackView.setCustomSpacing(20, after: twoDView)
view.addSubview(stackView)
Which makes it look like this:
AND: There aren't the thin lines between each Icon of the stackView :/
Thanks for your help!
Not sure what's going on, because when I ran you code as-is I did not get the stretched images you've shown.
To get "thin lines between each Icon", you could either modify your custom image view to add the lines to the middle icon image, or, what might be easier, would be to use a 1-point height UIView between each icon.
Also, you'll be better off using auto-layout.
See if this gives you the desired result:
class CustomView: UIImageView {
init(frame: CGRect, corners: CACornerMask, systemName: String) {
super.init(frame: frame)
self.createBorders(corners: corners)
self.createImage(systemName: systemName)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createBorders(corners: CACornerMask) {
self.contentMode = .scaleAspectFill
self.clipsToBounds = false
self.layer.cornerRadius = 20
self.layer.maskedCorners = corners
self.layer.masksToBounds = false
self.layer.shadowOffset = .zero
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowRadius = 20
self.layer.shadowOpacity = 0.2
self.backgroundColor = .white
let shadowAmount: CGFloat = 2
let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
self.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
func createImage(systemName: String) {
guard let image = UIImage(systemName: systemName)?.withTintColor(.blue, renderingMode: .alwaysOriginal) else { return }
let renderer = UIGraphicsImageRenderer(bounds: self.frame)
let renderedImage = renderer.image { (_) in
image.draw(in: frame.insetBy(dx: 30, dy: 30))
}
self.image = renderedImage
}
}
class TestCustomViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
let size = CGSize(width: 100, height: 100)
let frame = CGRect(origin: .zero, size: size)
let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
let locationView = CustomView(frame: frame, corners: [], systemName: "location")
let twoDView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "view.2d")
let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")
// create 2 separator views
let sep1 = UIView()
sep1.backgroundColor = .blue
let sep2 = UIView()
sep2.backgroundColor = .blue
let stackView = UIStackView(arrangedSubviews: [infoView, sep1, locationView, sep2, twoDView, binocularsView])
// use .fill, not .fillEqually
stackView.distribution = .fill
stackView.axis = .vertical
stackView.setCustomSpacing(20, after: twoDView)
view.addSubview(stackView)
// let's use auto-layout
stackView.translatesAutoresizingMaskIntoConstraints = false
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// only need top and leading constraints for the stack view
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 100.0),
// both separator views need height constraint of 1.0
sep1.heightAnchor.constraint(equalToConstant: 1.0),
sep2.heightAnchor.constraint(equalToConstant: 1.0),
])
// set all images to the same 1:1 ratio size
[infoView, locationView, twoDView, binocularsView].forEach { v in
v.widthAnchor.constraint(equalToConstant: size.width).isActive = true
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
}
}
}
This is what I get with that code example:

Resize Image in stackView, keep aspect-ratio

I want to clone the Apple Maps App UIButtons, they look like this:
This is my Code:
class CustomView: UIImageView {
init(frame: CGRect, corners: CACornerMask, systemName: String) {
super.init(frame: frame)
self.createBorders(corners: corners)
self.createImage(systemName: systemName)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createBorders(corners: CACornerMask) {
self.contentMode = .scaleAspectFill
self.clipsToBounds = false
self.layer.cornerRadius = 10
self.layer.maskedCorners = corners
self.layer.masksToBounds = false
self.layer.shadowOffset = .zero
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowRadius = 10
self.layer.shadowOpacity = 0.2
self.backgroundColor = .white
let shadowAmount: CGFloat = 2
let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
self.layer.shadowPath = UIBezierPath(rect: rect).cgPath
}
func createImage(systemName: String) {
let image = UIImage(systemName: systemName)!
// let renderer = UIGraphicsImageRenderer(bounds: self.frame)
// let renderedImage = renderer.image { (_) in
// image.draw(in: frame.insetBy(dx: 16, dy: 30))
// }
// self.image = renderedImage.withRenderingMode(.alwaysTemplate)
self.image = image
}
}
And used it like this:
let size = CGSize(width: 10, height: 10)
let frame = CGRect(origin: CGPoint(x: 360, y: 45), size: size)
let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
let locationView = CustomView(frame: frame, corners: [], systemName: "location")
let plusView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "plus.circle")
let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")
let stackView = UIStackView(arrangedSubviews: [infoView, locationView, plusView, binocularsView])
stackView.frame = CGRect(origin: CGPoint(x: 360, y: 45), size: CGSize(width: 45, height: 180))
stackView.distribution = .fillEqually
stackView.axis = .vertical
stackView.setCustomSpacing(3, after: plusView)
view.addSubview(stackView)
With this code, it looks like this:
As you can see, the icons are too big... So this is what I tried to shrink them: (uncommented the lines in createImage())
func createImage(systemName: String) {
let image = UIImage(systemName: systemName)!
let renderer = UIGraphicsImageRenderer(bounds: self.frame)
let renderedImage = renderer.image { (_) in
image.draw(in: frame.insetBy(dx: 16, dy: 30))
}
self.image = renderedImage.withRenderingMode(.alwaysTemplate)
}
But then its distorted...
Does can help me? :)
Thank you!!!!
Rather than subclassing UIImageView subclass UIView and add a UIImageView as a subview. Then you can create as much spacing around the image as you want.
class CustomView: UIView {
lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
init(frame: CGRect, corners: CACornerMask, systemName: String) {
super.init(frame: frame)
self.setUpViews()
// ...
}
func setUpViews() {
addSubview(imageView)
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5),
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5),
imageView.topAnchor.constraint(equalTo: topAnchor, constant: 5),
imageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5),
])
}
func createImage(systemName: String) {
let image = UIImage(systemName: systemName)!
self.imageView.image = image
}
}

Image in the UIImageView is clipped when width is device width & height is original image's height

I created an ImageViewController, that is used to view long images. (like comics app)
but, as it is UIImageView's width is device-width, height is original size(aspect ratio)
But the image is cropped. How do I do it?
UI Code
fileprivate let scrollView = UIScrollView().then {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .yellow
$0.bounces = false
}
fileprivate let stackView = UIStackView().then {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.scrollView)
self.scrollView.addSubview(self.stackView)
self.scrollView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
self.stackView.snp.makeConstraints { make in
make.edges.equalTo(self.scrollView)
}
}
Add UIImageView code
for element in lists {
let imgView = UIImageView()
imgView.setImage(element) // set image from url
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.translatesAutoresizingMaskIntoConstraints = false
self.stackView.addArrangedSubview(imgView)
imgView.widthAnchor.constraint(equalTo: self.scrollView.widthAnchor, multiplier: 1.0).isActive = true
imgView.heightAnchor.constraint(equalTo: self.scrollView.heightAnchor, multiplier: 1.0).isActive = true
}
How to aspect ratio(max width) UIImageView?
I found solution!
added below code,
func imageScaled(with sourceImage: UIImage?, scaledToWidth i_width: Float) -> UIImage? {
let oldWidth = Float(sourceImage?.size.width ?? 0.0)
let scaleFactor: Float = i_width / oldWidth
let newHeight = Float((sourceImage?.size.height ?? 0.0) * CGFloat(scaleFactor))
let newWidth: Float = oldWidth * scaleFactor
print(newHeight)
UIGraphicsBeginImageContext(CGSize(width: CGFloat(newWidth), height: CGFloat(newHeight)))
sourceImage?.draw(in: CGRect(x: 0, y: 0, width: CGFloat(newWidth), height: CGFloat(newHeight)))
let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
then, set to UIImageView.
imgView.setImageScaled(element, width: Float(self.scrollView.frame.width))
as your image is bigger in height then the iphone's display height you have to put your image view in the scrollView and set content mode to .scaleAspectFit and set imageView's height equal to image's height
imgView.contentMode = .scaleAspectFit

Swift:Unable to center ImageView on ScrollView

I am attempting to display an imageView at the center in the X and Y axis onto a scrollView, fitted nicely within the width of the device when I run the app. I would also want to pan and zoom the imageView. I have tried almost all the solution that has been posted on SO and also followed this tutorial but somehow it just doesn't resolve my issue. The image is generated from a PDF.
I do my layout programmatically. My implementation so far:
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.backgroundColor = .lightGray
return sv
}()
let imageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
view.addSubview(scrollView)
scrollView.addSubview(imageView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10),
imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10),
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10),
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -10),
])
imageView.image = obtainThumbnail()
}
fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {
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
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateMinZoomScaleForSize(view.bounds.size)
}
func obtainThumbnail() -> UIImage? {
guard let url = Bundle.main.url(forResource: "drawings", withExtension: "pdf") else {return nil}
guard
let data = try? Data(contentsOf: url),
let page = PDFDocument(data: data)?.page(at: 0) else {
return nil
}
let pageSize = page.bounds(for: .mediaBox)
return page.thumbnail(of: CGSize(width: pageSize.width, height: pageSize.height), for: .mediaBox)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
This is my desired results when I run the app.
And this is the results when I run the app now, overstretched.
Would anyone advice what am I missing?
I think that views are loaded lazily in UIViewControllers so making the calculation later in the UI lifecycle should solve the problem. Removing the call in viewWillLayoutSubviews and instead using:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateMinZoomScaleForSize(view.bounds.size)
}
seems to work.
To make it work well, there are two places to fix:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
view.addSubview(scrollView)
scrollView.addSubview(imageView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
//First place.
// scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
scrollView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10),
imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10),
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10),
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -10),
])
imageView.backgroundColor = UIColor.red
imageView.image = UIImage.init(named: "temp2.png")
//second place.
imageView.sizeToFit()
Hope it solved your problem well.
If you need the view in the middle, may try the following method:
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.backgroundColor = .lightGray
sv.contentMode = .center
return sv
}()
let imageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = true
iv.contentMode = .scaleAspectFill
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
view.addSubview(scrollView)
scrollView.addSubview(imageView)
NSLayoutConstraint.activate([
scrollView.centerYAnchor.constraint(equalTo: view.centerYAnchor ),
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor ),
scrollView.contentLayoutGuide.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
scrollView.contentLayoutGuide.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
])
imageView.backgroundColor = UIColor.red
imageView.image = UIImage.init(named: "temp2.png")
imageView.sizeToFit()
updateMinZoomScaleForSize(view.bounds.size)
}
var constantHeight : CGFloat{
return min(view.bounds.height, imageView.bounds.height * scrollView.zoomScale)
}
var constantWidth : CGFloat{
return min( view.bounds.width, imageView.bounds.width * scrollView.zoomScale)
}
lazy var constraintHeight: NSLayoutConstraint = {
return scrollView.contentLayoutGuide.heightAnchor.constraint(equalToConstant: constantHeight)
}()
lazy var constraintWeight: NSLayoutConstraint = {
return
scrollView.contentLayoutGuide.widthAnchor.constraint(equalToConstant: constantWidth)
}()
fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {
let widthScale = size.width / imageView.bounds.width
let heightScale = size.height / imageView.bounds.height
let minScale = min(widthScale, heightScale)
imageView.frame = CGRect.init(x: 0, y: 0, width: minScale * imageView.bounds.width, height: minScale*imageView.bounds.height)
scrollView.maximumZoomScale = 10.0
scrollView.zoomScale = 1.0
constraintHeight.isActive = true
constraintWeight.isActive = true
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat){
constraintHeight.constant = constantHeight
constraintWeight.constant = constantWidth
scrollView.clipsToBounds = true
}
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
scrollView.clipsToBounds = false
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}