SwiftUI: принудительное изображение за изображением при переходе

#swiftui

#swiftui

Вопрос:

Цель: принудительно поместить изображение кнопки за другим изображением, чтобы при появлении / исчезновении других кнопок они не отображались на экране до тех пор, пока не выйдут за край верхнего изображения. Пример, но, похоже, это работает только тогда, когда кнопки исчезают.

Текущее: кнопки исчезают и накладываются на верхнюю кнопку. Пример.

Попытка: я отрегулировал непрозрачность и попытался использовать ZStack, но ни один из подходов не был успешным.

Код:

 struct buttonReveal : View {
@State var isShowing = false

var body: some View {
    
    
    HStack{
        ZStack{
            
            Circle()
                .foregroundColor(.white)
                .frame(width: 100, height: 100)
            
            
            Button(action: {
                withAnimation(.linear(duration: 0.5)) { self.isShowing.toggle() }
            }, label: {
                ZStack {
                    Circle()
                        .foregroundColor(.white)
                        .frame(width: 100, height: 100)
                    Image(systemName: "chevron.forward.circle")
                        .buttonModifier()
                }
                .rotationEffect(Angle(degrees: isShowing ? -180 : 0))
                .animation(.spring())
                
            })
        }

    
        if isShowing {
            Button(action: {
                isShowing.toggle()
            }, label: {
                Image(systemName: "trash.circle")
                    .buttonModifier()
            })
            .transition(.moveAndFade(edge: .leading, offsetX: 0, offsetY: 0))
            .animation(.spring())
            
            Button(action: {
                isShowing.toggle()
            }, label: {
                Image(systemName: "gear")
                    .buttonModifier()
            })
            .transition(.moveAndFade(edge: .leading, offsetX: -150, offsetY: 0))
            .animation(.spring())

        } else {
            Spacer()
            
        }
    
    }
    

    
    
}
}


extension AnyTransition {
static func moveAndFade(edge: Edge, offsetX: CGFloat, offsetY: CGFloat) -> AnyTransition {
    AnyTransition.move(edge: edge).combined(with: .opacity).combined(with: offset(x: offsetX, y: offsetY))
}
}


extension Image {
func buttonModifier() -> some View {
    self
        .resizable()
        .frame(width: 100, height: 100)
        .foregroundColor(.primary)
        .padding()
}
}
 

Ответ №1:

Не уверен, что это лучшая практика, она не идеально выравнивается и нуждается в некоторой очистке, но, возможно, поможет вам двигаться в правильном направлении..

по сути, я создал вторичный HStack, который находится под основным, в отличие от двух, использующих один и тот же

(может быть, кто-нибудь может дать рекомендации по улучшению практики)

 struct buttonReveal : View {
    @State var isShowing = false

    var body: some View {

        ZStack {
            HStack {
                Spacer().frame(width: 100, height: 100)
                if isShowing {
                    Button(action: {
                        isShowing.toggle()
                    }, label: {
                        Image(systemName: "trash.circle")
                            .buttonModifier()
                    })
                    .transition(.moveAndFade(edge: .leading, offsetX: 0, offsetY: 0))
                    .animation(.spring())

                    Button(action: {
                        isShowing.toggle()
                    }, label: {
                        Image(systemName: "gear")
                            .buttonModifier()
                    })
                    .transition(.moveAndFade(edge: .leading, offsetX: -150, offsetY: 0))
                    .animation(.spring())
                }
            }
            HStack {
                ZStack {
                    Circle()
                        .foregroundColor(.white)
                        .frame(width: 100, height: 100)


                    Button(action: {
                        withAnimation(.linear(duration: 0.5)) { self.isShowing.toggle() }
                    }, label: {
                        ZStack {
                            Circle()
                                .foregroundColor(.white)
                                .frame(width: 100, height: 100)
                            Image(systemName: "chevron.forward.circle")
                                .buttonModifier()
                        }
                        .rotationEffect(Angle(degrees: isShowing ? -180 : 0))
                        .animation(.spring())

                    })

                }

                Spacer()
            }
        }
    }
}
 

Ответ №2:

Это работает для вас? Вместо того, чтобы рисовать кнопки на экране при отображении, я просто включил их уже на экране в ZStack, за верхней кнопкой.

 struct ButtonReveal: View {
    
    @State var isShowing = false

    var body: some View {
        ZStack {
            Image(systemName: "gear")
                .buttonModifier()
                .offset(x: isShowing ? 300 : 0)

            Image(systemName: "trash.circle")
                .buttonModifier()
                .offset(x: isShowing ? 150 : 0)

            Image(systemName: "chevron.forward.circle")
                .buttonModifier()
                .rotationEffect(Angle(degrees: isShowing ? -180 : 0))
        }
        .animation(.spring())
        .onTapGesture {
            isShowing.toggle()
        }
    }
}

struct ButtonReveal_Previews: PreviewProvider {
    static var previews: some View {
        ButtonReveal()
            .frame(maxWidth: .infinity, alignment: .leading)
    }
}


extension Image {
    func buttonModifier() -> some View {
        self
            .resizable()
            .frame(width: 100, height: 100)
            .foregroundColor(.primary)
            .padding()
            .background(
                Circle()
                    .fill(Color.white)
            )
    }
}