Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swiftui animation how to determine the asymmetric transition in real time

It's hard to describe my problem in words.

I want to do a selector behave like that.

enter image description here

But after I change the browse direction, because I already set the in and out transition, even if I change the transition to the new one, the view still uses the old one to disappear. (the first one and the last one)

Is there some way to solve the problem?

enter image description here

Here is the code

 @State var translations:[String] = ["未知"]
    @State var index = 0
    @State var transition:AnyTransition = .asymmetric(insertion: .move(edge: .trailing), removal:.move(edge: .leading)).combined(with: .opacity)
var transTexts:some View{
        HStack{
            if(translations.count > 1){
                Button {
                    if(index > 0){
                        transition = .asymmetric(insertion: .move(edge: .leading), removal:.move(edge: .trailing)).combined(with: .opacity)
                        disabled.toggle()
                        index -= 1
                    }
                } label: {
                    Image(systemName:"arrowtriangle.backward.circle.fill")
                }
                .disabled(index == 0)
                Text(translations[index])
                    .scroll()
                    .font(.body)
                    .id(index)
                    .disabled(disabled)
                    .transition(transition)
            }
            Spacer()
            if(translations.count  > 1){
                Button {
                    if(index < translations.count-1){
                        transition = .asymmetric(insertion: .move(edge: .trailing), removal:.move(edge: .leading)).combined(with: .opacity)
                        disabled.toggle()
                        index += 1
                    }
                } label: {
                    Image(systemName:"arrowtriangle.forward.circle.fill")
                }
                .disabled(index == translations.count - 1)
            }
        }
        .animation(.default,value: index)
like image 230
Perry Wang Avatar asked Feb 02 '26 19:02

Perry Wang


1 Answers

I tried manually controlling which transition it is, by using a Toggle. The transition works correctly if I just toggled the toggle before I switched direction. e.g. go right 3 times, toggle, then go left 3 times.

When you do this automatically with buttons, it seems like the time when index changes is "too close" to when transition changes, and doesn't have the same effect as when I do it manually.

If you just delay the change of index by a tiny amount of time, it should work.

transition = ...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
    index += 1 // or -= 1
}

Interestingly, DispatchQueue.main.async { ... } doesn't work. You have to give it some time.

I'd also suggest using a Bool or enum to encode the direction it is going in, rather than an AnyTransition.

Working code:

@State var translations:[String] = ["Lorem Ipsum", "Foo Bar baz", "Something Else", "AAAAA", "BBBBB"]
@State var index = 0

@State var isForward = true

var body:some View{
    VStack {
        HStack{
            if(translations.count > 1){
                Button {
                    if(index > 0){
                        isForward = false
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
                            index -= 1
                        }
                    }
                } label: {
                    Image(systemName:"arrowtriangle.backward.circle.fill")
                }
                .disabled(index == 0)
            }
            
            if translations.count > 0 {
                Text(translations[index])
                    .font(.body)
                    .id(index)
                    .transition(
                        // I did not combine with .opacity here to make it more obvious
                        isForward ?
                            .asymmetric(insertion: .move(edge: .trailing), removal:.move(edge: .leading)) :
                                .asymmetric(insertion: .move(edge: .leading), removal:.move(edge: .trailing))
                    )
            }
            
            Spacer()
            
            if(translations.count  > 1){
                Button {
                    if index < translations.count - 1 {
                        isForward = true
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
                            index += 1
                        }
                    }
                } label: {
                    Image(systemName:"arrowtriangle.forward.circle.fill")
                }
                .disabled(index == translations.count - 1)
            }
        }
        .animation(.default,value: index)
    }
}
like image 125
Sweeper Avatar answered Feb 04 '26 08:02

Sweeper



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!