我正在努力实现
this
SwiftUI中的某种功能。基本想法是我有一个滑块和一个
ScrollView
,移动滑块可以滚动
卷轴视图
内容(不一定是上述示例中所示的视频,它可以是任何视图)。
当然
ScrollViewReader
不包裹
UIScrollView.setContentOffset
并且仅提供
scrollTo(_ id: Hashable [, anchor: UnitPoint])
,这并不能让我随心所欲地控制偏移量。
在我看来
Slider
将生成一个介于0和1之间的数字,然后使用
lerp
从
滑块
的绑定,其中
lerp
可能如下:
extension Range where Bound: BinaryFloatingPoint {
func lerp(_ t: Bound) -> Bound {
return (1-t)*self.lowerBound + t*self.upperBound
}
}
extension ClosedRange where Bound: BinaryFloatingPoint {
func lerp(_ t: Bound) -> Bound {
return (1-t)*self.lowerBound + t*self.upperBound
}
}
所以我试图绕过
ScrollViewReader
尽量避免
Introspect
和
UIViewRepresentable
/
UIViewControllerRepresentable
,其流程如下:
-
向ScrollView的内容添加覆盖,覆盖整个大小
frame(maxWidth: .infinity, maxHeight: .infinity)
并且是
.leading
对齐。
-
覆盖将包含一个简单的
Rectangle()
具有
.clear
背景和
.contentShape(Rectangle())
,其相对于
主要的
的锚
卷轴视图
的内容使用
@State private var offsetAnchor: CGFloat
变量,最初
.zero
以及分配给它的静态id,例如
.id("scrollAnchor")
。
-
当我想移动
卷轴视图
到给定的偏移量,我首先设置
offsetAnchor
到所需的偏移量,然后调用
scrollProxy.scrollTo(scrollAnchor)
。
不幸的是,解决方法没有起作用,它似乎滚动到随机偏移(这并不意味着它实际上是随机的,只是我不知道标准)。以下是我的想法的一个非常基本的实现
struct ContentView: View {
@State private var inputValue: String = ""
@State private var contentOffset: CGFloat = .zero {
didSet {
self.updateOffset("scrollAnchor")
}
}
@State private var updateOffset: (String) -> Void = { _ in }
var body: some View {
VStack {
ScrollViewReader { scrollProxy in
ScrollView(.horizontal, showsIndicators: true) {
Rectangle()
.fill(Colors.teal500)
.frame(width: 1000, height: 100)
.overlay(
ZStack {
ForEach(1..<40) { i in
VStack(alignment: .leading) {
Rectangle()
.fill(Colors.white)
.frame(width: 1, height: 10)
Text("\(i*25)")
.font(.caption)
}
.offset(x: CGFloat(i*25))
}
}
.fillMaxWidth(alignment: .leading)
,alignment: .topLeading)
.overlay(
ZStack {
Rectangle()
.fill(Color.red)
.contentShape(Rectangle())
.frame(width: 1, height: 100)
.position(x: self.contentOffset, y: 50)
.onAppear {
self.updateOffset = { id in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
scrollProxy.scrollTo(id)
}
}
}
.id("scrollAnchor")
}
.fillMaxWidth(alignment: .leading)
, alignment: .leading
)
}
}
TextField(
"Scroll offset:",
text: self.$inputValue
)
.keyboardType(.decimalPad)
Button(action: {
self.contentOffset = CGFloat(Int(inputValue) ?? 0)
}) {
Text("Scroll to input offset")
}
}
.fillMaxSize(alignment: .leading)
}
}
哪里
Colors.teal500
是
#319795
颜色,以及
fillMaxWidth()
/
fillMaxHeight()
/
fillMaxSize()
实现方式如下:
struct FillMaxSize: ViewModifier {
private var alignment: Alignment?
init(alignment: Alignment?) {
self.alignment = alignment
}
func body(content: Content) -> some View {
if self.alignment != nil {
return content
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: self.alignment!)
} else {
return content
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}
}
struct FillMaxWidth: ViewModifier {
private var alignment: Alignment?
init(alignment: Alignment?) {
self.alignment = alignment
}
func body(content: Content) -> some View {
if self.alignment != nil {
return content
.frame(minWidth: 0, maxWidth: .infinity, alignment: self.alignment!)
} else {
return content
.frame(minWidth: 0, maxWidth: .infinity)
}
}
}
struct FillMaxHeight: ViewModifier {
private var alignment: Alignment?
init(alignment: Alignment?) {
self.alignment = alignment
}
func body(content: Content) -> some View {
if self.alignment != nil {
return content
.frame(minHeight: 0, maxHeight: .infinity, alignment: self.alignment!)
} else {
return content
.frame(minHeight: 0, maxHeight: .infinity)
}
}
}
extension View {
func fillMaxSize(alignment: Alignment? = nil) -> some View {
return self.modifier(FillMaxSize(alignment: alignment))
}
func fillMaxWidth(alignment: Alignment? = nil) -> some View {
return self.modifier(FillMaxWidth(alignment: alignment))
}
func fillMaxHeight(alignment: Alignment? = nil) -> some View {
return self.modifier(FillMaxHeight(alignment: alignment))
}
}
我需要帮助来纠正我的实现中的错误(我怀疑
scrollTo
可能会在“scrollAnchor”移动到新位置之前调用,但情况似乎并非如此,因为我尝试执行
滚动到
延迟几秒钟的操作,并且没有任何更改),或者一些提示来实现这一点。
非常感谢!!