I'm struggling with problem in rotating image in a UIScrollView, my view hierarchy is described by:
I've an UIScrollView with an UIView as subview. The UIView has an UIImageView as subview.
I would be able to rotate the imageView inside it and then recalculate the contentSize of the scrollView in order to be able to pan over all of it.
I'm using this code:
//
// TestView.swift
//
import UIKit
class TestView: UIView {
// MARK: - properties
private var imageView: UIImageView?
private var zoomingView: UIView?
private var scrollView: UIScrollView?
private let kDecayRotation = "kDecayRotation"
var image: UIImage? {
didSet {
setupImageView(image)
}
}
// MARK: - init
convenience init(frame: CGRect, image: UIImage) {
self.init(frame: frame)
setupImageView(image)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.blueColor()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - methods
private func setupImageView(image: UIImage?) {
guard let image = image else { return }
imageView = UIImageView(image: image)
zoomingView = UIView(frame: imageView!.frame)
zoomingView?.addSubview(imageView!)
scrollView = UIScrollView(frame: frame)
scrollView?.backgroundColor = UIColor.redColor()
scrollView?.contentSize = zoomingView!.frame.size
scrollView?.addSubview(zoomingView!)
addSubview(scrollView!)
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
rotationGesture.delegate = self
scrollView?.addGestureRecognizer(rotationGesture)
}
@objc private func handleRotation(gestureRecognizer: UIRotationGestureRecognizer) {
adjustAnchorPointForGestureRecognizer(gestureRecognizer)
guard let targetView = zoomingView else { return }
if gestureRecognizer.state == .Began { }
else if gestureRecognizer.state == .Changed {
targetView.transform = CGAffineTransformRotate(targetView.transform, gestureRecognizer.rotation)
gestureRecognizer.rotation = 0
scrollView?.contentSize = targetView.frame.size
var targetFrame = targetView.frame
let targetBounds = targetView.bounds
targetFrame.origin.x = 0
targetFrame.origin.y = 0
targetView.frame = targetFrame
targetView.bounds = targetBounds
scrollView?.setNeedsDisplay()
} else if gestureRecognizer.state == .Cancelled || gestureRecognizer.state == .Ended { }
}
// MARK: - gesture helper
private func adjustAnchorPointForGestureRecognizer(gestureRecognizer: UIGestureRecognizer) {
guard let zoomingView = zoomingView else { return }
if gestureRecognizer.state == .Began {
let locationInView = gestureRecognizer.locationInView(zoomingView)
let locationInSuperview = gestureRecognizer.locationInView(zoomingView.superview)
zoomingView.layer.anchorPoint = CGPointMake(locationInView.x / zoomingView.bounds.size.width, locationInView.y / zoomingView.bounds.size.height)
zoomingView.center = locationInSuperview
}
}
}
extension TestView: UIGestureRecognizerDelegate {
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
The problem are:
- The View doesn't rotate around the location of the finger (although I implemented the method to calculate the new anchorPoint)
- The contentSize if recalculated correctly (only if I comment adjustAnchorPointForGestureRecognizer(gestureRecognizer) in handleRotation(_:)), but the image move across the scrollview in an horrible way. How can I get the effect of the image remain fixed (and rotate) and the contentSize being recalculated under of that in a 'silence' manner?
Edit 1
The code to use the TestView is:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
let sampleImage = UIImage(named: "testImage")
let testView = TestView(frame: view.frame, image: sampleImage!)
view.addSubview(testView)
}
}