@@ -52,23 +52,28 @@ class EdgeAnimationView: UIView {
5252 if edge == . right {
5353 NSLayoutConstraint . activate ( [
5454 iconView. centerYAnchor. constraint ( equalTo: centerYAnchor) ,
55- iconView. leadingAnchor . constraint ( equalTo: leadingAnchor , constant: 8 ) ,
55+ iconView. leftAnchor . constraint ( equalTo: leftAnchor , constant: 8 ) ,
5656 ] )
5757 } else {
5858 NSLayoutConstraint . activate ( [
5959 iconView. centerYAnchor. constraint ( equalTo: centerYAnchor) ,
60- iconView. trailingAnchor . constraint ( equalTo: trailingAnchor , constant: - 8 ) ,
60+ iconView. rightAnchor . constraint ( equalTo: rightAnchor , constant: - 8 ) ,
6161 ] )
6262 }
6363 }
6464
6565 override func layoutSubviews( ) {
6666 super. layoutSubviews ( )
67+
68+ drawShapeLayer ( )
69+ }
70+
71+ private func drawShapeLayer( ) {
72+
73+ if cachedPaths. isEmpty { return }
74+
6775 // 更新layer的路径
6876 var indexWidth = Int ( self . frame. width)
69- if indexWidth == 0 {
70- return
71- }
7277 if indexWidth >= cachedPaths. count{
7378 indexWidth = cachedPaths. count- 1
7479 }
@@ -89,75 +94,98 @@ class EdgeAnimationView: UIView {
8994
9095 @objc func handlePanGesture( _ sender: UIPanGestureRecognizer ) {
9196 let translation = sender. translation ( in: self . superview)
92-
97+
98+ switch sender. state {
99+ case . began, . changed:
100+ let gesPoint = sender. location ( in: sender. view)
101+ let newY = gesPoint. y - initFrame. height/ 2
102+ initFrame = CGRect ( x: initFrame. minX, y: newY, width: initFrame. width, height: initFrame. height)
103+ default :
104+ break
105+ }
93106 switch sender. state {
94107 case . began:
95108 // 如果有正在进行的动画,停止并完成它
96109 if let animator = animator, animator. isRunning {
97- animator. stopAnimation ( true )
98- animator. finishAnimation ( at: . current)
110+ stopAnimation ( )
99111 } else {
100112 //这是每次新触发的手势。 否则如果有动画在进行,二次拖拽动画则建立在前一个动画位置之上
101- let gesPoint = sender. location ( in: sender. view)
102- initFrame = CGRect ( x: initFrame. minX, y: gesPoint. y - initFrame. height/ 2 , width: initFrame. width, height: initFrame. height)
113+ self . frame = initFrame
103114 }
104- stopAnimation ( )
105115 originalWidth = self . bounds. width
106- initialOriginX = self . frame. origin. x // 记录初始的 x 坐标
107- self . frame = initFrame
116+ initialOriginX = translation . x // self.frame.origin.x // 记录初始的 x 坐标
117+ drawShapeLayer ( )
108118 case . changed:
109- // 根据手势的水平移动调整宽度
110- let newWidth = min ( originalWidth + abs ( translation. x) , CGFloat ( fullWidth) )
111- if edge == . left {
112- self . frame = CGRect ( x : self . frame . origin . x , y : self . frame . origin . y , width : newWidth , height : self . bounds . height )
113- } else {
114- let newX = initialOriginX - ( newWidth - originalWidth )
115- self . frame = CGRect ( x: newX, y: self . frame . origin . y , width: newWidth, height: self . bounds . height)
119+ if edge == . left {
120+ let newWidth = max ( 0 , min ( translation. x - initialOriginX + originalWidth , CGFloat ( fullWidth) ) )
121+ self . frame = CGRect ( x : initFrame . minX , y : initFrame . minY , width : newWidth , height : initFrame . height )
122+ } else {
123+ let newWidth = max ( 0 , min ( initialOriginX - translation . x + originalWidth , CGFloat ( fullWidth ) ) )
124+ let newX = initFrame . minX - newWidth
125+ self . frame = CGRect ( x: newX, y: initFrame . minY , width: newWidth, height: initFrame . height)
116126 }
127+ self . iconView. alpha = ( ( self . frame. width / CGFloat( fullWidth) ) > 0.7 ) ? 1.0 : 0.5
128+
117129 case . ended, . cancelled:
118- let offsetX = self . frame. width
119- if ( offsetX / CGFloat ( fullWidth ) ) > 0.7 {
130+ if ( self . frame. width / CGFloat ( fullWidth ) ) > 0.7 {
131+ // 执行用户动作
120132 triggerAction ( )
133+ // 轻微的震动
134+ let impactFeedbackGenerator = UIImpactFeedbackGenerator ( style: . light)
135+ impactFeedbackGenerator. prepare ( )
136+ impactFeedbackGenerator. impactOccurred ( )
121137 }
122- animateShapes ( )
123- animateBackToOriginalWidth ( )
138+ startAnimation ( )
124139 default :
125140 break
126141 }
127142 }
128143
129- private func animateBackToOriginalWidth( ) {
130- animator = UIViewPropertyAnimator ( duration: 0.6 , curve: . linear) {
144+ private func animateBackToOriginalWidth( _ duration: Double ) {
145+ let uianimator = UIViewPropertyAnimator (
146+ duration: duration,
147+ controlPoint1: CGPoint ( x: 0 , y: 0.95 ) ,
148+ controlPoint2: CGPoint ( x: 0.5 , y: 1 )
149+ ) {
131150 self . frame = self . initFrame
132151 }
152+ self . animator = uianimator
133153
134154 // 确保动画完成后才释放
135- animator ? . addCompletion { position in
136- if position == . end && self . animator == animator {
155+ uianimator . addCompletion { position in
156+ if position == . end && self . animator == uianimator {
137157 self . animator = nil
138158 }
139159 }
140160
141- animator ? . startAnimation ( )
161+ uianimator . startAnimation ( )
142162 }
143163
144- private func animateShapes( ) {
164+ private func animateShapes( _ duration : Double ) {
145165 let currentWidth = Int ( self . frame. width)
146166 let index = min ( currentWidth, cachedPaths. count - 1 )
147167 let subArray = Array ( cachedPaths [ 0 ... index] . reversed ( ) )
148168 let animation = CAKeyframeAnimation ( keyPath: " path " )
149169 animation. values = subArray
150- animation. duration = 0.6 // 动画持续时间
151- animation. timingFunction = CAMediaTimingFunction ( name: . linear)
170+ animation. duration = duration // 动画持续时间
171+ let timingFunction = CAMediaTimingFunction ( controlPoints: 0 , 0.95 , 0.5 , 1 )
172+ animation. timingFunction = timingFunction
152173 shapeLayer. add ( animation, forKey: " shapeAnimation " )
153174 }
154175
176+ func startAnimation( ) {
177+ let duration : Double = 0.6 ;
178+ animateShapes ( duration)
179+ animateBackToOriginalWidth ( duration)
180+ }
181+
155182 func stopAnimation( ) {
156- if let presentationLayer = shapeLayer. presentation ( ) ,
157- let currentPath = presentationLayer. path {
158- // 在停止动画时,将当前路径设置为 shapeLayer 的路径,避免一闪现象
159- shapeLayer. path = currentPath
160- }
183+ self . animator? . stopAnimation ( true )
184+ self . animator? . finishAnimation ( at: . current)
185+ // if let currentPath = shapeLayer.presentation()?.path {
186+ // // 在停止动画时,将当前路径设置为 shapeLayer 的路径,避免一闪现象
187+ // shapeLayer.path = currentPath
188+ // }
161189 // 二次拖拽时,需要停止前一次的回原动画,通过 key 来移除动画,这里使用添加动画时的 key
162190 shapeLayer. removeAnimation ( forKey: " shapeAnimation " )
163191 }
0 commit comments