gpt4 book ai didi

ios - 一种使用 UIPinchGestureRecognizer UIRotationGestureRecognizer 和 UIPanGestureRecognizer 添加 Instagram 类布局指南的方法?

转载 作者:IT王子 更新时间:2023-10-29 05:44:16 27 4
gpt4 key购买 nike

我用UIPinchGestureRecognizer UIPanGestureRecognizer & UIRotationGestureRecognizerUILabel 来实现Instagram像缩放和拖动功能。现在我想显示布局指南,就像将 UILabel 拖到中心时它应该显示布局指南,如下例所示。当您旋转 UILabel 时,它还应该显示布局指南。

实现此功能的最佳且准确的方法是什么?

这是我已经拥有的

(图片取自 this question by @Skiddswarmik )

这是我用于简单拖动和缩放功能的代码(取自 this answer by @lbsweek)

SnapGesture Class

import UIKit

/*
usage:

add gesture:
yourObjToStoreMe.snapGesture = SnapGesture(view: your_view)
remove gesture:
yourObjToStoreMe.snapGesture = nil
disable gesture:
yourObjToStoreMe.snapGesture.isGestureEnabled = false
advanced usage:
view to receive gesture(usually superview) is different from view to be transformed,
thus you can zoom the view even if it is too small to be touched.
yourObjToStoreMe.snapGesture = SnapGesture(transformView: your_view_to_transform, gestureView: your_view_to_recieve_gesture)

*/

class SnapGesture: NSObject, UIGestureRecognizerDelegate {

// MARK: - init and deinit
convenience init(view: UIView) {
self.init(transformView: view, gestureView: view)
}
init(transformView: UIView, gestureView: UIView) {
super.init()

self.addGestures(v: gestureView)
self.weakTransformView = transformView
}
deinit {
self.cleanGesture()
}

// MARK: - private method
private weak var weakGestureView: UIView?
private weak var weakTransformView: UIView?

private var panGesture: UIPanGestureRecognizer?
private var pinchGesture: UIPinchGestureRecognizer?
private var rotationGesture: UIRotationGestureRecognizer?

private func addGestures(v: UIView) {

panGesture = UIPanGestureRecognizer(target: self, action: #selector(panProcess(_:)))
v.isUserInteractionEnabled = true
panGesture?.delegate = self // for simultaneous recog
v.addGestureRecognizer(panGesture!)

pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinchProcess(_:)))
//view.isUserInteractionEnabled = true
pinchGesture?.delegate = self // for simultaneous recog
v.addGestureRecognizer(pinchGesture!)

rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotationProcess(_:)))
rotationGesture?.delegate = self
v.addGestureRecognizer(rotationGesture!)

self.weakGestureView = v
}

private func cleanGesture() {
if let view = self.weakGestureView {
//for recognizer in view.gestureRecognizers ?? [] {
// view.removeGestureRecognizer(recognizer)
//}
if panGesture != nil {
view.removeGestureRecognizer(panGesture!)
panGesture = nil
}
if pinchGesture != nil {
view.removeGestureRecognizer(pinchGesture!)
pinchGesture = nil
}
if rotationGesture != nil {
view.removeGestureRecognizer(rotationGesture!)
rotationGesture = nil
}
}
self.weakGestureView = nil
self.weakTransformView = nil
}




// MARK: - API

private func setView(view:UIView?) {
self.setTransformView(view, gestgureView: view)
}

private func setTransformView(_ transformView: UIView?, gestgureView:UIView?) {
self.cleanGesture()

if let v = gestgureView {
self.addGestures(v: v)
}
self.weakTransformView = transformView
}

open func resetViewPosition() {
UIView.animate(withDuration: 0.4) {
self.weakTransformView?.transform = CGAffineTransform.identity
}
}

open var isGestureEnabled = true

// MARK: - gesture handle

// location will jump when finger number change
private var initPanFingerNumber:Int = 1
private var isPanFingerNumberChangedInThisSession = false
private var lastPanPoint:CGPoint = CGPoint(x: 0, y: 0)
@objc func panProcess(_ recognizer:UIPanGestureRecognizer) {
if isGestureEnabled {
//guard let view = recognizer.view else { return }
guard let view = self.weakTransformView else { return }

// init
if recognizer.state == .began {
lastPanPoint = recognizer.location(in: view)
initPanFingerNumber = recognizer.numberOfTouches
isPanFingerNumberChangedInThisSession = false
}

// judge valid
if recognizer.numberOfTouches != initPanFingerNumber {
isPanFingerNumberChangedInThisSession = true
}
if isPanFingerNumberChangedInThisSession {
return
}

// perform change
let point = recognizer.location(in: view)
view.transform = view.transform.translatedBy(x: point.x - lastPanPoint.x, y: point.y - lastPanPoint.y)
lastPanPoint = recognizer.location(in: view)
}
}



