SwiftUI Segmented Control 在视图刷新时选择段文本动画
Posted
技术标签:
【中文标题】SwiftUI Segmented Control 在视图刷新时选择段文本动画【英文标题】:SwiftUI Segmented Control selected segment text animation on view refresh 【发布时间】:2020-03-29 09:15:47 【问题描述】:在更改视图中的一些其他数据后刷新视图时,我在选定的分段控件段中遇到以下文本动画:
这是一个错误/功能还是有办法消除这种行为?
这是重现效果的代码:
import SwiftUI
struct ContentView: View
let colorNames1 = ["Red", "Green", "Blue"]
@State private var color1 = 0
let colorNames2 = ["Yellow", "Purple", "Orange"]
@State private var color2 = 0
var body: some View
VStack
VStack
Picker(selection: $color1, label: Text("Color"))
ForEach(0..<3, id: \.self) index in
Text(self.colorNames1[index]).tag(index)
.pickerStyle(SegmentedPickerStyle())
Text("Color 1: \(color1)")
.padding()
VStack
Picker(selection: $color2, label: Text("Color"))
ForEach(0..<3, id: \.self) index in
Text(self.colorNames2[index]).tag(index)
.pickerStyle(SegmentedPickerStyle())
Text("Color 2: \(color2)")
.padding()
这是在ios 13.4 / Xcode 11.4
下运行的
【问题讨论】:
是的,值得向 Apple 提交反馈。 我没有使用 SwiftUI(只是将小部件放在情节提要中),并且在更新片段中的文本时看到了同样的问题。我在我的段标题中显示项目计数,因此需要定期更新它们。我还没有找到解决办法。 【参考方案1】:重新排列代码库...(这有助于 SwiftUI 仅“刷新”必要的视图)
import SwiftUI
struct ContentView: View
let colorNames1 = ["Red", "Green", "Blue"]
@State private var color1 = 0
let colorNames2 = ["Yellow", "Purple", "Orange"]
@State private var color2 = 0
var body: some View
VStack
MyPicker(colorNames: colorNames1, color: $color1)
.padding()
MyPicker(colorNames: colorNames2, color: $color2)
.padding()
struct MyPicker: View
let colorNames: [String]
@Binding var color: Int
var body: some View
VStack
Picker(selection: $color, label: Text("Color"))
ForEach(0..<colorNames.count) index in
Text(self.colorNames[index]).tag(index)
.pickerStyle(SegmentedPickerStyle())
Text("Color 1: \(color)")
struct ContetView_Preview: PreviewProvider
static var previews: some View
ContentView()
结果
【讨论】:
谢谢。您的解决方法解决了上面的问题,所以我会接受答案。然而,我的实际问题是一个核心数据对象作为 ObservableObject 传递的视图,并且分段控件用于设置该对象的属性;因此,如果可能的话,我将需要找到一种方法使这种方法在那里相应地起作用。尽管如此,我还是提交了反馈,希望苹果能解决这个问题。【参考方案2】:我创建了一个自定义 SegmentControl 来解决这个问题:
import SwiftUI
struct MyTextPreferenceKey: PreferenceKey
typealias Value = [MyTextPreferenceData]
static var defaultValue: [MyTextPreferenceData] = []
static func reduce(value: inout [MyTextPreferenceData], nextValue: () -> [MyTextPreferenceData])
value.append(contentsOf: nextValue())
struct MyTextPreferenceData: Equatable
let viewIndex: Int
let rect: CGRect
struct SegmentedControl : View
@Binding var selectedIndex: Int
@Binding var rects: [CGRect]
@Binding var titles: [String]
var body: some View
ZStack(alignment: .topLeading)
SelectedView()
.frame(width: rects[selectedIndex].size.width - 4, height: rects[selectedIndex].size.height - 4)
.offset(x: rects[selectedIndex].minX + 2, y: rects[selectedIndex].minY + 2)
.animation(.easeInOut(duration: 0.5))
VStack
self.addTitles()
.onPreferenceChange(MyTextPreferenceKey.self) preferences in
for p in preferences
self.rects[p.viewIndex] = p.rect
.background(Color(.red)).clipShape(Capsule()).coordinateSpace(name: "CustomSegmentedControl")
func totalSize() -> CGSize
var totalSize: CGSize = .zero
for rect in rects
totalSize.width += rect.width
totalSize.height = rect.height
return totalSize
func addTitles() -> some View
HStack(alignment: .center, spacing: 8, content:
ForEach(0..<titles.count) index in
return SegmentView(selectedIndex: self.$selectedIndex, label: self.titles[index], index: index, isSelected: self.segmentIsSelected(selectedIndex: self.selectedIndex, segmentIndex: index))
)
func segmentIsSelected(selectedIndex: Int, segmentIndex: Int) -> Binding<Bool>
return Binding(get:
return selectedIndex == segmentIndex
) (value) in
struct SegmentView: View
@Binding var selectedIndex: Int
let label: String
let index: Int
@Binding var isSelected: Bool
var body: some View
Text(label)
.padding(.vertical, 6)
.padding(.horizontal, 10)
.foregroundColor(Color(.label))
.background(MyPreferenceViewSetter(index: index)).onTapGesture
self.selectedIndex = self.index
struct MyPreferenceViewSetter: View
let index: Int
var body: some View
GeometryReader geometry in
Rectangle()
.fill(Color.clear)
.preference(key: MyTextPreferenceKey.self,
value: [MyTextPreferenceData(viewIndex: self.index, rect: geometry.frame(in: .named("CustomSegmentedControl")))])
struct SelectedView: View
var body: some View
Capsule()
.fill(Color(.systemBackground))
.edgesIgnoringSafeArea(.horizontal)
result
【讨论】:
以上是关于SwiftUI Segmented Control 在视图刷新时选择段文本动画的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI Segmented Picker 在点击时不会切换
Scroll Segmented Control(Swift)
Scroll Segmented Control(Swift)