我有两个viewControllers:
ViewController1
一组复杂的子视图控制器,其中间位于imageView
ViewController2
其中嵌入了imageView的scrollView
我想要实现的是两个viewControllers之间的转换,它通过从viewController 1捏住imageView来触发,导致它放大并切换到viewController 2.当转换结束时,imageView应该只要在捏合手势触发过渡期间被放大,就会被放大。
同时我想支持在执行缩放转换时平移图像,这样就像缩放一样,处于结束状态的图像将被转换到它已被平移到的位置。
到目前为止,我已经尝试了Hero过渡窗格以及我自己编写的自定义viewController过渡。英雄过渡的问题是图像没有在第二个viewController中正确地捕捉到结束状态。我在自定义viewController转换时遇到的问题是我无法同时进行缩放和平移。
有没有人知道如何在Swift中实现它?非常感谢帮助。
答案 0 :(得分:4)
问题可以分为两个:
imageView
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
和图片,例如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)
}
}
答案 1 :(得分:3)
似乎UIView.animate(withDuration: animations: completion:)
可以帮到你;例如,在animations
块中,您可以设置新的图像帧,并在completion:
- 呈现第二个视图控制器(无动画);