SwiftUI-淡出ScrollView [英] SwiftUI - fade out a ScrollView

查看:233
本文介绍了SwiftUI-淡出ScrollView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个生成的超大图表,将其放入ScrollView中,以便用户可以向右滚动并查看所有值.我想向用户指出,还有更多的东西要来".通过将ScrollView淡出在右侧.通过应用CAGradientLayer,Swift中的操作很容易.

I have a generated, oversized chart, which I put into a ScrollView so that the user can scroll to the right and see all values. I would like to indicate to the user that there's "more to come" on the right by fading the ScrollView out. Something in Swift was easy by applying CAGradientLayer.

我的方法是应用从透明(从80%开始)到系统背景颜色(从100%结束)渐变的叠加层.结果可以在所附的屏幕截图中看到.

My approach was to apply an overlay with a gradient from clear (starting at 80%) to system background color (ending at 100%). The result can be seen the attached screenshot.

问题编号. 1:看起来不像应该看起来的那样.

Issue no. 1: Does not look like it's supposed to look.

问题编号. 2:尽管将zIndex -1应用于叠加层,但ScrollView在叠加层应用后将不再滚动.

Issue no. 2: Despite applying zIndex of -1 to the overlay, the ScrollView won't scroll any longer as soon as an overlay is applied.

任何想法如何实现这一目标?谢谢!

Any idea how to achieve this? Thanks!

这是我的代码:

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问题是,它甚至不透明地通过覆盖传递一些手势.

Ok, it is known SwiftUI issue that it does not pass some gestures via overlays even transparent.

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

Here is possible approach to solve this - the idea is to have gradient to cover only small edge location, so other part of scroll view be accessed directly (yes, under gradient it will be still not draggable, but it is small part).

演示已准备好&在Xcode 11.7/iOS 13.7上进行了测试

Demo prepared & tested with Xcode 11.7 / iOS 13.7

(您的视图的简化变体)

(simplified variant of your view)

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)
        }
    }
}

这篇关于SwiftUI-淡出ScrollView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