iOS开发 SwiftUI 15:手势 拖动 缩放 旋转

iOS开发 SwiftUI 15:手势 拖动 缩放 旋转
苹果公司做对了很重要的一点移动设备要为手指设计你看我是个很客观的人不总是说苹果的坏话。相比而言windows的移动化的一大失败就是手指很难精确点中狭小的菜单而通过长按呼出右键菜单也完全失去了“快捷”的意义。目录.onTapGesture 点击.onLongPressGesture 长按DragGesture 拖动 任意移动对象MagnificationGesture 缩放RotationGesture 旋转手势组合.onTapGesture 点击点击很容易理解就是鼠标单击。点击和Button有什么区别Button按下有默认效果处理点击就是点击没有效果的。点击可以用.onTapGesture修饰最简单。也可以用比较复杂的.gesture.gesture里面可以综合综合多种手势。因为太简单我们代码后面再列。.onLongPressGesture 长按长按可以理解为对应鼠标右键长按有参数控制最短时间和手指移动最大距离不过一般来讲不需要修改遵循系统就可以了。长按可以区分按下和结束按下会被不断触发可以搞点动画结束处理和单击差不多。按下和结束通过bool参数来区分。下面是点击和长按的代码State var str info var body: some View { VStack { Spacer() Text(str) .padding() .border(.blue) .onTapGesture { str Tap Text } .onLongPressGesture(pressing: { b in if b { str ...... Text } }) { str LongPress Text } Spacer() .background(.green) //没用 .border(.green) //没用 .onTapGesture { //没用Spacer啥都不支持 str Tap Spacer } } .background(.green) //背景区域都有效 .border(.green, width: 5) //仅对边框有效但是当边框位置改变时原来的位置仍然有效或许是BUG .onTapGesture { str Tap VStack } }代码里对不同视图都做了点击处理。实测Spacer是完全不响应的设置边界和背景也没有意义也就是Spacer只是用来改变布局的。对VStack设置背景的话整个背景都有效设置边界则只有边界上才有效而且当边界改变时新边界有效原来的边界位置也仍然有效不晓得是feature还是BUG。程序很简单都是改变文本内容初始点击长按过程中截不到图效果是有的。DragGesture 拖动 任意移动对象注意是拖动不是拖放onDrag是拖放没有onDragGesture只能用.gesture(DragGesture())来实现。拖动最简单的应用就是改变对象位置然而可笑的是书上和网上的例子试了都有问题手指放开就会回到原位下面的代码是我重新琢磨的State var offset CGSize.zero State var lastoffset CGSize.zero //in body Text(str)//长按下面增加两个修饰 .offset(offset) .gesture( DragGesture() .onChanged { Value in offset.width lastoffset.width Value.translation.width offset.height lastoffset.height Value.translation.height } .onEnded { Value in lastoffset.width Value.translation.width lastoffset.height Value.translation.height } ).offset用来指示位置移动参数是移动多少就是说每次绑定变量offset改变时触发.offset修饰基于原始位置移动对象。DragGesture的onChanged和onEnded参数都是DragGesture.Value包含了手势的相关参数包括起始位置、当前位置、当前偏移量预测结束位置、预测结束偏移量和持续时间。由于.offset的参数是偏移量因此我们用了translation。但是每次拖动时translation都是基于本次拖动所以需要额外一个变量来记住上次拖动最后停留在哪里了。代码中lastoffset用来记录本次拖动之前的偏移量offset则是本次之前的偏移量加上当前拖动的偏移量。效果原始位置拖到左上角再拖到右下角我们注意到文本变成了省略号这是在长按过程中设置的。刚开始动作被识别为长按然后变成了拖动因此最后长按结束动作不会被触发。MagnificationGesture 缩放缩放和拖动一样没有独立的onMagnificationGesture只能在.gesture处理。.gesture如果需要同时处理多个手势需要使用SimultaneousGesture。如果希望只识别优先级高的则使用ExclusiveGesture。下面的代码同时支持拖动和缩放State var scale: CGFloat 1.0 .scaleEffect(scale) .gesture( SimultaneousGesture( //ExclusiveGesture DragGesture() .onChanged { Value in offset.width lastoffset.width Value.translation.width offset.height lastoffset.height Value.translation.height } .onEnded { Value in lastoffset.width Value.translation.width lastoffset.height Value.translation.height }, MagnificationGesture() .onChanged { value in scale value } ) )这段代码在预览里不太容易做出缩放效果需要借助option键而且由于Text视图比较小容易因为系统认为的两指位置超出范围而无效但在实体机上是比较容易的。由于同时支持了拖动缩放完位置也会变。.scaleEffect控制视图的缩放效果参数是缩放比例onChanged的参数也是缩放比例所以只要把参数复制给绑定变量就行了。效果与拖动一样也有onEnded可供处理。RotationGesture 旋转旋转是两指旋转而不是单指。代码也完全类似缩放.rotationEffect修饰视图的旋转参数是角度类型为Angle。角度分角度和弧度两种不过只影响Angle类型的使用我们这里直接从onChanged参数复制不涉及具体计算。由于ExclusiveGesture只支持两种手势组合所以需要用.highPriorityGesture代替.gesture这两个的区别顾名思义一个是高优先级一个是默认优先级。代码State private var angle: Angle Angle(degrees: 90) //.zero // 旋转角度 .rotationEffect(angle) .gesture( 。。。。。。 ) .highPriorityGesture( RotationGesture() .onChanged { value in angle value } )手势组合前面我们已经使用了不同的手势组合方法实话说有点乱。下面是本篇的完整代码import SwiftUI struct SwiftUIViewGesture: View { State var str info //跟踪信息 State var offset CGSize.zero //偏移量基于原始位置 State var lastoffset CGSize.zero //拖动时的基础偏移量 State var scale: CGFloat 1.0 //放大倍数 State private var angle: Angle Angle(degrees: 90) //.zero // 旋转角度 var body: some View { VStack { Spacer() Text(str) .padding() .border(.blue) .onTapGesture { str Tap Text } .onLongPressGesture(pressing: { b in if b { str ...... Text } }) { str LongPress Text } .offset(offset) .scaleEffect(scale) .rotationEffect(angle) .gesture( SimultaneousGesture( //仅处理优先级高的 //ExclusiveGesture(//同时处理 DragGesture() .onChanged { Value in offset.width lastoffset.width Value.translation.width offset.height lastoffset.height Value.translation.height } .onEnded { Value in lastoffset.width Value.translation.width lastoffset.height Value.translation.height }, MagnificationGesture() .onChanged { value in scale value } ) ) .highPriorityGesture( RotationGesture() .onChanged { value in angle value } ) Spacer() .background(.green) //没用 .border(.green) //没用 .onTapGesture { //没用Spacer啥都不支持 str Tap Spacer } } .background(.green) //背景区域都有效 .border(.green, width: 5) //仅对边框有效但是当边框位置改变时原来的位置仍然有效或许是BUG .onTapGesture { str Tap VStack } } } #Preview { SwiftUIViewGesture() }