繁体   English   中英

如何在 SwiftUI HStack 中隐藏剪辑视图

[英]How to hide clipped views in SwiftUI HStack

SwiftUI 中是否有可能在一行中只显示尽可能多的(固定大小)视图而不进行裁剪,例如裁剪视图的一部分。

示例布局

 ----------------------------------------- 
|                                         |
|  -------     -------     -------     -------     -------
| | VIEW1 |   | VIEW2 |   | VIEW3 |   | VIEW4 |   | VIEW5 |
|  -------     -------     -------     -------     -------
|                                         |
 -----------------------------------------

在这个例子VIEW5应该被隐藏,因为它完全超出了父视图的范围。 这可以通过.clipped()来完成。

VIEW4也应该完全隐藏,因为如果它会显示。 它必须被切断。 所有其他视图应正常呈现。

SwiftUI 布局

此初始尝试存在以下问题:

  • 由于VStack itemView所以前缘没有对齐 => ☑️解决方案
  • ❌最后一个视图可能是可见的,即使它没有完全显示在屏幕上。 应该避免这种情况。
struct DemoView: View {
    let items: [String]
    @State private var totalHeight = CGFloat.zero
    
    var body: some View {
        VStack(alignment: .leading) {
            Text("Demo View:")
            itemView
                .padding()
                .background(Color.green)
        }
    }
    
    private var itemView: some View {
        HStack {
            ForEach(items, id: \.self) { item in
                Text(item)
                    .lineLimit(1)
                    .fixedSize()
                    .padding(.all, 5)
                    .font(.body)
                    .background(Color.blue)
                    .foregroundColor(Color.white)
                    .cornerRadius(5)
            }
        }
        .frame(maxWidth: 350)
    }
}

#if DEBUG
struct DemoView_Previews: PreviewProvider {
    static var items: [String] = (2000..<2020).map(String.init)
//        .map { item in
//            Bool.random() ? item : item + item
//        }
    
    static var previews: some View {
        DemoView(items: items)
//            .previewLayout(.sizeThatFits)
    }
}
#endif

错误的布局示例

HStack框架上使用 alignment 剪辑(使用 Xcode 12.1 / iOS 14.1 测试)

private var itemView: some View {
    HStack {
        ForEach(items, id: \.self) { item in
            Text(item)
                .lineLimit(1)
                .fixedSize()
                .padding(.all, 5)
                .font(.body)
                .background(Color.blue)
                .foregroundColor(Color.white)
                .cornerRadius(5)
        }
    }
    .frame(maxWidth: 350, alignment: .leading)
    .clipped()
}

这是基于您的评论的解决方案。 VStack 与前导对齐,然后我添加了一个 ScrollView 来保存项目的 HStack。 此外,我为每个项目添加了一个 white.overlay,当项目完全移动到屏幕上时,不透明度将从 1.0(完全覆盖项目)变为 0.0(显示项目)。

另请注意,我将前导填充移动到 VStack 内,这样当您向左滚动时,项目会移动到屏幕边缘,而不是在填充所在的位置被截断。

struct DemoView: View {
    let items: [String]
    
    var body: some View {
        VStack(alignment: .leading) {
            Text("Demo View:")
                .padding(.leading)

            ScrollView(.horizontal, showsIndicators: false) {
                HStack {
                    ForEach(items, id: \.self) { item in
                        Text(item)
                            .lineLimit(1)
                            .fixedSize()
                            .padding(.all, 5)
                            .font(.body)
                            .background(Color.blue)
                            .foregroundColor(Color.white)
                            .cornerRadius(5)
                            .overlay(
                                GeometryReader { geometry in
                                    Color.white
                                        .opacity(
                                            geometry.frame(in: .global).maxX < UIScreen.main.bounds.width ? 0.0 : 1.0
                                        )
                                }
                            )
                    }
                }
                .background(Color.orange) // Starting HStack
                .padding(.leading)
                .background(Color.green) // Scrollable area
            }
        }
    }
    
}

struct DemoView_Previews: PreviewProvider {
    static var previews: some View {
        DemoView(items: [
            "ONE",
            "TWO",
            "THREE",
            "FOUR",
            "FIVE",
            "SIX",
            "SEVEN",
            "EIGHT",
            "NINE",
            "TEN",
        ])
    }
}

这是来自具有通用内容ViewBuildernicksamo的答案。 感谢您提供此解决方案::)

import SwiftUI

struct NonClippingView<Item: Hashable, Content: View>: View {
    let items: [Item]
    let isScrollingDisabled: Bool
    let backgroundColor: Color
    let content: (Item) -> Content

    init(items: [Item], isScrollingDisabled: Bool = false, backgroundColor: Color = .white, @ViewBuilder content: @escaping (Item) -> Content) {
        self.items = items
        self.isScrollingDisabled = isScrollingDisabled
        self.backgroundColor = backgroundColor
        self.content = content
    }
    
    var body: some View {
        GeometryReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack {
                    ForEach(items, id: \.self) { item in
                        content(item)
                            .overlay(
                                GeometryReader { geometry in
                                    backgroundColor
                                        .opacity(
                                            geometry.frame(in: .global).maxX < proxy.size.width ? 0.0 : 1.0
                                        )
                                }
                            )
                    }
                }
//                .background(Color.orange) // Starting HStack
                .padding(.leading)
//                .background(Color.green) // Scrollable area
            }
            .disabled(isScrollingDisabled)
        }
    }
    
}

struct NonClippingView_Previews: PreviewProvider {
    static var previews: some View {
        NonClippingView(items: [
            "ONE",
            "TWO",
            "THREE",
            "FOUR",
            "FIVE",
            "SIX",
            "SEVEN",
            "EIGHT",
            "NINE",
            "TEN",
        ], content: { item in
            VStack {
                Image(systemName: "tag")
                Text(item)
                    .lineLimit(1)
                    .fixedSize()
            }
            .padding(.all, 5)
            .font(.body)
            .background(Color.blue)
            .foregroundColor(Color.white)
            .cornerRadius(5)
        })
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM