Анимировать заголовок при прокрутке в swiftui

#ios #animation #swiftui

#iOS #Анимация #свифтуи

Вопрос:

Я пытаюсь создать представление, в котором заголовок фиксируется в верхней части представления и изменяет его размер в соответствии со смещением прокрутки, когда смещение равно 0, заголовок становится больше, а когда пользователь прокручивает заголовок, он становится меньше

 struct ContentView : View {
    
    @State var largeHeader = true
    
    var body: some View {
        VStack {
            Text("HEADER").padding(.vertical, largeHeader ? 30 : 10)
            Divider()
            ScrollView {
                VStack {
                    Text("Content0")
                        .padding()
                    Text("Content1")
                        .padding()
                    Text("Content2")
                        .padding()
                }
                .background(GeometryReader { geometryProxy -> Color in
                    DispatchQueue.main.async {
                        largeHeader = geometryProxy.frame(in: .named("myspace")).minY >= 0
                    }
                    return Color.clear
                })
            }
            .coordinateSpace(name: "myspace")
        }.animation(.default)
    }
}
 

Это работает нормально, когда содержимое прокрутки длиннее, но когда содержимого мало, как в приведенном выше коде, я получаю это мерцание (на устройстве это еще хуже, но качество gif низкое).

введите описание изображения здесь

Есть идеи, как это исправить?

Ответ №1:

Похоже, что происходит некоторое вмешательство.

Я нашел два обходных решения, это зависит от желаемого эффекта.

Решение 1

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

 struct ContentView: View {
    
    @State var largeHeader = true
    
    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach(0..<3) { i in
                        Text("Content(i)")
                            .padding()
                    }
                }
                .background(GeometryReader { geometryProxy -> Color in
                    DispatchQueue.main.async {
                        largeHeader = geometryProxy.frame(in: .named("1")).minY >= 0
                    }
                    return Color.clear
                })
            }
            .coordinateSpace(name: "1")
            .offset(y: largeHeader ? 100 : 60)
            
            VStack {
                VStack {
                    Spacer()
                    Text("HEADER")
                        .padding()
                    Divider()
                }
                .frame(maxWidth: .infinity)
                .frame(height: largeHeader ? 140 : 100)
                .background(Color.white)
                
                Spacer()
            }
            .edgesIgnoringSafeArea(.all)
        }
        .animation(.default)
    }
}
 


Заголовок этого заголовка всегда будет изменять высоту, независимо от того, насколько велика высота содержимого.

Решение 2

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

Решение: определение высоты содержимого scrollview.

 struct ContentView: View {
    
    @State var largeHeader = true
    @State var scrollViewScrollable = false
    
    var body: some View {
        VStack {
            Text("HEADER").padding(.vertical, largeHeader ? 30 : 10)
            Divider()
            ScrollView {
                VStack {
                    ForEach(0..<3) { i in
                        Text("Content(i)")
                            .padding()
                    }
                }
                .background(GeometryReader { geometryProxy -> Color in
                    if scrollViewScrollable {
                        DispatchQueue.main.async {
                            largeHeader = geometryProxy.frame(in: .named("1")).minY >= 0
                        }
                    }
                    return Color.clear
                })
                .background(
                    GeometryReader { proxy in
                        Color.clear.onAppear {
                            scrollViewScrollable = proxy.frame(in: .named("1")).size.height >= UIScreen.main.bounds.size.height - 100
                        }
                    }
                )
            }
            .coordinateSpace(name: "1")
        }
        .animation(.default)
    }
}