首页 > 解决方案 > 在 SwiftUI 中滚动时隐藏视图的动画

问题描述

我正在尝试在滚动时隐藏自定义 SearchBar 的动画,尽管它可以是任何视图。就像 Instagram 在他们的发现页面和许多其他应用程序上所做的那样。我找到了一些关于如何做到这一点的资源: UiKit 部分解决方案SwiftUI 部分解决方案

我使用这些资源创建了两个代码示例,但它们并不能完全满足我的要求。

import SwiftUI


struct DiscoverView: View {
    
    @State var textList: [String] = [
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text"
    ]
    
    @State var initialOffset: CGFloat?
    @State var offset: CGFloat?
    
    var body: some View {
        NavigationView {
            VStack(alignment: .leading, spacing: 0) {
                RoundedRectangle(cornerRadius: 10)
                    .frame(maxHeight: CGFloat(50) - CGFloat((initialOffset ?? 0) / 5) + CGFloat((offset ?? 0) / 5))
                    .opacity(Double(1) - Double((initialOffset ?? 0) / 100) + Double((offset ?? 0) / 100))
                    .edgesIgnoringSafeArea(.all))
                ScrollView {
                    GeometryReader { geometry in
                        Color.clear.preference(key: OffsetKey.self, value: geometry.frame(in: .global).minY)
                            .frame(height: 0)
                    }
                    ForEach(textList, id: \.self) { text in
                        Text(text)
                            .font(.title)
                    }
                }
                
                Spacer()
            }
            .navigationBarHidden(true)
            .onPreferenceChange(OffsetKey.self) {
                if self.initialOffset == nil || self.initialOffset == 0 {
                    self.initialOffset = $0
                }
                
                self.offset = $0
            }
        }
    }
}


struct OffsetKey: PreferenceKey {
    static let defaultValue: CGFloat? = nil
    static func reduce(value: inout CGFloat?,
                       nextValue: () -> CGFloat?) {
        value = value ?? nextValue()
    }
}

这个动画是想要的,但如果用户停止滚动,我希望 SearchBar (RoundedRectangle) 完全显示或完全隐藏,现在它保持半显示(这意味着可能以编程方式滚动直到视图被隐藏/显示)

import SwiftUI

struct DiscoverView: View {
    
    @State var textList: [String] = [
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text",
        "Some text",
        "Another text"
    ]
    
    @State var initialOffset: CGFloat?
    @State var offset: CGFloat?
    @State var viewIsShown: Bool = true
    
    var body: some View {
        NavigationView {
            VStack(alignment: .leading, spacing: 0) {
                if viewIsShown {
                    RoundedRectangle(cornerRadius: 10)
                        .frame(maxHeight: 50)
//                        .opacity(Double(1) - Double((initialOffset ?? 0) / 100) + Double((offset ?? 0) / 100))
                        .transition(.asymmetric(insertion: .opacity , removal: .opacity))
                }
                ScrollView {
                    GeometryReader { geometry in
                        Color.clear.preference(key: OffsetKey.self, value: geometry.frame(in: .global).minY)
                            .frame(height: 0)
                    }
                    ForEach(textList, id: \.self) { text in
                        Text(text)
                            .font(.title)
                    }
                }
                
                Spacer()
            }
            .navigationBarHidden(true)
            .onPreferenceChange(OffsetKey.self) {
                if self.initialOffset == nil || self.initialOffset == 0 {
                    self.initialOffset = $0
                }
                
                self.offset = $0
                
                guard let initialOffset = self.initialOffset,
                      let offset = self.offset else {
                    return
                }
                
                
                if(initialOffset > offset){
                    withAnimation {
                        self.viewIsShown = false
                    }
                } else {
                    withAnimation {
                        self.viewIsShown = true
                    }
                }
            }
        }
    }
}

这是使用默认动画的另一种尝试,但我希望视图在滚动时动画并仅在用户停止滚动或滚动偏移使视图处于一致状态(隐藏/显示)时完全隐藏/显示,而不是应用在特定偏移处立即为视图设置动画的过渡。

标签: iosswiftswiftui

解决方案


推荐阅读