使用 header UICollectionViewFlowLayout 时的简单 Swift Ui View 错误

问题描述 投票:0回答:1

您好,我正在制作一个简单的视图,其内容位于滚动视图和顶部标题中。当用户向下滚动时我想隐藏标题,当用户向上滚动时我想显示它。我有三个不同的选项卡,如果我在它们之间手动滑动,一切都会正常。如果我尝试单击标题中的按钮来切换选项卡,滚动视图会稍微调整滚动视图并且不执行任何操作,然后我会收到下面的大量错误。我已经为此工作了几个小时,但没有运气,我感谢任何帮助,谢谢。

UICollectionViewFlowLayout 的行为未定义,因为:项目高度必须小于 UICollectionView 的高度减去部分插入顶部和底部值,减去内容插入顶部和底部值。相关的 UICollectionViewFlowLayout 实例是并且它附加到 ;背景颜色 = UIExtendedGrayColorSpace 0 0;层=;内容偏移: {0.33333333333333331, 0};内容大小: {1170, 763};调整内容插入: {47, 0, 34, 0};布局:在 UICollectionViewFlowLayoutBreakForInvalidSizes 处设置一个符号断点以在调试器中捕获此断点。

import SwiftUI

struct FeedViewSec: View {
    @Environment(\.colorScheme) var colorScheme
    @State private var selection = 0
    @State var headerHeight: CGFloat = 130
    @State var headerOffset: CGFloat = 0
    @State var lastHeaderOffset: CGFloat = 0
    @State var direction: SwipeDirection = .none
    @State var shiftOffset: CGFloat = 0
    
    var body: some View {
        ZStack(alignment: .bottomTrailing){
            TabView(selection: $selection) {
                scrollBody().tag(0)
                scrollBody().tag(1)
                scrollBody().tag(2)
            }.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
        }
        .overlay(alignment: .top) {
            headerView().offset(y: -headerOffset < headerHeight ? headerOffset : (headerOffset < 0 ? headerOffset : 0))
        }
        .edgesIgnoringSafeArea(.top)
    }
    func scrollBody() -> some View {
        ScrollView {
            LazyVStack {
                Color.clear.frame(height: 130)
                ForEach(0..<30){ i in
                    RoundedRectangle(cornerRadius: 15).frame(width: 100, height: 100)
                }
            }
            .offsetY { previous, current in
                if previous > current {
                    if direction != .up && current < 0{
                        shiftOffset = current - headerOffset
                        direction = .up
                        lastHeaderOffset = headerOffset
                    }
                    
                    let offset = current < 0 ? (current - shiftOffset) : 0

                    headerOffset = (-offset < headerHeight ? (offset < 0 ? offset : 0) : -headerHeight * 2.0)
                } else {
                    if direction != .down{
                        shiftOffset = current
                        direction = .down
                        lastHeaderOffset = headerOffset
                    }
                    
                    let offset = lastHeaderOffset + (current - shiftOffset)
                    headerOffset = (offset > 0 ? 0 : offset)
                }
            }
        }.coordinateSpace(name: "SCROLL")
    }
    func headerView() -> some View {
        VStack(spacing: 0){
            HStack {
                HStack(spacing: 1){
                    Text("Explore").font(.title).bold()
                    Image(systemName: "chevron.down").font(.body).bold()
                }
                Spacer()
            }
            .padding(.leading)
            HStack(alignment: .center, spacing: 0) {
                Button {
                    withAnimation(.easeInOut){
                        selection = 0
                    }
                } label: {
                    Text("New")
                        .foregroundColor(.black).bold()
                        .frame(width: 80, height: 25)
                }
                .background((selection == 0) ? colorScheme == .dark ? .gray.opacity(0.3) : .gray : colorScheme == .dark ? .gray : .gray.opacity(0.3))
                Button {
                    withAnimation(.easeInOut){
                        selection = 1
                    }
                } label: {
                    Text("LeaderBoard")
                        .foregroundColor(.black).bold()
                        .frame(width: 120, height: 25)
                }
                .background((selection == 1) ? colorScheme == .dark ? .gray.opacity(0.3) : .gray : colorScheme == .dark ? .gray : .gray.opacity(0.3))
                Button {
                    withAnimation(.easeInOut){
                        selection = 2
                    }
                } label: {
                    Text("Hot")
                        .foregroundColor(.black).bold()
                        .frame(width: 80, height: 25)
                }
                .background((selection == 2) ? colorScheme == .dark ? .gray.opacity(0.3) : .gray : colorScheme == .dark ? .gray : .gray.opacity(0.3))
            }
            .mask {
                RoundedRectangle(cornerRadius: 5)
            }
            .padding(.top, 8)
            Color.clear.frame(height: 13)
        }
        .padding(.top, top_Inset())
        .background(.ultraThinMaterial)
    }
}

