Home > database >  How to make a function returning "some Gesture" or "SimultaneousGesture" properl
How to make a function returning "some Gesture" or "SimultaneousGesture" properl

Time:11-05

Let say I have two functions to create gestures:

func makeGesture1() -> some Gesture {
 ...
}

func makeGesture2() -> some Gesture {
 ...
}

and I want to use them like:

Text("abc")
  .gesture( someCondition? makeGesture1() : makeGesture1().simultaneously(with: makeGesture2())

I got the error:

Result values in '? :' expression have mismatching types 'SimultaneousGesture<some Gesture, some Gesture>' and 'some Gesture'

If I wrap the expression with a function:

func makeGestureConditionally() -> some Gesture() {
  if someCondition {
    return makeGesture1()
  } else {
    return makeGesture1().simultaneously(with: makeGesture2())
  }
}

I got the error:

Function declares an opaque return type, but the return statements in its body do not have matching underlying types

I found that I can do the following, but I wonder if there is a less hacky way, a proper way:

someCondition
                ? AnyGesture(makeGesture1().simultaneously(with: makeGesture2()).map { _ in () })
                : AnyGesture(makeGesture1().map { _ in () })

CodePudding user response:

When you use an "opaque return type" (like some Gesture), you are asserting to the compiler that you will only return 1 specific concrete Gesture type, you just don't want to write out its full signature. This is why you cannot dynamically return different types at runtime. This is why the ternary operator (?:) is failing as well; a ternary operator only accepts operands of the same type.

A workaround is use a SwiftUI ViewBuilder to let the SwiftUI runtime activate the correct gesture on the view based on the condition.

var baseView: some View {
    Text("123")
}

@ViewBuilder
var content: some View {
    if someCondition {
        baseView
            .gesture(makeGesture1())
    } else {
        baseView
            .gesture(makeGesture1().simultaneously(with: makeGesture2()))
    }
}

This is allowed as internally SwiftUI is using something called a "result builder" to only return 1 branch of the if statement at runtime, with each branch of the if statement having a concrete type.

In SwiftUI, avoid using any of the type erased wrappers (AnyView, AnyGesture etc.) as they increase the computational overhead on SwiftUI.

  • Related