SwiftUI如何处理手势

SwiftUI处理手势的API非常丰富,而且把反锁的部分都做了封装少了很多麻烦。最常用的就是onTapGesture(),当然还有其他的API,也可以把这些API组合使用。

首先看看最简单的onTapGesture()

官方定义:

swift 复制代码
func onTapGesture(
  count: Int = 1,
  perform action: @escaping () -> Void
) -> some View

参数count是说一共摸或者点击多少次才可以触发第二个参数的动作。默认是一次。

swift 复制代码
Text("Hello, World!")
    .onTapGesture(count: 2) {
        print("Double tapped!")
    }

这里count明确指定了需要两次才会打印Double tapped!

处理长按可以使用onLongPressGesture(), 例如:

swift 复制代码
Text("Hello, World!")
    .onLongPressGesture {
        print("Long pressed!")
    }

和处理tap手势一样,长按手势也可以定制处理。比如,可以指定最少时长:

swift 复制代码
Text("Hello, World!")
    .onLongPressGesture(minimumDuration: 2) {
        print("Long pressed!")
    }

还可以指定另外一个闭包来处理长按手势状态的变化:

scss 复制代码
Text("Hello, World!")
    .onLongPressGesture(minimumDuration: 1) {
        print("Long pressed!")
    } onPressingChanged: { inProgress in
        print("In progress: (inProgress)!")
    }

可以看到第二个闭包接收了一个bool型的参数,它可以这样处理:

  1. 在用户按下的时候就会调用这个闭包,参数的值为true
  2. 如果在手势被确认为长按(比如在设定长按最少需要两秒,而在一秒钟的时候就释放)之前就松开手指,第二个闭包也会执行,参数值为false
  3. 如果手势被确认(按的时间足够长),第二个闭包会被执行并且参数值为false。因为这个时候会执行第一个闭包。

对于更加复杂的手势可以使用gesture()方法和一些手势结构体结合来处理。手势结构体有:DragGesture, LongPressGesture, MagnificationGesture, RotationGestureTapGesture。当然少不了用到这些结构体的方法: onEnd()onChanged()。手势进行中使用onChanged(),手势完成用onEnd()

下面的例子里,给一个视图加上一个磁性手势。通过手势来缩放该视图。这不仅需要手势,还需要一个@State属性来存储缩放大小值,然后在scaleEffect()里使用就可以达到想要的效果。例如:

swift 复制代码
struct ContentView: View {
    @State private var currentAmount = 0.0
    @State private var finalAmount = 1.0

    var body: some View {
        Text("Hello, World!")
            .scaleEffect(finalAmount + currentAmount)
            .gesture(
                MagnificationGesture()
                    .onChanged { amount in
                        currentAmount = amount - 1
                    }
                    .onEnded { amount in
                        finalAmount += currentAmount 
                        currentAmount = 0
                    }
            )
    }
}

同样的道理,可以使用RotationGesture来实现,这是这次要调用rotationEffect()。例如:

swift 复制代码
struct ContentView: View {
    @State private var currentAmount = Angle.zero
    @State private var finalAmount = Angle.zero

    var body: some View {
        Text("Hello, World!")
            .rotationEffect(currentAmount + finalAmount)
            .gesture(
                RotationGesture()
                    .onChanged { angle in
                        currentAmount = angle
                    }
                    .onEnded { angle in
                        finalAmount += currentAmount
                        currentAmount = .zero
                    }
            )
    }
}

还有一种很有趣的情况。如果一个试图挂了一个手势识别方法,而它的父试图也挂了一个会发生什么呢?比如一个文本试图和它的父试图都挂了onTapGesture()。例如:

swift 复制代码
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .onTapGesture {
                    print("Text tapped")
                }
        }
        .onTapGesture {
            print("VStack tapped")
        }
    }
}

在这种情况下,SwiftUI会把优先权交给子视图 。也就是说,点了TextView会显示Text Tapped 。如果要强行改变这个事实可以使用highPriorityGesture()。例如:

swift 复制代码
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .onTapGesture {
                    print("Text tapped")
                }
        }
        .highPriorityGesture(
            TapGesture()
                .onEnded { _ in
                    print("VStack tapped")
                }
        )
    }
}

于是就不得不提另外一个具有类似功能的方法了simultaneousGesture()。它会告诉SwiftUI同时满足两个试图的手势功能。例如:

swift 复制代码
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .onTapGesture {
                    print("Text tapped")
                }
        }
        .simultaneousGesture(
            TapGesture()
                .onEnded { _ in
                    print("VStack tapped")
                }
        )
    }
}

这是会同时显示Text tappedVStack tapped

最后,SwiftUI还支持使用手势序列。就是把几个手势组合到一起。要使用这个功能就不能像上面的例子一样简单的挂在某个试图上了。

这个例子是长按之后拖动一个圆形,看看下面的代码:

swift 复制代码
struct ContentView: View {
    // 圆形被拖动了多远
    @State private var offset = CGSize.zero

    // 当前是否被拖动
    @State private var isDragging = false

    var body: some View {
        // 拖动手势,拖动时更新offset和isDragging属性
        let dragGesture = DragGesture()
            .onChanged { value in offset = value.translation }
            .onEnded { _ in
                withAnimation {
                    offset = .zero
                    isDragging = false
                }
            }

        // 一个长按手势,完成之后可以开启拖动
        let pressGesture = LongPressGesture()
            .onEnded { value in
                withAnimation {
                    isDragging = true
                }
            }

        // 手势的组合,组合了长按和拖动手势
        let combined = pressGesture.sequenced(before: dragGesture)

        // 一个64*64的圆形。拖动时变大,拖动时把拖动的值作为偏移值设置给圆形。最受用gesture方法设置了上面的组合手势
        Circle()
            .fill(.red)
            .frame(width: 64, height: 64)
            .scaleEffect(isDragging ? 1.5 : 1)
            .offset(offset)
            .gesture(combined)
    }
}

手势处理,是实现app优秀交互必不可少的!

相关推荐
问道飞鱼23 分钟前
【移动端知识】移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
android·ios·harmonyos·多webview互访
mascon2 小时前
U3D打包IOS的自我总结
ios
名字不要太长 像我这样就好2 小时前
【iOS】继承链
macos·ios·cocoa
karshey3 小时前
【IOS webview】IOS13不支持svelte 样式嵌套
ios
潜龙95273 小时前
第4.3节 iOS App生成追溯关系
macos·ios·cocoa
游戏开发爱好者812 小时前
iOS App 电池消耗管理与优化 提升用户体验的完整指南
android·ios·小程序·https·uni-app·iphone·webview
神策技术社区18 小时前
iOS 全埋点点击事件采集白皮书
大数据·ios·app
wuyoula19 小时前
iOS V2签名网站系统源码/IPA在线签名/全开源版本/亲测
ios
2501_9159184120 小时前
iOS 性能监控工具全解析 选择合适的调试方案提升 App 性能
android·ios·小程序·https·uni-app·iphone·webview
fishycx20 小时前
iOS 构建配置与 AdHoc 打包说明
ios