Swiftui 快捷键/首选键:如何避免滚动时移动矩形?
我正在使用一个可滚动的选项卡栏,它为所选的选项卡提供了一个移动的背景 解决方案基于Swiftui 快捷键/首选键:如何避免滚动时移动矩形?,swiftui,Swiftui,我正在使用一个可滚动的选项卡栏,它为所选的选项卡提供了一个移动的背景 解决方案基于PreferenceKeys;然而,我有一个问题,使移动背景相对于标签稳定。当前,它在滚动时移动,这是不需要的;相反,它应相对于选项卡项进行固定,并与选项卡项一起滚动 为什么会这样,如何避免?删除滚动视图时,背景将正确移动到所选选项卡项。TabItemButton只是一个带有特殊标签的按钮 struct TabBar: View { @EnvironmentObject var service:
PreferenceKey
s;然而,我有一个问题,使移动背景相对于标签稳定。当前,它在滚动时移动,这是不需要的;相反,它应相对于选项卡项进行固定,并与选项卡项一起滚动
为什么会这样,如何避免?删除滚动视图时,背景将正确移动到所选选项卡项。TabItemButton
只是一个带有特殊标签的按钮
struct TabBar: View {
@EnvironmentObject var service: IRScrollableTabView.Service
// We support up to 15 items.
@State private var rects: [CGRect] = Array<CGRect>(repeating: CGRect(), count: 15)
var body: some View {
GeometryReader { geo in
ScrollView(.horizontal) {
ZStack {
IRScrollableTabView.Indicator()
.frame(width: self.rects[self.service.selectedIndex].size.width,
height: self.rects[self.service.selectedIndex].size.height)
.offset(x: self.offset(width: geo.size.width))
.animation(.easeInOut(duration: 0.3))
HStack(alignment: .top, spacing: 10) {
ForEach(0..<self.service.tabItems.count, id: \.self) { index in
TabItemButton(index: index,
isSelected: true,
item: self.service.tabItems[index])
// We want a fixed tab item with.
.frame(width: 70)
// This detects the effective positions of the tabs.
.background(IRTabItemViewSetter(index: index))
}
}
// We want to have the positions within this space.
.coordinateSpace(name: "IRReference")
// Update the current tab positions.
.onPreferenceChange(IRTabItemPreferenceKey.self) { preferences in
debugPrint(">>> Preferences:")
for p in preferences {
debugPrint(p.rect)
self.rects[p.viewIndex] = p.rect
}
}
}
}
}
}
private func offset(width: CGFloat) -> CGFloat {
debugPrint(width)
let selectedRect = self.rects[self.service.selectedIndex]
debugPrint(selectedRect)
let selectedOffset = selectedRect.minX + selectedRect.size.width / 2 - width / 2
debugPrint(selectedOffset)
return selectedOffset
}
}
struct Setter: View {
let index: Int
var body: some View {
GeometryReader { geo in
Rectangle()
.fill(Color.clear)
.preference(key: IRPreferenceKey.self,
value: [IRData(viewIndex: self.index,
rect: geo.frame(in: .named("IRReference")))])
}
}
}
struct IRPreferenceKey: PreferenceKey {
typealias Value = [IRData]
static var defaultValue: [IRScrollableTabView.IRData] = []
static func reduce(value: inout [IRScrollableTabView.IRData], nextValue: () -> [IRScrollableTabView.IRData]) {
value.append(contentsOf: nextValue())
}
}
struct IRData: Equatable {
let viewIndex: Int
let rect: CGRect
}
我解决了这个问题!技巧似乎是在指示器
视图周围放置另一个GeometryReader
,并获取其宽度以计算偏移量。.onPreferenceChange
必须附加到HStack
,而.coordinateSpace
必须附加到ZStack
。现在它开始工作了
var body: some View {
GeometryReader { geo in
ScrollView(.horizontal) {
ZStack {
GeometryReader { innerGeo in
IRScrollableTabView.Indicator()
.frame(width: self.rects[self.service.selectedIndex].size.width,
height: self.rects[self.service.selectedIndex].size.height)
.offset(x: self.offset(width: innerGeo.size.width))
.animation(.easeInOut(duration: 0.3))
}
HStack(alignment: .top, spacing: 10) {
ForEach(0..<self.service.tabItems.count, id: \.self) { index in
TabItemButton(index: index,
isSelected: true,
item: self.service.tabItems[index])
// We want a fixed tab item with.
.frame(width: 70)
// This detects the effective positions of the tabs.
.background(IRTabItemViewSetter(index: index))
}
}
// Update the current tab positions.
.onPreferenceChange(IRTabItemPreferenceKey.self) { preferences in
debugPrint(">>> Preferences:")
for p in preferences {
debugPrint(p.rect)
self.rects[p.viewIndex] = p.rect
}
}
}
// We want to have the positions within this space.
.coordinateSpace(name: "IRReference")
}
}
}
private func offset(width: CGFloat) -> CGFloat {
debugPrint(width)
let selectedRect = self.rects[self.service.selectedIndex]
debugPrint(selectedRect)
let selectedOffset = -width / 2 + CGFloat(80 * self.service.selectedIndex) + selectedRect.size.width / 2
debugPrint(selectedOffset)
return selectedOffset
}
var主体:一些视图{
GeometryReader{geo-in
滚动视图(.horizontal){
ZStack{
GeometryReader{innerGeo in
iScrollableTabView.Indicator()
.frame(宽度:self.rects[self.service.selectedIndex].size.width,
高度:self.rects[self.service.selectedIndex].size.height)
.offset(x:self.offset(宽度:innerGeo.size.width))
.animation(.easeInOut(持续时间:0.3))
}
HStack(对齐:顶部,间距:10){
ForEach(0..CGFloat{
调试打印(宽度)
让selectedRect=self.rects[self.service.selectedIndex]
调试打印(已选择打印)
让selectedOffset=-width/2+CGFloat(80*self.service.selectedIndex)+selectedRect.size.width/2
调试打印(已选择偏移)
返回selectedOffset
}
这是不可测试的。任何演示?短拷贝粘贴运行示例?将所有代码放在下面的要点中:实际上,不是那么短…顺便说一下:我一把GeometryReader放进ScrollView,它就不再滚动了…我不明白为什么…同时我在这里提供了一个带有全功能视图的Swift包:
var body: some View {
GeometryReader { geo in
ScrollView(.horizontal) {
ZStack {
GeometryReader { innerGeo in
IRScrollableTabView.Indicator()
.frame(width: self.rects[self.service.selectedIndex].size.width,
height: self.rects[self.service.selectedIndex].size.height)
.offset(x: self.offset(width: innerGeo.size.width))
.animation(.easeInOut(duration: 0.3))
}
HStack(alignment: .top, spacing: 10) {
ForEach(0..<self.service.tabItems.count, id: \.self) { index in
TabItemButton(index: index,
isSelected: true,
item: self.service.tabItems[index])
// We want a fixed tab item with.
.frame(width: 70)
// This detects the effective positions of the tabs.
.background(IRTabItemViewSetter(index: index))
}
}
// Update the current tab positions.
.onPreferenceChange(IRTabItemPreferenceKey.self) { preferences in
debugPrint(">>> Preferences:")
for p in preferences {
debugPrint(p.rect)
self.rects[p.viewIndex] = p.rect
}
}
}
// We want to have the positions within this space.
.coordinateSpace(name: "IRReference")
}
}
}
private func offset(width: CGFloat) -> CGFloat {
debugPrint(width)
let selectedRect = self.rects[self.service.selectedIndex]
debugPrint(selectedRect)
let selectedOffset = -width / 2 + CGFloat(80 * self.service.selectedIndex) + selectedRect.size.width / 2
debugPrint(selectedOffset)
return selectedOffset
}