如何在 SwiftUI 中停止快速移动视图的频闪
Posted
技术标签:
【中文标题】如何在 SwiftUI 中停止快速移动视图的频闪【英文标题】:How to stop strobing with fast moving view in 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)
【问题讨论】:
【参考方案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% 时,我希望圆形后缘与视图后缘对齐。所以圆圈并不完全代表真实的位置,但它可以防止任何悬挂在视图之外。我希望这是有道理的。 好的,理解您的要求,但是如果您想要这种行为,您可以简单地使指针大于矩形。我认为原来的苹果滑块是一样的,指针比线大,你看不到这种行为以上是关于如何在 SwiftUI 中停止快速移动视图的频闪的主要内容,如果未能解决你的问题,请参考以下文章
如何滚动滚动视图以使 TextField 在 SwiftUI 中移动到键盘上方?
SwiftUI 如何快速识别视图(View)界面的刷新是由哪个状态的改变导致的?
SwiftUI 如何快速识别视图(View)界面的刷新是由哪个状态的改变导致的?