美文网首页
SwiftUI - Gesture

SwiftUI - Gesture

作者: 西西的一天 | 来源:发表于2020-01-11 17:25 被阅读0次

SwiftUI中的手势是通过modifier来实现了,采用声明式的API相比于UIKit中的实现更易于理解和维护。同时也简化了手势冲突的处理方式,并可通过内建API自定义复杂的手势操作。

本文将从简单的手势开始介绍,包括:

在此基础上,将介绍两个场景:

  • gesture修饰符的顺序对效果的影响
  • 如何使用simultaneousGesture来组合多个gesture

Tap

为了演示方便,准备了一个简单的View用于接收手势操作:

struct CardView: View {
    
    let selected: Bool
    
    var body: some View {
        VStack {
            Text("Card")
                .fontWeight(.bold)
                .foregroundColor(Color.white)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                .background(self.selected ? Color.purple : Color.orange)
                .cornerRadius(30)
        }
    }
}

在ContentView中使用这个CardView,并添加一个gesture修饰符,在其中传入一个TapGesture

struct ContentView: View {
    @State private var selected: Bool = false
    var body: some View {
        CardView(selected: self.selected)
            .gesture(TapGesture(count: 1)
                .onEnded {
                    self.selected.toggle()
                }
            )
    }
}

TapGesture可以说是所有手势中最简单的一个,它的构造函数中只需要传入一个count,代表点击几下来触发一个事件,若不传入参数,默认值为1。

LongPressGesture

类似于TapGesture,LongPressGesture的触发条件为按住超过一定的时间,这个事件由构造函数的两个参数决定,minimumDuration, maximumDistance。

struct ContentView: View {
    @State private var selected: Bool = false
    @State private var didLongPress: Bool = false
    
    var body: some View {
        CardView(selected: self.selected)
            .scaleEffect(didLongPress ? 1.2 : 1)
            .animation(.easeInOut)
            .frame(width: 200, height: 200, alignment: .center)
            .gesture(LongPressGesture(minimumDuration: 1)
                .onEnded({_ in self.didLongPress.toggle() })
            )
    }

使用方式和TapGesture相同,指定一个onEnd的事件即可,这里添加了一个animation的修饰符,目的只是让这个手势产生的效果流畅一些。

LongPressGesture 配合 @GestureState

在上面的例子中,CardView变大之后,若想让它回到原来的大小则需要再次长按。在配合使用@GestureState,可以实现变大后自动回复原状的效果:

struct ContentView: View {
    @State private var selected: Bool = false
    @GestureState private var didLongPress: Bool = false
    
    var body: some View {
        CardView(selected: self.selected)
            .scaleEffect(didLongPress ? 1.2 : 1)
            .animation(.easeInOut)
            .frame(width: 200, height: 200, alignment: .center)
            .gesture(LongPressGesture(minimumDuration: 1)
                .updating($didLongPress, body: { (value, state, transcation) in
                    state = value
                })
            )
    }

示例中的update用于返回过程中的状态。在进行下一个手势讲解前,我们看一下当添加了两个手势修饰符的效果:

.gesture(TapGesture().onEnded { print("Tap") })
.gesture(LongPressGesture(minimumDuration: 1).onEnded { _ in print("LongPress") })

如果顺序如上所述,那么在快速点击的情况下,只有Tap生效,在长按的情况下,只有LongPress生效。如果我们翻转两个手势的顺序,情况将大不相同,Tap手势将完全失效。

DragGesture

Drag相对于UIKit中的Pan手势,示例如下所示:

struct ContentView: View {
    
    @State private var selected: Bool = false
    @State private var offset: CGSize = CGSize.zero
    
    var body: some View {
        CardView(selected: self.selected)
            .offset(self.offset)
            .gesture(DragGesture()
                .onChanged { value in
                    self.offset = CGSize(width: 0, height: value.translation.height)
                }
                .onEnded{ value in
                    self.offset = .zero
                }
            )
            .animation(.spring())
    }
}

此处添加了一个Drag手势,通过onChanged获得在drag过程中的状态,onEnded获得手指离开该View后的时间,并且在这两个回调中改变一个@State,无需过多的介绍,声明式的API足以自解释。

Magnification & Rotation Gestures

Magnification相当于UIKit中的Pinch,先看例子:

struct ContentView: View {
    @State var magnificationValue: CGFloat = CGFloat(1)
    @State var rotationValue: Angle = .zero
    
    var body: some View {
        CardView(selected: self.selected)
            .frame(width: 200, height: 200, alignment: .center)
            .scaleEffect(magnificationValue)
            .rotationEffect(rotationValue)
            .gesture(MagnificationGesture()
                .onChanged({ (value) in
                    self.magnificationValue = value
                })
            )
            .simultaneousGesture(RotationGesture().onChanged({ (value) in
                self.rotationValue = value
            }))
    }
}

此处MagnificationGesture可配合scaleEffect,开放大缩小当前view,RotationGesture可配合rotationEffect,旋转当前view。同时,这里使用了一个simultaneousGesture,这个修饰符用于组合多个手势共同生效,如示例代码中,两个手势可以共同决定view的大小和角度。到此是不是可以足够体现出声明式API的优势。

相关文章

网友评论

      本文标题:SwiftUI - Gesture

      本文链接:https://www.haomeiwen.com/subject/cutbactx.html