75142913在线留言
【SwiftUI基础篇】19 一文读懂手势操作,常用手势监听与触发应用详解_IOS开发_网络人

【SwiftUI基础篇】19 一文读懂手势操作,常用手势监听与触发应用详解

Kwok 发表于:2021-04-06 16:06:47 点击:122 评论: 0

SwiftUI为我们提供了处理视图的许多手势,并且在消除大部分辛苦工作方面做得非常出色,因此我们可以专注于程序的重要的部分。最常见的是onTapGesture(),这很容易,但是还有其他几个,还有一些有趣的方式可以将手势组合在一起,值得一试。

任何SwiftUI视图都可以使用手势识别器,而这些手势识别器又可以连接将在识别器激活时运行的闭包。 下面将介绍一个简单的gesture。例如,这将创建一个图像,该图像在每次点击时都会变小:

Text("ireland")
    .scaleEffect(scale)
    .gesture(
        TapGesture()
            .onEnded { _ in
                scale -= 0.1//点击一次减0.1
            }
    )

gesture可以将手势附加到任何视图,其优先级低于视图定义的手势。

符合gesture协议的有下列手势(iOS 13.0以上、macOS 10.15以上、Mac Catalyst 13.0以上、watchOS 6.0+):

  1. AnyGesture 一种类型擦除的手势。
  2. DragGesture 拖动动作,随着拖动事件序列的更改而调用动作。
  3. ExclusiveGesture 一个由两个手势组成的手势,其中只有一个可以成功。
  4. GestureStateGesture 一个更新手势更新回调提供的状态的手势。
  5. LongPressGesture 用户执行长按时成功的手势。
  6. MagnificationGesture 识别放大运动并跟踪放大量的手势。
  7. RotationGesture 识别旋转运动并跟踪旋转角度的手势(下面组合手势里有介绍)。
  8. SequenceGesture 一个由两个手势组成的序列的手势。
  9. SimultaneousGesture 一个包含两个手势的手势,两个手势可以同时发生,并且两个手势都不在另一个手势之前(最下面有单独介绍)。
  10. TapGesture 识别一个或多个轻击的手势。

一、快速应用视图的基本手势

SwiftUI提供2个快速应用于视图2个带on的基本手势,一个是点击(onTapGesture),另一个是长按(onLongPressGesture) 。

1、onTapGesture 识别一个或多个轻击的手势。添加一个在视图识别点击手势时执行的操作。

Text("点我试试!")
    .onTapGesture{
        print("单击")
    }
    .onTapGesture(count: 2) {
        print("双击")
    }

小TIPS:可以ButtonStyle修饰达到与按钮一样的视觉效果哦~

2、onLongPressGesture 当用户执行长按时成功的手势。 

Text("长按我不放!")
    .onLongPressGesture {
        print("用户长按操作!")
    }

像轻击onTapGesture手势一样,长按手势也可以自定义。例如,您可以指定印刷的最短时间,因此仅在经过特定的秒数后才触发操作关闭。例如,这将仅在两秒钟后触发:

Text("按我5秒才能触发操作!")
    .onLongPressGesture(minimumDuration: 5) {
        print("长按5秒,停止运动!")//类似Keep的结束跑步
    }

如果想监听长按的开始与结束,可以使用下面的方法:

Text("监听长按期间的事件!")
    .onLongPressGesture(minimumDuration: 5, pressing: { inProgress in
        print( inProgress ? "用户正在长按中~" : "用户长按结束")
    }) {
        print("长按成功")
    }
  1. 按下后,progress闭包将被调用,其inProgress参数设置为true。
  2. 如果在识别手势之前释放(不到5秒就松手),则将以其inProgress参数设置为false的方式调用更改闭包。
  3. 如果按住识别器的整个长度(按了5秒),则将以其inProgress参数设置为false调用更改闭包(因为该手势不再处于按压状态),并且也将调用完成闭包。

二、基本手势

上面已介绍了长按与点击操作,因为比较简单,所以这里将不再赘述,下面将介绍几个经常会用到的基本手势的使用:

1、DragGesture 拖动动作,随着拖动事件序列的更改而调用动作。

初始化(minimumDistance: CGFloat, coordinateSpace: coordinateSpace),创建一个拖动手势,使其在手势成功之前具有最小的拖动距离和手势所在的坐标空间。

