最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - Animate parent view height without moving subviews - Stack Overflow

programmeradmin0浏览0评论

I’m trying to figure out how to change the height of a SwiftUI view with animation, without affecting the position of its subviews. I have an HStack containing some text objects. I want to animate the height of the view from 0 to 40 like so:

Note that the text objects shouldn't move as the parent view height changes (they should be anchored to the top of the parent view). I’ve tried the standard transitions such as: .transition(.scale(scale: 0, anchor: UnitPoint.top)). But that scales the entire view, including the subviews.

Here’s the view code:

struct SampleView: View {
    
    var body: some View {
        
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                
                let strings = ["Text 1", "Text 2", "Text 3"]
                ForEach(strings, id: \.self) { string in
                    Text(string)
                }
            }
            .foregroundStyle(Color.white)
        }
        .background(Color.orange)
    }
}

An instance of this SampleView is created when a button action withAnimation block updates a variable.

Any suggestions?

I’m trying to figure out how to change the height of a SwiftUI view with animation, without affecting the position of its subviews. I have an HStack containing some text objects. I want to animate the height of the view from 0 to 40 like so:

Note that the text objects shouldn't move as the parent view height changes (they should be anchored to the top of the parent view). I’ve tried the standard transitions such as: .transition(.scale(scale: 0, anchor: UnitPoint.top)). But that scales the entire view, including the subviews.

Here’s the view code:

struct SampleView: View {
    
    var body: some View {
        
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                
                let strings = ["Text 1", "Text 2", "Text 3"]
                ForEach(strings, id: \.self) { string in
                    Text(string)
                }
            }
            .foregroundStyle(Color.white)
        }
        .background(Color.orange)
    }
}

An instance of this SampleView is created when a button action withAnimation block updates a variable.

Any suggestions?

Share Improve this question edited Feb 4 at 8:27 Benzy Neez 22.3k3 gold badges15 silver badges42 bronze badges asked Feb 4 at 7:02 RobertoRoberto 4136 silver badges9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 2

You can create a custom transition that scales a mask of the view.

struct RevealTransition: Transition {
    func body(content: Content, phase: TransitionPhase) -> some View {
        content.mask {
            Rectangle()
                .scaleEffect(y: phase.isIdentity ? 1 : 0, anchor: .top)
        }
    }
}

extension AnyTransition {
    static var reveal: AnyTransition { AnyTransition(RevealTransition()) }
}

Example usage:

@State private var show = false
var body: some View {
    VStack {
        if show {
            SampleView()
                .transition(.reveal)
        }
        Button("Animate") {
            show.toggle()
        }
    }
    .animation(.default, value: show)
}

If you want the view to disappear in a different way, edit the body as needed, or use an asymmetric transition.

If the content that is being revealed is inside a VStack and if it is followed by content with an opaque background (in other words, if it is followed by content that will cover it during the transition), then the reveal works this way as standard. Normally it is accompanied by an .opacity transition, but you can override this by applying a .transition modifier.

If you want to enforce a fixed height and if you want the revealed content to have an orange background for this full height, you either need to apply the fixed height inside SampleView before applying the orange background, or you can apply an orange background to the surrounding container too (the VStack). However, a better approach might be to let the revealed view determine its own height.

struct ContentView: View {
    @State private var isShowing = false

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            Text("The quick brown fox")
                .padding()
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(.background)
            if isShowing {
                SampleView()
                    .frame(height: 40)
                    .transition(.asymmetric(
                        insertion: .identity,
                        removal: .opacity
                    ))
            }
            Text("jumps over the lazy dog")
                .padding()
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(.background)
        }
        .background(.orange)
        .frame(height: 100, alignment: .top)
        .onTapGesture {
            withAnimation(.easeInOut(duration: 3)) {
                isShowing.toggle()
            }
        }
    }
}

发布评论

评论列表(0)

  1. 暂无评论