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

scrollview - How to Prevent Scrolling Lag scroll UI like Apple Weather in SwiftUI? - Stack Overflow

programmeradmin0浏览0评论

I’m working on a major update for my app and I’m aiming to improve the design to match premium standards, similar to Apple’s design approach. The app I’m working on is SignDict, which displays both ASL and JSL. To elevate the UI, I was getting bored with using Apple's original TableView or Scroll view, with new design inspired by VisionOS style.

While brainstorming, I came across the Apple Weather app show great scroll view UI, which features a smooth scrolling experience where it's good for under the AIUEO collection view on my app. However, I’m encountering an issue where the scroll view seems to lag when I swipe quickly like this 2 screenshots...

, In the last screenshot, I noticed that the slide with slowly down or up make prefect on top without nagged, but super little lag. This is exactly what I wanted, but I’m not sure how to prevent it from happening. Can you help me with that?

A friend of mine suggested that the issue might be related to using GeometryReader, but I’m still learning how to work with it effectively. I’ve followed tutorials and tried various solutions, but the problem persists.

If anyone has experience with this or could point me in the right direction, I would greatly appreciate your help. Here’s the full code I’m currently using:

struct CustomCorner: Shape {
    
    var corners: UIRectCorner
    var radius: CGFloat
    
    func path( in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        
        return Path(path.cgPath)
        
    }
}



struct CustomStackView < Title: View, Content: View>: View {
    
    @State var topOffset: CGFloat = 0
    @State var bottomOffset: CGFloat = 0
    
    var titleView: Title
    var contentView: Content
    
    init(@ViewBuilder titleView: @escaping ()->Title, @ViewBuilder contentView: @escaping () -> Content) {
        
        self.titleView = titleView()
        self.contentView = contentView()
        
    }
    
    
    var body: some View {
        
        
        VStack(spacing: 0) {
            
            titleView
                .font(.callout)
                .lineLimit(1)
                .frame(height: 38)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(.leading)
                .background(.ultraThinMaterial, in: CustomCorner(corners: bottomOffset < 37 ? .allCorners : [.topLeft, .topRight], radius: 12))
                .zIndex(1)
            
            VStack {
                
                Divider()
                
                contentView
                    .padding()
                
                
            }
            .background(.ultraThinMaterial, in: CustomCorner(corners: [.bottomLeft, .bottomRight], radius: 12))
            // Moving Content upward
            .offset(y: topOffset >= 165 ? 0 : -(-topOffset + 165))
            .zIndex(0)
            .clipped()
            .opacity(getOpacity())
            
        }
        .offset(y: topOffset >= 165 ? 0 : -topOffset + 165)
        .opacity(getOpacity())
        
        .background(
            
            GeometryReader{proxy -> Color in
                
                let minY = proxy.frame(in: .global).minY
                let maxY = proxy.frame(in: .global).maxY
                
                DispatchQueue.main.async {
                    self.topOffset = minY
                    self.bottomOffset = maxY - 165
                    
                }
                
                return Color.clear
                
                
            }
            
        )
        .modifier(CornerModifer(bottomOffset: $bottomOffset))
    }
    
    func getOpacity()->CGFloat {
        
        if bottomOffset < 38 {
            
            let progress = bottomOffset / 28
            
            return progress
            
        }
        
        return 1
    }
    
}



struct CornerModifer: ViewModifier {
    
    @Binding var bottomOffset: CGFloat
    
    func body(content: Content) -> some View {
        
        if bottomOffset < 37 {
            content
        } else {
            content.cornerRadius(12)
        }
    }
    
    
}

this code to custom scroll view like Weather Scroll View... now I put a last one code for to display it right here:

struct RedesignList: View {
    
    @State private var scrollOffset: CGFloat = 0
    
    @State private var searchText = ""
    @State private var selectedHiragana: String = "あ" // Default to "あ" on app launch
    
    // color here
    
    let headerView: [HeaderData] = [
        // MARK: Vowels
        HeaderData(title: "あ", color: Color(uiColor: UIColor.systemMint)),
        HeaderData(title: "い", color: Color(uiColor: UIColor.systemMint)),
        
    ].sorted { $0.title < $1.title }
    
    @State private var isJapaneseSheetPresented = false
    @State private var isEnglishSheetPresented = false
    @State private var selectedItem: (HeaderTitle: String, HiraganaText: String, KanjiText: String, japImage: String, engTitle: String, engImage: String)?
    
    var listOfLanguage: [(HeaderTitle: String, HiraganaText: String, KanjiText: String, japImage: String, engTitle: String, engImage: String)] = [
        (HeaderTitle: "あ", HiraganaText: "あか", KanjiText: "", japImage: "あか", engTitle: "Red (Color)", engImage: "Red"),
        (HeaderTitle: "あ", HiraganaText: "あかちやん", KanjiText: "赤ちやん", japImage: "あかちやん", engTitle: "Baby", engImage: "Baby"),
        
        (HeaderTitle: "あ", HiraganaText: "あか", KanjiText: "", japImage: "あか", engTitle: "Red (Color)", engImage: "Red"),
        (HeaderTitle: "あ", HiraganaText: "あかちやん", KanjiText: "赤ちやん", japImage: "あかちやん", engTitle: "Baby", engImage: "Baby"),
        
        (HeaderTitle: "あ", HiraganaText: "あか", KanjiText: "", japImage: "あか", engTitle: "Red (Color)", engImage: "Red"),
        (HeaderTitle: "あ", HiraganaText: "あかちやん", KanjiText: "赤ちやん", japImage: "あかちやん", engTitle: "Baby", engImage: "Baby"),
        
        (HeaderTitle: "あ", HiraganaText: "あか", KanjiText: "", japImage: "あか", engTitle: "Red (Color)", engImage: "Red"),
        (HeaderTitle: "あ", HiraganaText: "あかちやん", KanjiText: "赤ちやん", japImage: "あかちやん", engTitle: "Baby", engImage: "Baby"),
        
        
    ]
    
    var filteredList: [(HeaderTitle: String, HiraganaText: String, KanjiText: String, japImage: String, engTitle: String, engImage: String)] {
        if searchText.isEmpty {
            return listOfLanguage.filter { $0.HeaderTitle == selectedHiragana }
        } else {
            return listOfLanguage.filter {
                $0.engTitle.localizedCaseInsensitiveContains(searchText) ||
                $0.HiraganaText.localizedCaseInsensitiveContains(searchText) ||
                $0.KanjiText.localizedCaseInsensitiveContains(searchText)
            }
        }
    }
    