func top_Inset() -> CGFloat {
    let scenes = UIApplication.shared.connectedScenes
    let windowScene = scenes.first as? UIWindowScene
    let window = windowScene?.windows.first
    
    return window?.safeAreaInsets.top ?? 0
}
extension View{
    @ViewBuilder
    func offsetY(completion: @escaping (CGFloat,CGFloat)->())->some View{
        self.modifier(OffsetHelper(onChange: completion))
    }
    func safeArea()->UIEdgeInsets{
        guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else{return .zero}
        guard let safeArea = scene.windows.first?.safeAreaInsets else{return .zero}
        return safeArea
    }
}
struct OffsetHelper: ViewModifier{
    var onChange: (CGFloat,CGFloat)->()
    @State var currentOffset: CGFloat = 0
    @State var previousOffset: CGFloat = 0
    
    func body(content: Content) -> some View {
        content
            .overlay {
                GeometryReader{proxy in
                    let minY = proxy.frame(in: .named("SCROLL")).minY
                    Color.clear
                        .preference(key: OffsetKeyNew.self, value: minY)
                        .onPreferenceChange(OffsetKeyNew.self) { value in
                            previousOffset = currentOffset
                            currentOffset = value
                            onChange(previousOffset,currentOffset)
                        }
                }
            }
    }
}
struct OffsetKeyNew: PreferenceKey{
    static var defaultValue: CGFloat = 0
    
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}
struct HeaderBoundsKey: PreferenceKey{
    static var defaultValue: Anchor<CGRect>?
    
    static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) {
        value = nextValue()
    }
}
enum SwipeDirection{
    case up
    case down
    case none
}
ios swift swiftui view header
1个回答
0
投票

您有一个来自

UICollectionViewFlowLayout
的错误,该错误与集合视图中项目的高度有关。
但是,您使用 SwiftUI 作为界面,而不是 UIKit
UICollectionView
。该错误应该意味着集合视图中的项目高度对于可用空间来说太高。这可能与 SwiftUI 如何在底层桥接到 UIKit 相关,或者如何在 SwiftUI 的
UIScrollView
中管理 UIKit 的
ScrollView
相关。

您正在尝试使用偏移、首选项和几何读取器通过相当复杂的设置来管理标题的可见性和选项卡选择。该设置可能会使调试或识别实际问题变得困难。

我建议首先简化布局:暂时删除标题并查看错误是否仍然存在。这可以帮助确定标头是否是问题的原因。您可能希望将布局简化到最低限度,以查看错误是否仍然发生,然后逐渐重新引入元素以找出原因。

然后检查布局计算:验证所有偏移和高度计算,尤其是在操作

headerOffset
shiftOffset
的地方,以确保它们产生预期值。

您可能需要添加一些打印语句或使用调试器在运行时检查偏移量和其他变量的值,以查看是否发生任何意外情况。错误消息建议在

UICollectionViewFlowLayoutBreakForInvalidSizes
设置符号断点以在调试器中捕获此错误。这可能会让您更深入地了解这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.