这个问题可以分为两个:
- 如何在屏幕上使用平移手势实现捏缩放和拖动
imageView
- 如何呈现一个视图控制器,其子视图之一(在 vc2 中)与呈现视图控制器中
imageView
的子视图(在 vc1 中)相同imageView
捏合手势缩放:捏合缩放更容易实现,UIScrollView
因为它支持开箱即用,无需添加手势识别器。创建一个scrollView
并添加您想要缩放的视图作为其子视图 ( scrollView.addSubview(imageView)
)。不要忘记也添加scrollView
自身 ( view.addSubview(scrollView)
)。
配置scrollView's
最小和最大缩放比例:scrollView.minimumZoomScale, scrollView.maximumZoomScale
. 设置一个委托scrollView.delegate
并实现UIScrollViewDelegate
:
func viewForZooming(in scrollView: UIScrollView) -> UIView?
在这种情况下应该返回你imageView
的,
还符合UIGestureRecognizerDelegate
并执行:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
哪个应该返回true。这是允许我们将平移手势识别器与内部捏合手势识别器一起使用的关键。
平移手势拖动:只需创建一个带有目标的平移手势识别器并将其添加到您的滚动视图scrollView.addGestureRecognizer(pan)
中。
处理手势:捏缩放在这个阶段工作得很好,除了你想在捏结束时显示第二个视图控制器。实现UIScrollViewDelegate
缩放结束时通知的另一种方法:
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
并调用您提供详细视图控制器的方法presentDetail()
,我们稍后会实现它。
下一步是处理平移手势,我将让代码自行解释:
// NOTE: Do NOT set from anywhere else than pan handler.
private var initialTouchPositionY: CGFloat = 0
private var initialTouchPositionX: CGFloat = 0
@objc func panned(_ pan: UIPanGestureRecognizer) {
let y = pan.location(in: scrollView).y
let x = pan.location(in: scrollView).x
switch pan.state {
case .began:
initialTouchPositionY = pan.location(in: imageView).y
initialTouchPositionX = pan.location(in: imageView).x
case .changed:
let offsetY = y - initialTouchPositionY
let offsetX = x - initialTouchPositionX
imageView.frame.origin = CGPoint(x: offsetX, y: offsetY)
case .ended:
presentDetail()
default: break
}
}
实现imageView
跟随平移位置移动,并presentDetail()
在手势结束时调用。
在我们实现之前presentDetail()
,前往详细视图控制器并添加要保存imageViewFrame
的属性和image
自身。现在在vc1中,我们这样实现presentDetail()
:
private func presentDetail() {
let frame = view.convert(imageView.frame, from: scrollView)
let detail = DetailViewController()
detail.imageViewFrame = frame
detail.image = imageView.image
// Note that we do not need the animation.
present(detail, animated: false, completion: nil)
}
在您的DetailViewController
中,确保imageViewFrame
在 eg 中设置和图像,viewDidLoad
然后您将被设置。
完整的工作示例:
class ViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate {
let imageView: UIImageView = UIImageView()
let scrollView: UIScrollView = UIScrollView()
lazy var pan: UIPanGestureRecognizer = {
return UIPanGestureRecognizer(target: self, action: #selector(panned(_:)))
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = // set your image
scrollView.delegate = self
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 10.0
scrollView.addSubview(imageView)
view.addSubview(scrollView)
scrollView.frame = view.frame
let w = view.bounds.width - 30 // padding of 15 on each side
imageView.frame = CGRect(x: 0, y: 0, width: w, height: w)
imageView.center = scrollView.center
scrollView.addGestureRecognizer(pan)
}
// NOTE: Do NOT set from anywhere else than pan handler.
private var initialTouchPositionY: CGFloat = 0
private var initialTouchPositionX: CGFloat = 0
@objc func panned(_ pan: UIPanGestureRecognizer) {
let y = pan.location(in: scrollView).y
let x = pan.location(in: scrollView).x
switch pan.state {
case .began:
initialTouchPositionY = pan.location(in: imageView).y
initialTouchPositionX = pan.location(in: imageView).x
case .changed:
let offsetY = y - initialTouchPositionY
let offsetX = x - initialTouchPositionX
imageView.frame.origin = CGPoint(x: offsetX, y: offsetY)
case .ended:
presentDetail()
default: break
}
}
// MARK: UIScrollViewDelegate
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
presentDetail()
}
// MARK: UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// MARK: Private
private func presentDetail() {
let frame = view.convert(imageView.frame, from: scrollView)
let detail = DetailViewController()
detail.imageViewFrame = frame
detail.image = imageView.image
present(detail, animated: false, completion: nil)
}
}
class DetailViewController: UIViewController {
let imageView: UIImageView = UIImageView()
var imageViewFrame: CGRect!
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
imageView.frame = imageViewFrame
imageView.image = image
view.addSubview(imageView)
view.addSubview(backButton)
}
lazy var backButton: UIButton = {
let button: UIButton = UIButton(frame: CGRect(x: 10, y: 30, width: 60, height: 30))
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("back", for: .normal)
return button
}()
@objc func back(_ sender: UIButton) {
dismiss(animated: false, completion: nil)
}
}