【问题标题】:SwiftUI - fade out a ScrollViewSwiftUI - 淡出 ScrollView
【发布时间】:2020-09-13 16:28:42
【问题描述】:

我有一个生成的超大图表,我将它放入一个 ScrollView 中,以便用户可以向右滚动并查看所有值。我想通过淡出 ScrollView 向用户表明右侧还有“更多内容”。通过应用 CAGradientLayer,Swift 中的某些事情变得简单。

我的方法是应用从透明(从 80% 开始)到系统背景颜色(以 100% 结束)渐变的叠加层。结果见附件截图。

问题编号。 1:看起来不像它应该的样子。

问题编号。 2:尽管应用了 -1 的 zIndex 到叠加层,但一旦应用了叠加层,ScrollView 就不会再滚动了。

知道如何实现这一目标吗?谢谢!

这是我的代码:

struct HealthExportPreview: View {
    @ObservedObject var carbsEntries: CarbsEntries
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack {
                ForEach(0..<self.carbsEntries.carbsRegime.count, id: \.self) { index in
                    ChartBar(carbsEntries: self.carbsEntries, entry: self.carbsEntries.carbsRegime[index], requiresTimeSplitting: index == self.carbsEntries.timeSplittingAfterIndex)
                }
            }
            .padding()
            .animation(.interactiveSpring())
        }
        .overlay(Rectangle()
            .fill(
                LinearGradient(gradient: Gradient(stops: [
                    .init(color: .clear, location: 0.8),
                    .init(color: Color(UIColor.systemBackground), location: 1.0)
                ]), startPoint: .leading, endPoint: .trailing)
            )
            .zIndex(-1)
        )
        .frame(height: CGFloat(carbsEntries.previewHeight + 80))
        .onAppear() {
            self.carbsEntries.fitCarbChartBars()
        }
    }
}

struct ChartBar: View {
    var carbsEntries: CarbsEntries
    var entry: (date: Date, carbs: Double)
    var requiresTimeSplitting: Bool
    
    static var timeStyle: DateFormatter {
        let formatter = DateFormatter()
        formatter.timeStyle = .short
        return formatter
    }
    
    var body: some View {
        VStack {
            Spacer()
            Text(FoodItemViewModel.doubleFormatter(numberOfDigits: entry.carbs >= 100 ? 0 : (entry.carbs >= 10 ? 1 : 2)).string(from: NSNumber(value: entry.carbs))!)
                .font(.footnote)
                .rotationEffect(.degrees(-90))
                .offset(y: self.carbsEntries.appliedMultiplier * entry.carbs <= 40 ? 0 : 40)
                .zIndex(1)
            
            if entry.carbs <= self.carbsEntries.maxCarbsWithoutSplitting {
                Rectangle()
                    .fill(Color.green)
                    .frame(width: 15, height: CGFloat(self.carbsEntries.appliedMultiplier * entry.carbs))
            } else {
                Rectangle()
                    .fill(Color.green)
                    .frame(width: 15, height: CGFloat(self.carbsEntries.getSplitBarHeight(carbs: entry.carbs)))
                    .overlay(Rectangle()
                        .fill(Color(UIColor.systemBackground))
                        .frame(width: 20, height: 5)
                        .padding([.bottom, .top], 1.0)
                        .background(Color.primary)
                        .rotationEffect(.degrees(-10))
                        .offset(y: CGFloat(self.carbsEntries.getSplitBarHeight(carbs: entry.carbs) / 2 - 10))
                )
            }
            
            if self.requiresTimeSplitting {
                Rectangle()
                    .fill(Color(UIColor.systemBackground))
                    .frame(width: 40, height: 0)
                    .padding([.top], 2.0)
                    .background(Color.primary)
                    .overlay(Rectangle()
                        .fill(Color(UIColor.systemBackground))
                        .frame(width: 20, height: 5)
                        .padding([.bottom, .top], 1.0)
                        .background(Color.black)
                        .rotationEffect(.degrees(80))
                        .offset(x: 20)
                        .zIndex(1)
                    )
            } else {
                Rectangle()
                    .fill(Color(UIColor.systemBackground))
                    .frame(width: 40, height: 0)
                    .padding([.top], 2.0)
                    .background(Color.primary)
            }
            
            Text(ChartBar.timeStyle.string(from: entry.date))
                .fixedSize()
                .layoutPriority(1)
                .font(.footnote)
                .rotationEffect(.degrees(-90))
                .offset(y: 10)
                .frame(height: 50)
                .lineLimit(1)
        }.frame(width: 30)
    }
}

【问题讨论】:

    标签: swiftui scrollview gradient


    【解决方案1】:

    使用 alpha .mask 怎么样?

    您可以像这样使用 alpha 掩码来代替您的 .overlay:

    .mask(
        HStack(spacing: 0) {
    
            // Left gradient
            LinearGradient(gradient: 
               Gradient(
                   colors: [Color.black.opacity(0), Color.black]), 
                   startPoint: .leading, endPoint: .trailing
               )
               .frame(width: 50)
    
            // Middle
            Rectangle().fill(Color.black)
    
            // Right gradient
            LinearGradient(gradient: 
               Gradient(
                   colors: [Color.black, Color.black.opacity(0)]), 
                   startPoint: .leading, endPoint: .trailing
               )
               .frame(width: 50)
        }
     )
    

    【讨论】:

      【解决方案2】:

      好的,已知 SwiftUI 问题是它不会通过覆盖传递一些手势,甚至是透明的。

      这是解决这个问题的可能方法 - 想法是让渐变仅覆盖小的边缘位置,因此可以直接访问滚动视图的其他部分(是的,在渐变下它仍然不可拖动,但它是一小部分)。

      使用 Xcode 11.7 / iOS 13.7 准备和测试的演示

      (您的视图的简化变体)

      struct HealthExportPreview: View {
          var body: some View {
              GeometryReader { gp in
                  ZStack {
                      ScrollView(.horizontal) {
                          HStack {
                             // simplified content
                              ForEach(0..<20, id: \.self) { index in
                                  Rectangle().fill(Color.red)
                                      .frame(width: 40, height: 80)
                              }
                          }
                          .padding()
                          .animation(.interactiveSpring())
                      }
      
                      // inject gradient at right side only
                      Rectangle()
                          .fill(
                              LinearGradient(gradient: Gradient(stops: [
                                  .init(color: Color(UIColor.systemBackground).opacity(0.01), location: 0),
                                  .init(color: Color(UIColor.systemBackground), location: 1)
                              ]), startPoint: .leading, endPoint: .trailing)
                          ).frame(width: 0.2 * gp.size.width)
                          .frame(maxWidth: .infinity, alignment: .trailing)
                  }.fixedSize(horizontal: false, vertical: true)
              }
          }
      }
      

      【讨论】:

      • 使用 .allowsHitTesting(false) 可以使其可拖动
      • @RayXu 不幸的是它不适用于 iOS
      猜你喜欢
      • 2022-11-22
      • 2023-04-10
      • 1970-01-01
      • 2020-10-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2021-05-31
      • 2020-06-26
      相关资源
      最近更新 更多