private var lastScale:CGFloat = 1.0
private var lastPinchPoint:CGPoint = CGPoint(x: 0, y: 0)
@objc func pinchProcess(_ recognizer:UIPinchGestureRecognizer) {
if isGestureEnabled {
guard let view = self.weakTransformView else { return }

// init
if recognizer.state == .began {
lastScale = 1.0;
lastPinchPoint = recognizer.location(in: view)
}

// judge valid
if recognizer.numberOfTouches < 2 {
lastPinchPoint = recognizer.location(in: view)
return
}

// Scale
let scale = 1.0 - (lastScale - recognizer.scale);
view.transform = view.transform.scaledBy(x: scale, y: scale)
lastScale = recognizer.scale;

// Translate
let point = recognizer.location(in: view)
view.transform = view.transform.translatedBy(x: point.x - lastPinchPoint.x, y: point.y - lastPinchPoint.y)
lastPinchPoint = recognizer.location(in: view)
}
}


@objc func rotationProcess(_ recognizer: UIRotationGestureRecognizer) {
if isGestureEnabled {
guard let view = self.weakTransformView else { return }

view.transform = view.transform.rotated(by: recognizer.rotation)
recognizer.rotation = 0
}
}


//MARK:- UIGestureRecognizerDelegate Methods
func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
return true
}

}

Add Gesture in UILabel

// define 
var snapGesture: SnapGesture?

// add gesture
self.snapGesture = SnapGesture(view: self.myLabel!)

最佳答案

您将在下面找到您的类的更新版本,它应该按照您的描述进行操作。

大部分更新的代码位于接近尾声的最后一节(指南),但我已经稍微更新了您的 UIGestureRecognizer 操作以及您的主要 初始化方法。

特点:

- 用于水平居中 View 位置的垂直引用线。

- 用于将 View 的旋转居中于 0 度的水平引用线。

- 位置和旋转捕捉到具有公差值的引用线(snapToleranceDistancesnapToleranceAngle 属性)。

- 指南的动画外观/消失(animateGuidesguideAnimationDuration 属性)。

- 可以根据用例更改的指南 View (movementGuideViewrotationGuideView 属性)

class SnapGesture: NSObject, UIGestureRecognizerDelegate {

// MARK: - init and deinit
convenience init(view: UIView) {
self.init(transformView: view, gestureView: view)
}

init(transformView: UIView, gestureView: UIView) {
super.init()

self.addGestures(v: gestureView)
self.weakTransformView = transformView

guard let transformView = self.weakTransformView, let superview = transformView.superview else {
return
}

// This is required in order to be able to snap the view to center later on,
// using the `tx` property of its transform.
transformView.center = superview.center
}
deinit {
self.cleanGesture()
}

// MARK: - private method
private weak var weakGestureView: UIView?
private weak var weakTransformView: UIView?

private var panGesture: UIPanGestureRecognizer?
private var pinchGesture: UIPinchGestureRecognizer?
private var rotationGesture: UIRotationGestureRecognizer?

private func addGestures(v: UIView) {

panGesture = UIPanGestureRecognizer(target: self, action: #selector(panProcess(_:)))
v.isUserInteractionEnabled = true
panGesture?.delegate = self // for simultaneous recog
v.addGestureRecognizer(panGesture!)

pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinchProcess(_:)))
//view.isUserInteractionEnabled = true
pinchGesture?.delegate = self // for simultaneous recog
v.addGestureRecognizer(pinchGesture!)

rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotationProcess(_:)))
rotationGesture?.delegate = self
v.addGestureRecognizer(rotationGesture!)

self.weakGestureView = v
}

private func cleanGesture() {
if let view = self.weakGestureView {
//for recognizer in view.gestureRecognizers ?? [] {
// view.removeGestureRecognizer(recognizer)
//}
if panGesture != nil {
view.removeGestureRecognizer(panGesture!)
panGesture = nil
}
if pinchGesture != nil {
view.removeGestureRecognizer(pinchGesture!)
pinchGesture = nil
}
if rotationGesture != nil {
view.removeGestureRecognizer(rotationGesture!)
rotationGesture = nil
}
}
self.weakGestureView = nil
self.weakTransformView = nil
}

// MARK: - API

private func setView(view:UIView?) {
self.setTransformView(view, gestgureView: view)
}

private func setTransformView(_ transformView: UIView?, gestgureView:UIView?) {
self.cleanGesture()

if let v = gestgureView {
self.addGestures(v: v)
}
self.weakTransformView = transformView
}

open func resetViewPosition() {
UIView.animate(withDuration: 0.4) {
self.weakTransformView?.transform = CGAffineTransform.identity
}
}

open var isGestureEnabled = true

// MARK: - gesture handle

// location will jump when finger number change
private var initPanFingerNumber:Int = 1
private var isPanFingerNumberChangedInThisSession = false
private var lastPanPoint:CGPoint = CGPoint(x: 0, y: 0)
@objc func panProcess(_ recognizer:UIPanGestureRecognizer) {
guard isGestureEnabled, let view = self.weakTransformView else { return }

// init
if recognizer.state == .began {
lastPanPoint = recognizer.location(in: view)
initPanFingerNumber = recognizer.numberOfTouches
isPanFingerNumberChangedInThisSession = false
}

// judge valid
if recognizer.numberOfTouches != initPanFingerNumber {
isPanFingerNumberChangedInThisSession = true
}

if isPanFingerNumberChangedInThisSession {
hideGuidesOnGestureEnd(recognizer)
return
}

// perform change
let point = recognizer.location(in: view)
view.transform = view.transform.translatedBy(x: point.x - lastPanPoint.x, y: point.y - lastPanPoint.y)
lastPanPoint = recognizer.location(in: view)

updateMovementGuide()
hideGuidesOnGestureEnd(recognizer)
}

