I have a UIImageView inside a scrollView. Its initial width and height are equal to 100. I want this UIImageView to appear in same size even after zooming. For that I modify its size inside scrollViewDidEndZooming method. This is working fine without a problem. However UiImage of the UiImageView looks blurry when zoom scale is increased. I tried to set contentScale property of the UiImageView, but it is not working.
How I can fix this?
import UIKit
class ViewController: UIViewController {
static var zoomScale: CGFloat = 1.0
let scrollView = UIScrollView()
let contentView = UIView()
let imageView: UIImageView = UIImageView()
let imageViewSize: CGFloat = 100
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
self.setupScrollView()
self.setuContentView()
self.addImageView()
}
func setupScrollView() {
let vWidth = self.view.frame.width
let vHeight = self.view.frame.height
scrollView.delegate = self
scrollView.frame = CGRect(x: 0, y: 0, width: vWidth, height: vHeight)
scrollView.backgroundColor = .lightGray
scrollView.alwaysBounceVertical = false
scrollView.alwaysBounceHorizontal = false
scrollView.showsVerticalScrollIndicator = true
scrollView.flashScrollIndicators()
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 10.0
self.view.addSubview(scrollView)
}
func setuContentView() {
contentView.frame = scrollView.frame.insetBy(dx: 20, dy: 20)
contentView.backgroundColor = .white
scrollView.addSubview(contentView)
}
func addImageView(){
let image = UIImage(systemName: "arrow.triangle.2.circlepath")
imageView.frame = CGRect(x: 200, y: 200, width: imageViewSize, height: imageViewSize)
imageView.image = image
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .systemBlue
imageView.tintColor = .white
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 0.5 * imageView.bounds.size.width // I want rounded imageView
self.contentView.addSubview(imageView)
}
}
extension ViewController: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.contentView
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
ViewController.zoomScale = scale
imageView.frame = CGRect(x: imageView.frame.origin.x, y: imageView.frame.origin.y, width: imageViewSize/scale, height: imageViewSize/scale)
imageView.layer.cornerRadius = 0.5 * imageView.bounds.size.width
imageView.layer.contentsScale = scale
}
}
Related
In my iOS app, I have a scroll view which has the image view to scroll and zoom images.
What I am unable to do is launch the image view with pre-defined offset and zoom value.
Right now, on launch, the original image is shown which I can zoom-in/out and scroll, but I wanted to zoom-in/out with an offset with default values.
On setting the scrollview.zoomScale, I am not able to scroll the image completely.
Code below:-
let scroller = UIScrollView()
scroller.translatesAutoresizingMaskIntoConstraints = false
scroller.minimumZoomScale = 1
scroller.maximumZoomScale = 5
return scroller
}()
let imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
The first step:
scrollView = UIScrollView(frame: view.bounds)
view.addSubview(scrollView)
imageView.center = view.center
scrollView.addSubview(imageView)
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 2.0
scrollView.delegate = self
Step 2: implement the UIScrollViewDelegate method
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
Finally:
private func setImagePosition(_ image: UIImage) {
imageView.image = image
let size = imageSize(WithScreen: image)
imageView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
scrollView?.contentSize = size
if let scrollView = scrollView, size.height < scrollView.bounds.size.height {
let offsetY = (scrollView.bounds.size.height - size.height) * 0.5
scrollView.contentInset = UIEdgeInsets(top: offsetY, left: 0, bottom: offsetY, right: 0)
}
}
private func imageSize(WithScreen image: UIImage) -> CGSize {
var size = UIScreen.main.bounds.size
size.height = image.size.height * size.width / image.size.width
return size
}
I have an UIImageView object in UIScrollView(to zoom). In addition, UIImageView also contains subviews. I am using following function to zoom in UIImageView.
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.imageView
}
It works perfectly fine for UIImageView.
The problem is when zooming these subviews, it also zooms these subviews the same way. Is there a way to disable zooming for these subviews?
There are many ways to achieve this. I would say the easiest one is to simply back-scale your views. This is what I used as a demo:
class ViewController: UIViewController {
#IBOutlet private var scrollView: UIScrollView?
private var imageView: UIImageView?
private var annotations: [UIView] = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(image: UIImage(named: "background"))
scrollView?.addSubview(imageView)
scrollView?.contentSize = imageView.bounds.size
scrollView?.maximumZoomScale = 5.0
self.imageView = imageView
scrollView?.delegate = self
applyAnnotationView({
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0))
view.backgroundColor = UIColor.red
view.layer.cornerRadius = view.bounds.width*0.5
return view
}(), at: CGPoint(x: 100.0, y: 100.0))
applyAnnotationView({
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0))
view.backgroundColor = UIColor.green
view.layer.cornerRadius = view.bounds.width*0.5
return view
}(), at: CGPoint(x: 200.0, y: 200.0))
}
func applyAnnotationView(_ view: UIView, at position: CGPoint) {
view.center = position
annotations.append(view)
imageView?.addSubview(view)
}
}
// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
func scrollViewDidZoom(_ scrollView: UIScrollView) {
annotations.forEach { item in
item.transform = CGAffineTransform(scaleX: 1.0/scrollView.zoomScale, y: 1.0/scrollView.zoomScale)
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
}
Simply applying CGAffineTransform(scaleX: 1.0/scrollView.zoomScale, y: 1.0/scrollView.zoomScale) does all the work. It should be OK even when using constraints.
The UIView, which is inside the UIImageView, will always be the same size, but will be anchored to a specific point in the image (10% at the top and 10% at the left):
var item: UIView?
override func viewDidLoad() {
...
item = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
setPosition()
item?.backgroundColor = UIColor.red
imageView.addSubview(item!)
...
}
func setPosition() {
let x = imageView.frame.size.width/10
let y = imageView.frame.size.height/10
item?.frame = CGRect(x: x, y: y, width: 200, height: 200)
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
...
setPosition()
...
}
Instead of 10%, you can use the exact figure, using the formula:
x = startPosition/startWidthImage*currentWidthImage
If I pinch the simulator, it does not work! I want to zoom in and out )=
Here is my Code:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
let onView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.contentSize.width = 10000
scrollView.contentSize.height = 10000
onView.frame = CGRect(x: 0, y: 0, width: scrollView.frame.width, height: scrollView.frame.height)
scrollView.addSubview(onView)
for _ in 1...1000{
let x = arc4random_uniform(10000)
let y = arc4random_uniform(10000)
let btn = UIButton(frame: CGRect(x: Int(x), y: Int(y), width: Int(150), height: Int(100)))
btn.backgroundColor = .red
self.board.addSubview(btn)
}
scrollView.minimumZoomScale = 0.5
scrollView.maximumZoomScale = 10.0
scrollView.zoomScale = 1.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return scrollView
}
}
I have a bunch of CAShapeLayers built with UIBezierPath and stored inside of UIScrollView.
I wonder if there any way to keep the lineWidth of CAShapeLayer's path away from scaling when UIScrollView is zoomed over 100%. I mean applying the UIScrollView's transformation rules to CAShapeLayer, but keep the path's widh the same as it used to be on 1.0 zoomScale. (So when the layer with 1pt path lineWidth will be zoomed to 300%, the lineWidth of the layer on #2x device won't become 6px height, but will keep the height of 2px)
class ViewController : UIViewController, UIScrollViewDelegate {
private lazy var scrollView = UIScrollView()
private lazy var contentView = UIView()
private lazy var shapeLayer = CAShapeLayer()
private let lineWidth: CGFloat = 1
override func loadView() {
view = UIView()
view.addSubview(scrollView)
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
scrollView.addSubview(contentView)
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 2.0
scrollView.delegate = self
contentView.backgroundColor = .red
let viewSize = CGSize(width: 400, height: 400)
scrollView.contentSize = viewSize
contentView.frame = CGRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)
setUpShapeLayer(in: viewSize)
}
//MARK: - UIScrollViewDelegate
func scrollViewDidZoom(_ scrollView: UIScrollView) {
shapeLayer.lineWidth = lineWidth * 1 / scrollView.zoomScale
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return contentView
}
//MARK: - Private
private func setUpShapeLayer(in size: CGSize) {
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = lineWidth
contentView.layer.addSublayer(shapeLayer)
let shapeSize = CGSize(width: 200, height: 200)
let shapeBounds = CGRect(origin: .zero, size: shapeSize)
shapeLayer.bounds.size = shapeSize
shapeLayer.path = UIBezierPath(ovalIn: shapeBounds).cgPath
shapeLayer.position = CGPoint(x: size.width / 2, y: size.height / 2)
}
}
Is it what you want ?
I have a UIScrollView with paging enabled. All works good but sometimes when I scroll to the next image, 1 pixel from the last image is still displayed.
as you can see on the very left of the image, there is 1 pixel vertical line that is from the image before it. Meaning that the image was not swiped completely.
I tried a lot but couldn't find the problem.
Here is my code:
import Foundation
import UIKit
import SwiftyGif
class MBSPagingScrollView: UIScrollView, UIScrollViewDelegate {
var scrollView = UIScrollView()
var pageControl = UIPageControl()
var imagesArray = [String]()
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
init(frame : CGRect, imagesarray : [String]){
super.init(frame : frame)
imagesArray = imagesarray
scrollView.delegate = self
scrollView.isPagingEnabled = true
scrollView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
scrollView.showsHorizontalScrollIndicator = false
self.addSubview(scrollView)
pageControl.frame = CGRect(x: 0, y: self.frame.size.height - 50, width: self.frame.size.width, height: 50)
pageControl.numberOfPages = imagesArray.count
pageControl.currentPage = 0
pageControl.tintColor = UIColor.red
pageControl.pageIndicatorTintColor = UIColor.black
pageControl.currentPageIndicatorTintColor = MainOrangeColor
self.addSubview(pageControl)
scrollView.contentSize = CGSize(width:scrollView.frame.size.width * CGFloat(imagesArray.count),height: scrollView.frame.size.height)
pageControl.addTarget(self, action: #selector(changePage(sender:)), for: UIControlEvents.valueChanged)
for (index, imageName) in imagesArray.enumerated() {
var imageView = UIImageView()
if imageName.hasSuffix(".gif") {
let gifManager = SwiftyGifManager(memoryLimit:30)
let gif = UIImage(gifName: imageName)
imageView = UIImageView(gifImage: gif, manager: gifManager)
}
else {
imageView = UIImageView(image: UIImage(named: imageName))
imageView.contentMode = UIViewContentMode.scaleToFill
}
imageView.frame = CGRect(x: CGFloat(index) * self.frame.size.width, y: 0, width: self.frame.size.width, height: self.frame.size.height)
scrollView.addSubview(imageView)
}
}
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x:x, y:0), animated: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
Any help is much appreciated.
Thanks a lot!
Try and set clipsToBounds = true for all the imageviews.