为按钮的凹凸效果制作动画的简单而常规的方法,但是在SwiftUI中却不简单。
我试图改变scale的tapGesture改性剂,但它不会有任何效果。我不知道如何制作动画链,可能是因为SwiftUI没有动画。所以我的天真做法是:
@State private var scaleValue = CGFloat(1)
...
Button(action: {
withAnimation {
self.scaleValue = 1.5
}
withAnimation {
self.scaleValue = 1.0
}
}) {
Image("button1")
.scaleEffect(self.scaleValue)
}
Run Code Online (Sandbox Code Playgroud)
显然,它不起作用,按钮图像会立即获得上一个比例值。
我的第二个想法是0.8在hold事件release发生时将标度更改为值,然后在事件发生后将标度1.2更改为,并在几毫秒后再次将标度更改为1.0。我猜想这种算法应该会产生更好的自然碰撞效果。但是我gesture在SwiftUI中找不到合适的结构来处理hold-n-release事件。
PS为了便于理解,我将描述hold-n-release算法的步骤:
1.00.81.20.1秒1.0UPD:我发现了使用动画delay修改器的简单解决方案。但是我不确定这是正确和明确的。而且它不涵盖hold-n-release问题:
@State private var scaleValue = CGFloat(1)
...
Button(action: {
withAnimation {
self.scaleValue = 1.5
}
//
// Using delay for second animation block
//
withAnimation(Animation.linear.delay(0.2)) {
self.scaleValue = 1.0
}
}) {
Image("button1")
.scaleEffect(self.scaleValue)
}
Run Code Online (Sandbox Code Playgroud)
UPD 2:我在溶液中发现它上面什么并不重要价值我传递的参数delay修改:0.2或1000将有同样的效果。也许是个错误
所以我用Timer实例代替了delay动画修改器。现在它按预期工作:
...
Button(action: {
withAnimation {
self.scaleValue = 1.5
}
//
// Replace it
//
// withAnimation(Animation.linear.delay(0.2)) {
// self.scaleValue = 1.0
// }
//
// by Timer with 0.5 msec delay
//
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
withAnimation {
self.scaleValue = 1.0
}
}
}) {
...
Run Code Online (Sandbox Code Playgroud)
UPD 3:直到我们等待苹果官方更新,实现两个事件的合适的解决方案之一touchStart,并touchEnd基于@average乔回答:
import SwiftUI
struct TouchGestureViewModifier: ViewModifier {
let minimumDistance: CGFloat
let touchBegan: () -> Void
let touchEnd: (Bool) -> Void
@State private var hasBegun = false
@State private var hasEnded = false
init(minimumDistance: CGFloat, touchBegan: @escaping () -> Void, touchEnd: @escaping (Bool) -> Void) {
self.minimumDistance = minimumDistance
self.touchBegan = touchBegan
self.touchEnd = touchEnd
}
private func isTooFar(_ translation: CGSize) -> Bool {
let distance = sqrt(pow(translation.width, 2) + pow(translation.height, 2))
return distance >= minimumDistance
}
func body(content: Content) -> some View {
content.gesture(DragGesture(minimumDistance: 0)
.onChanged { event in
guard !self.hasEnded else { return }
if self.hasBegun == false {
self.hasBegun = true
self.touchBegan()
} else if self.isTooFar(event.translation) {
self.hasEnded = true
self.touchEnd(false)
}
}
.onEnded { event in
if !self.hasEnded {
let success = !self.isTooFar(event.translation)
self.touchEnd(success)
}
self.hasBegun = false
self.hasEnded = false
}
)
}
}
extension View {
func onTouchGesture(minimumDistance: CGFloat = 20.0,
touchBegan: @escaping () -> Void,
touchEnd: @escaping (Bool) -> Void) -> some View {
modifier(TouchGestureViewModifier(minimumDistance: minimumDistance, touchBegan: touchBegan, touchEnd: touchEnd))
}
}
Run Code Online (Sandbox Code Playgroud)
struct ScaleButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 2 : 1)
}
}
struct Test2View: View {
var body: some View {
Button(action: {}) {
Image("button1")
}.buttonStyle(ScaleButtonStyle())
}
}
Run Code Online (Sandbox Code Playgroud)
升级 iOS 15 的答案代码(但这也适用于 iOS 13)。animation() 修饰符的单参数形式现已被正式弃用,主要是因为它导致了各种意外行为(例如在惰性堆栈中:lazy(v/h)scroll、lazyvgrid):按钮意外动画(跳跃)滚动期间。
public struct ScaleButtonStyle: ButtonStyle {
public init() {}
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.animation(.linear(duration: 0.2), value: configuration.isPressed)
.brightness(configuration.isPressed ? -0.05 : 0)
}
}
public extension ButtonStyle where Self == ScaleButtonStyle {
static var scale: ScaleButtonStyle {
ScaleButtonStyle()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
339 次 |
| 最近记录: |