private var lastScale:CGFloat = 1.0
private var lastPinchPoint:CGPoint = CGPoint(x: 0, y: 0)
@objc func pinchProcess(_ recognizer:UIPinchGestureRecognizer) {
guard isGestureEnabled, let view = self.weakTransformView else { return }

// init
if recognizer.state == .began {
lastScale = 1.0;
lastPinchPoint = recognizer.location(in: view)
}

// judge valid
if recognizer.numberOfTouches < 2 {
lastPinchPoint = recognizer.location(in: view)
hideGuidesOnGestureEnd(recognizer)
return
}

// Scale
let scale = 1.0 - (lastScale - recognizer.scale);
view.transform = view.transform.scaledBy(x: scale, y: scale)
lastScale = recognizer.scale;

// Translate
let point = recognizer.location(in: view)
view.transform = view.transform.translatedBy(x: point.x - lastPinchPoint.x, y: point.y - lastPinchPoint.y)
lastPinchPoint = recognizer.location(in: view)

updateMovementGuide()
hideGuidesOnGestureEnd(recognizer)
}


@objc func rotationProcess(_ recognizer: UIRotationGestureRecognizer) {
guard isGestureEnabled, let view = self.weakTransformView else { return }

view.transform = view.transform.rotated(by: recognizer.rotation)
recognizer.rotation = 0
updateRotationGuide()
hideGuidesOnGestureEnd(recognizer)
}

func hideGuidesOnGestureEnd(_ recognizer: UIGestureRecognizer) {
if recognizer.state == .ended {
showMovementGuide(false)
showRotationGuide(false)
}
}

// MARK:- UIGestureRecognizerDelegate Methods
func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
return true
}

// MARK:- Guides

var animateGuides = true
var guideAnimationDuration: TimeInterval = 0.3

var snapToleranceDistance: CGFloat = 5 // pts
var snapToleranceAngle: CGFloat = 1 // degrees
* CGFloat.pi / 180 // (converted to radians)

var movementGuideView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.blue
return view
} ()

var rotationGuideView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.red
return view
} ()

// MARK: Movement guide and snap

func updateMovementGuide() {
guard let transformView = weakTransformView, let superview = transformView.superview else {
return
}

let transformX = transformView.frame.midX
let superX = superview.bounds.midX

if transformX - snapToleranceDistance < superX && transformX + snapToleranceDistance > superX {
transformView.transform.tx = 0
showMovementGuide(true)
} else {
showMovementGuide(false)
}

updateGuideFrames()
}

var isShowingMovementGuide = false

func showMovementGuide(_ shouldShow: Bool) {
guard isShowingMovementGuide != shouldShow,
let transformView = weakTransformView,
let superview = transformView.superview
else { return }

superview.insertSubview(movementGuideView, belowSubview: transformView)
movementGuideView.frame = CGRect(
x: superview.frame.midX,
y: 0,
width: 1,
height: superview.frame.size.height
)

let duration = animateGuides ? guideAnimationDuration : 0
isShowingMovementGuide = shouldShow
UIView.animate(withDuration: duration) { [weak self] in
self?.movementGuideView.alpha = shouldShow ? 1 : 0
}
}

// MARK: Rotation guide and snap

func updateRotationGuide() {
guard let transformView = weakTransformView else {
return
}

let angle = atan2(transformView.transform.b, transformView.transform.a)
if angle > -snapToleranceAngle && angle < snapToleranceAngle {
transformView.transform = transformView.transform.rotated(by: angle * -1)
showRotationGuide(true)
} else {
showRotationGuide(false)
}
}

var isShowingRotationGuide = false

func showRotationGuide(_ shouldShow: Bool) {
guard isShowingRotationGuide != shouldShow,
let transformView = weakTransformView,
let superview = transformView.superview
else { return }

superview.insertSubview(rotationGuideView, belowSubview: transformView)

let duration = animateGuides ? guideAnimationDuration : 0
isShowingRotationGuide = shouldShow
UIView.animate(withDuration: duration) { [weak self] in
self?.rotationGuideView.alpha = shouldShow ? 1 : 0
}
}

func updateGuideFrames() {
guard let transformView = weakTransformView,
let superview = transformView.superview
else { return }

rotationGuideView.frame = CGRect(
x: 0,
y: transformView.frame.midY,
width: superview.frame.size.width,
height: 1
)
}
}

对于任何感兴趣的人,这里有一个 test project使用这个类。

关于ios - 一种使用 UIPinchGestureRecognizer UIRotationGestureRecognizer 和 UIPanGestureRecognizer 添加 Instagram 类布局指南的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53967091/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com