SwiftUI如何根据变量条件添加手势?解决编译类型不匹配问题
解决SwiftUI中条件手势类型不匹配的问题
问题描述
我正在开发一款绘图应用,支持以下功能:
- 在画布上添加背景和图片。
- 用户可对画布执行放大手势,根据当前编辑模式,执行两种操作之一:
- 缩放画布上的所有内容
- 仅缩放图片
- 用户还可平移画布,使所有内容同步移动。
尝试实现时写出了如下代码:
struct Canvas: View { @State var zoomEverything = false var body: some View { ZStack { images // View that render images background // View that render the background } .gesture(gesturePanEverything().simultaneously(with: zoomEverything ? gestureZoomEverything() : gestureZoomImagesOnly())) } private func gestureZoomEverything() -> some Gesture { MagnificationGesture() .onChanged { value in updateBackgroundScale(value) } .onEnded { value in updateBackgroundScale(value) } } private func gestureZoomImagesOnly() -> some Gesture { MagnificationGesture() .onChanged { value in updateImagesScale(value) } .onEnded { value in updateImagesScale(value) } } private func gesturePanEverything() -> some Gesture { DragGesture() } private func updateImagesScale(_ value: CGFloat) { /* ... */ } private func updateBackgroundScale(_ value: CGFloat) { /* ... */ } var images: some View { EmptyView() } var background: some View { EmptyView() } }
(详细逻辑已替换为占位代码,但足以说明问题)
编译器报错:
Result values in '? :' expression have mismatching types 'some Gesture' (result of 'Canvas.gestureZoomEverything()') and 'some Gesture' (result of 'Canvas.gestureZoomImagesOnly()')
我理解错误原因是两个函数的返回类型实际是不同的具体手势类型(尽管都标注为some Gesture),编译器无法统一类型,但找不到优雅的解决办法。
解决方案
方法1:用AnyGesture统一类型
将两个缩放手势包装为AnyGesture,让三元表达式两边类型一致,编译器就能正常处理:
struct Canvas: View { @State var zoomEverything = false var body: some View { ZStack { images background } .gesture( gesturePanEverything().simultaneously(with: zoomEverything ? AnyGesture(gestureZoomEverything()) : AnyGesture(gestureZoomImagesOnly()) ) ) } private func gestureZoomEverything() -> some Gesture { MagnificationGesture() .onChanged { updateBackgroundScale($0) } .onEnded { updateBackgroundScale($0) } } private func gestureZoomImagesOnly() -> some Gesture { MagnificationGesture() .onChanged { updateImagesScale($0) } .onEnded { updateImagesScale($0) } } private func gesturePanEverything() -> some Gesture { DragGesture() } private func updateImagesScale(_ value: CGFloat) { /* ... */ } private func updateBackgroundScale(_ value: CGFloat) { /* ... */ } var images: some View { EmptyView() } var background: some View { EmptyView() } }
方法2:合并手势逻辑,消除分支
把两个缩放手势的逻辑合并成一个,通过zoomEverything状态判断执行对应的更新操作,无需返回不同手势:
struct Canvas: View { @State var zoomEverything = false var body: some View { ZStack { images background } .gesture( gesturePanEverything().simultaneously(with: gestureZoom()) ) } private func gestureZoom() -> some Gesture { MagnificationGesture() .onChanged { value in zoomEverything ? updateBackgroundScale(value) : updateImagesScale(value) } .onEnded { value in zoomEverything ? updateBackgroundScale(value) : updateImagesScale(value) } } private func gesturePanEverything() -> some Gesture { DragGesture() } private func updateImagesScale(_ value: CGFloat) { /* ... */ } private func updateBackgroundScale(_ value: CGFloat) { /* ... */ } var images: some View { EmptyView() } var background: some View { EmptyView() } }
方法3:拆分手势绑定,使用条件修饰符
将平移和缩放手势分开绑定到ZStack,通过条件判断添加对应缩放手势:
struct Canvas: View { @State var zoomEverything = false var body: some View { ZStack { images background } .gesture(gesturePanEverything()) .gesture(zoomEverything ? gestureZoomEverything() : AnyGesture(gestureZoomImagesOnly())) } private func gestureZoomEverything() -> some Gesture { MagnificationGesture() .onChanged { updateBackgroundScale($0) } .onEnded { updateBackgroundScale($0) } } private func gestureZoomImagesOnly() -> some Gesture { MagnificationGesture() .onChanged { updateImagesScale($0) } .onEnded { updateImagesScale($0) } } private func gesturePanEverything() -> some Gesture { DragGesture() } private func updateImagesScale(_ value: CGFloat) { /* ... */ } private func updateBackgroundScale(_ value: CGFloat) { /* ... */ } var images: some View { EmptyView() } var background: some View { EmptyView() } }
方案对比
AnyGesture方案:改动最小,直接解决类型问题,但存在轻微类型擦除开销(可忽略)。- 合并逻辑方案:无类型擦除,逻辑集中,后续维护更便捷。
- 拆分手势方案:结构清晰,拆分不同手势职责,适合复杂手势场景。
内容的提问来源于stack exchange,提问作者Stephen.W
相关产品推荐
相关产品推荐