struct ContentView: View {
    @State private var dragOffset = CGSize.zero
    var body: some View {
        VStack {
            Text("rome")
                .offset(dragOffset)//视图偏移
                .gesture(
                    DragGesture()
                        .onChanged { gesture in
                            dragOffset = gesture.translation//将拖动位置赋值给偏移量
                        }
                        .onEnded { gesture in
                            dragOffset = .zero//拖动结束后位置还原
                        }
                )
        }
    }
}

 关于拖动手势下面的示例中将有更多的代码应用。

2、MagnificationGesture 识别放大运动并跟踪放大的手势。

添加一个放大手势到一个圆上,当用户执行这个手势时,可以改变它的大小:

struct ContentView: View {
    //GestureState在用户执行手势时更新属性,并在手势结束时将属性重置回初始状态。
    @GestureState var magnifyBy:CGFloat = 1.0 //定义一个状态跟踪值
    //Gesture 将一系列事件匹配到一个手势,并为其每个状态返回一个值流。
    var magnification: some Gesture {
        MagnificationGesture()
            //当手势的值改变时利用updating更新提供的手势状态属性。
            .updating($magnifyBy) { currentState, gestureState, transaction in
                gestureState = currentState
            }
    }
    
    var body: some View {
        Circle()
            .frame(width: 100 * magnifyBy,
                   height: 100 * magnifyBy,
                   alignment: .center)
            .gesture(magnification)//应用手势操作
    }
}

放大手势跟踪放大事件序列的变化。要识别视图上的放大手势,创建并配置手势,然后使用手势(_:including:)修饰符将它添加到视图中。

3、RotationGesture 识别旋转运动并跟踪旋转角度的手势。

旋转手势跟踪一个旋转事件序列的变化。要识别视图上的旋转手势,创建并配置这个手势,然后使用手势(_:including:)修饰符将它添加到视图中。

给矩形添加一个旋转手势,并应用一个旋转效果: 

struct ContentView: View {
    @State var angle = Angle(degrees: 0.0)//形状的初始角度
    var rotation: some Gesture {
        RotationGesture()
            .onChanged { angle in
                self.angle = angle//将用户旋转的角度赋值给监听属性angle
            }
    }
    var body: some View {
        Rectangle()
            .frame(width: 200, height: 200, alignment: .center)
            .rotationEffect(self.angle)//通过监听属性值的变化发生角度变化
            .gesture(rotation)//应用手势操作
    }
}

三、组合手势

组成Swift UI手势组合手势以创建复杂的交互。上面代码里已看到 gesture 将组合的手势应用给视图,下面使用组合的手势实现一个可以拖动的红色圆圈:

struct ContentView: View {    
    @State private var offset = CGSize.zero//拖了多远
    @State private var isDragging = false//是否可以被拖走
    var body: some View {
        // 定义一个在移动时更新偏移量和isdrag
        let dragGesture = DragGesture()//当拖动事件序列改变时调用动作的拖动动作。
            .onChanged { value in self.offset = value.translation }//拖动后更新偏移量
            .onEnded { _ in
                withAnimation {
                    self.offset = .zero//位置还原,注释这行圈圈将停留在被拖到的位置
                    self.isDragging = false//拖动状态还原为不能拖走
                }
            }
        
        // 当用户长按后isDragging变成可拖动状态
        let pressGesture = LongPressGesture()
            .onEnded { value in
                withAnimation {
                    self.isDragging = true
                }
            }
        
        // 用户必须长按后才能拖动的组合手势
        let combined = pressGesture.sequenced(before: dragGesture)
        // 一个64x64的圆,当它被拖动时,它会放大,将它的偏移量设置为我们从拖动手势得到的任何东西,并使用我们的组合手势
         Circle()//仔细看,上面都是let,这里才是视图
            .fill(Color.red)
            .frame(width: 64, height: 64)
            .scaleEffect(isDragging ? 1.5 : 1)//isDragging可拖动状态时放大1.5倍
            .offset(offset)//偏移位置
            .gesture(combined)//将组合的手势应用给Circle视图
    }
}

用户需要将其中一个手势完成后才能使用另一个手势(长按成功后才会变为活动状态)。这需要更多的思考,因为手势需要能够相互引用,因此您不能仅将它们直接附加到视图。

 

三、高级手势