    @State private var scrolloffset: CGFloat = 0
    @State private var topInset: CGFloat = 0
    
    
    
    
    var body: some View {
        NavigationStack {
            
            
            ZStack {
                
                
                VStack {
                    // Fetch the selected HeaderData color dynamically
                    let selectedColor = headerView.first { $0.title == selectedHiragana }?.color ?? .clear
                    
                    // blur here...
                    LinearGradient(gradient: Gradient(stops: [
                        .init(color: selectedColor, location: 0.005), // Use selected color
                        .init(color: .clear, location: 0.20),
                    ]), startPoint: .top, endPoint: .bottom)
                    .ignoresSafeArea()
                }
                .blur(radius: 50)
                
                
                ScrollView(.vertical, showsIndicators: true) {
                    VStack {
                        
                        // Collection あいうえお
                        VStack {
                            
                            ScrollView(.horizontal, showsIndicators: false) {
                                HStack(spacing: 10) {
                                    ForEach(headerView, id: \.title) { header in
                                        if listOfLanguage.contains(where: { $0.HeaderTitle == header.title }) { // Changed filter logic
                                            Text(header.title)
                                                .font(Font.system(size: 20, weight: .medium))
                                                .frame(width: 50, height: 50)
                                                .background(header.color.opacity(selectedHiragana == header.title ? 0.7 : 0.3))
                                                .clipShape(RoundedRectangle(cornerRadius: 10))
                                                .overlay(
                                                    RoundedRectangle(cornerRadius: 10)
                                                        .stroke(Color.gray.opacity(0.5), lineWidth: 1)
                                                )
                                                .onTapGesture {
                                                    withAnimation {
                                                        selectedHiragana = header.title
                                                    }
                                                }
                                        }
                                    }
                                }
                                .padding(.horizontal)
                                .frame(height: 52)
                            }
                            .padding(.top, 6)
                            .offset(y: scrolloffset > 0 ? scrolloffset : 0)
                        }
                        
                        
                        
                        
                        // Table View
                        VStack {
                            ForEach(filteredList, id: \.HiraganaText) { item in
                                VStack(spacing: 12) {
                                    HStack {
                                        CustomStackView {
                                            Label {
                                                HStack {
                                                    Text(item.HiraganaText)
                                                        .padding(.leading, -17)
                                                    Spacer()
                                                    Text(item.KanjiText)
                                                        .foregroundStyle(.gray)
                                                }
                                                .padding()
                                            } icon: {}
                                        } contentView: {
                                            Image(item.japImage)
                                                .resizable()
                                                .scaledToFit()
                                                .clipped()
                                                .aspectRatio(contentMode: .fill)
                                                .frame(width: 150, height: 80)
                                                .padding(.top, -8)
                                                .edgesIgnoringSafeArea(.all)
                                                .onTapGesture {
                                                    selectedItem = item
                                                    isJapaneseSheetPresented.toggle()
                                                }
                                        }
                                        
                                        CustomStackView {
                                            Label {
                                                let splitTitle = extractTitleParts(from: item.engTitle)
                                                HStack {
                                                    Text(splitTitle.0)
                                                        .font(.system(size: 16))
                                                        .padding(.leading, -10)
                                                    Spacer()
                                                    if !splitTitle.1.isEmpty {
                                                        Text(splitTitle.1)
                                                            .foregroundColor(.secondary)
                                                            .padding(.trailing, 3)
                                                    }
                                                }
                                                
                                                .frame(maxWidth: .infinity)
                                                .padding(.horizontal, 15)
                                                
                                            } icon: {}
                                        } contentView: {
                                            VStack {
                                                Image(item.engImage)
                                                    .resizable()
                                                    .scaledToFit()
                                                    .clipped()
                                                    .aspectRatio(contentMode: .fill)
                                                    .frame(width: 150, height: 80)
                                                    .padding(.top, -8)
                                                    .edgesIgnoringSafeArea(.all)
                                                    .onTapGesture {
                                                        selectedItem = item
                                                        isEnglishSheetPresented.toggle()
                                                    }
                                            }
                                        }
                                    }
                                }
                                .padding(.horizontal, 15)
                            }
                            
                        }
                    }
                }
                
                .onScrollGeometryChange(for: CGFloat.self, of: {
                    $0.contentOffset.y + $0.contentInsets.top
                }, action: { _, newValue in
                    scrolloffset = newValue
                })
                .onScrollGeometryChange(for: CGFloat.self, of: {
                    $0.contentInsets.top
                }, action: { _, newValue in
                    topInset = newValue
                })
                .background(GeometryReader { geometry in
                    Color.clear
                        .onAppear {
                            scrollOffset = geometry.frame(in: .global).minY
                        }
                })
                
                .navigationTitle("Dictionary")
                .navigationBarTitleDisplayMode(.automatic)
                .searchable(text: $searchText)
             
                .toolbarBackground(.clear, for: .navigationBar) // Makes the nav bar background transparent
                .toolbarBackground(.hidden, for: .navigationBar)
                
                
            }
        }
        
    }
    
    
    // English
    private func extractTitleParts(from title: String) -> (String, String) {
        if let range = title.range(of: #"\((.*?)\)"#, options: .regularExpression) {
            let mainTitle = title[..<range.lowerBound].trimmingCharacters(in: .whitespaces)
            let insideParentheses = title[range].trimmingCharacters(in: ["(", ")"])
            return (mainTitle, insideParentheses)
        }
        return (title, "") // No parentheses found
    }
    
    
}

private let leftOffset: CGFloat = 0.1

DONE!

Please give me any issues you found I can make my app to improved before launch V2.0! Thanks!

发布评论

评论列表(0)

  1. 暂无评论