【问题标题】:How to stop strobing with fast moving view in SwiftUI如何在 SwiftUI 中停止快速移动视图的频闪
【发布时间】:2021-01-30 07:34:22
【问题描述】:

我正在尝试在 SwiftUI 中创建一个滑块(与原生 Slider 风格相同,但最终具有更多功能)。在下面的 gif 中,我的滑块位于顶部,SwiftUI 的原生滑块位于底部,两者的范围和值都相同。当我拖动原生滑块时,我的视图会更新,但是当我快速前后拖动时,圆圈会闪烁。请问有没有办法防止这种频闪? gif 不是很清楚,所以我提供了重现的代码。任何指向正确方向的指针表示赞赏!

这是我的滑块的代码:

struct BarSlider: View {

    @Binding var value: CGFloat
    var range: ClosedRange<CGFloat>
    
    var body: some View {

        GeometryReader { geo in
            ZStack {
                HStack(spacing: 0) {
                    Rectangle()
                        .fill(Color.red)
                        .frame(width: $value.wrappedValue.map(from: range, to: 0...geo.size.width))
                    Rectangle()
                        .fill(Color.green)
                }
                HStack {
                    Circle()
                        .frame(width: 30, height: 30)
                        .offset(x: valueForKnob(geometry: geo))
                    Spacer()
                }
            }
            .background(Color.blue)
        }
    }

    private func valueForKnob(geometry: GeometryProxy) -> CGFloat {
        $value.wrappedValue.map(from: range, to: 0...geometry.size.width) - (30 * valueAsPercent())
    }

    private func valueAsPercent() -> CGFloat {
        let percent = ((value - range.lowerBound) / (range.upperBound - range.lowerBound) * 100) / 100
        return percent.clamped(to: 0...1)
    }
}

这是两个滑块:

struct ContentView: View {
    @State private var amount = CGFloat(100)

    var body: some View {
        VStack {
            BarSlider(value: $amount, range: 100...1000)
                .frame(height: 100)
            Slider(value: $amount, in: 100...1000)
        }
    }
}

还有这些扩展:

extension CGFloat {
    func map(from: ClosedRange<CGFloat>, to: ClosedRange<CGFloat>) -> CGFloat {

        let value = self.clamped(to: from)

        let fromRange = from.upperBound - from.lowerBound
        let toRange = to.upperBound - to.lowerBound
        let result = (((value - from.lowerBound) / fromRange) * toRange) + to.lowerBound
        return result
    }
}

extension Comparable {
    func clamped(to limits: ClosedRange<Self>) -> Self {
        return min(max(self, limits.lowerBound), limits.upperBound)
    }
}

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    我认为你的圈子不在中心

    请测试一下:

    struct BarSlider: View {
    
        @Binding var value: CGFloat
        var range: ClosedRange<CGFloat>
        
        private let circleSize:CGFloat = 20
        
        var body: some View {
    
            GeometryReader { geo in
                VStack {
                    Text("\(value)")
                    ZStack (alignment: Alignment(horizontal: .leading, vertical: .center)){
                        HStack(spacing: 0) {
                            Rectangle()
                                .fill(Color.red)
                                .frame(width: $value.wrappedValue.map(from: range, to: 0...geo.size.width))
                                
                            Rectangle()
                                .fill(Color.green)
                        }
                        
                        HStack {
                            Circle()
                                .frame(width: circleSize, height: circleSize)
                                .offset(x: valueForKnob(geometry: geo) - circleSize/2)
                           
                        }
                    }
                }
                
            }
        }
    
        private func valueForKnob(geometry: GeometryProxy) -> CGFloat {
            $value.wrappedValue.map(from: range, to: 0...geometry.size.width )
        }
    
        private func valueAsPercent() -> CGFloat {
            let percent = ((value - range.lowerBound) / (range.upperBound - range.lowerBound) * 100) / 100
            return percent.clamped(to: 0...1)
        }
    }
    extension CGFloat {
        func map(from: ClosedRange<CGFloat>, to: ClosedRange<CGFloat>) -> CGFloat {
    
            let value = self.clamped(to: from)
    
            let fromRange = from.upperBound - from.lowerBound
            let toRange = to.upperBound - to.lowerBound
            let result = (((value - from.lowerBound) / fromRange) * toRange) + to.lowerBound
            return result
        }
    }
    extension Comparable {
        func clamped(to r: ClosedRange<Self>) -> Self {
            let min = r.lowerBound, max = r.upperBound
            return self < min ? min : (max < self ? max : self)
        }
    }
    
    
    struct ContentView: View {
        @State private var amount = CGFloat(100)
    
        var body: some View {
            VStack {
                BarSlider(value: $amount, range: 100...1000)
                    .frame(height: 70)
                    .padding()
                Spacer()
                    .frame(height: 70)
                Slider(value: $amount, in: 100...1000)
                    .padding()
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    【讨论】:

    • 感谢您查看,但这是故意的。如果你从头拖到尾,我希望圆圈始终完全在视图内。因此,当它处于 0% 时,我希望圆形前缘与视图前缘对齐,而当它处于 100% 时,我希望圆形后缘与视图后缘对齐。所以圆圈并不完全代表真实的位置,但它可以防止任何悬挂在视图之外。我希望这是有道理的。
    • 好的,理解您的要求,但是如果您想要这种行为,您可以简单地使指针大于矩形。我认为原来的苹果滑块是一样的,指针比线大,你看不到这种行为
    猜你喜欢
    • 2015-10-18
    • 2016-12-16
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 2016-12-15
    • 2019-10-13
    • 2011-04-10
    • 1970-01-01
    相关资源
    最近更新 更多