对于更高级的手势,你应该使用gesture()修饰符手势结构之一:DragGesture,LongPressGesture,MagnificationGesture,RotationGesture,和TapGesture。这些都具有通常onEnded()和经常onChanged()使用的特殊修饰符,并且您可以在手势进行中(for onChanged())或完成中(for onEnded())时使用它们进行操作。

例如,我们可以将放大手势附加到视图上,以便捏入和缩小可以放大和缩小视图。可以通过创建两个@State属性来存储缩放比例scaleEffect(),然后在修饰符中使用该属性,然后在手势中设置这些值来完成此操作,如下所示:

//模拟器里需要按住OPT键拖动看效果
struct ContentView: View {
    @State private var currentAmount: CGFloat = 0//目前的数量
    @State private var finalAmount: CGFloat = 1//最终的数量
    
    var body: some View {
        Text("我是一个可旋转的文本内容!")
            //scaleEffect 在水平和垂直方向上,相对于一个锚点,按给定的数量缩放这个视图的渲染输出。
            .scaleEffect(finalAmount + currentAmount)
            //gesture 将手势附加到视图,其优先级低于视图定义的手势。
            .gesture(
                //识别放大动作并跟踪放大量的手势。
                MagnificationGesture()
                    //将改变的数量交给监听属性
                    .onChanged { amount in
                        self.currentAmount = amount - 1
                    }
                    //将结束后最终数量finalAmount交给监听忏悔
                    .onEnded { amount in
                        self.finalAmount += self.currentAmount
                        self.currentAmount = 0//回到初始状态
                    }
            )
    }
}

可以使用完全相同的方法来旋转视图RotationGesture,只是现在我们使用rotationEffect()修饰符:

//按住键盘上的OPT(ALT)键盘测试放大旋转
struct ContentView: View {
    @State private var currentAmount: Angle = .degrees(0)//目前的角度
    @State private var finalAmount: Angle = .degrees(0)//最终角度
    
    var body: some View {
        Text("我是通过角度旋转的文字内容!")
            .rotationEffect(currentAmount + finalAmount)
            .gesture(
                RotationGesture()
                    .onChanged { angle in
                        self.currentAmount = angle//将改后的弧度或角度交给监听属性
                    }
                    .onEnded { angle in
                        self.finalAmount += self.currentAmount
                        self.currentAmount = .degrees(0)
                    }
            )
    }
}

四、手势优先权

当手势发生冲突时,事情开始变得更加有趣了–当您同时识别两个或多个手势时,例如您将一个手势附加到视图上,并将同一手势附加到其父视图上。

1、手势的优先权

例如,这会将on附加onTapGesture()到文本视图及其父视图:

VStack {
    Text("我离你最近,我具有点击优先权!")
        .onTapGesture {
            print("文本点击(我将优先识别)")
        }
}
.onTapGesture {
    print("VStack 点击(我非优先)")
}

在这种情况下,SwiftUI将始终为子视图提供手势优先权,这意味着当您点击上方的文本视图时,您将看到“文本点击”。但是,如果要更改它,可以使用highPriorityGesture()修饰符来强制触发父母的手势,如下所示:

VStack {
    Text("不能优先触发我的onTapGesture,你点我干嘛!!!")
        .onTapGesture {
            print("Text 点击(VStack做弊了~)")
        }
}
//highPriorityGesture将一个手势附加到视图,其优先级高于视图所定义的手势。
.highPriorityGesture(
    TapGesture()
        .onEnded { _ in
            print("VStack 点击(我有优先权,因为有highPriorityGesture加持~)")
        }
)

2、SimultaneousGesture 可以包含两个手势并可以同时触发,而两个手势都不在另一个手势之前。您可以使用simultaneousGesture()修饰符告诉SwiftUI您希望同时触发父手势和子视图的手势,如下所示:

VStack {
    Text("Hello, World!")
        .onTapGesture {
            print("Text 点击(我将VStack同时输出)")
        }
}
//将一个手势附加到视图,以便与视图定义的手势同时处理。
.simultaneousGesture(
    TapGesture()
        .onEnded { _ in
            print("VStack 点击(虽然打印有先后,但其实我们是同时输出的)")
        }
)

这将同时打印“文字被点击”和“ VStack被点击”。

除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/133
标签:手势Kwok最后编辑于:2021-04-07 12:07:50
0
感谢打赏!

《【SwiftUI基础篇】19 一文读懂手势操作,常用手势监听与触发应用详解》的网友评论(0)

本站推荐阅读

热门点击文章