SwiftUI分段控件视图刷新时选定的分段文本动画

SwiftUI分段控件视图刷新时选定的分段文本动画,swiftui,ios13,segmentedcontrol,Swiftui,Ios13,Segmentedcontrol,在更改视图中的某些其他数据后刷新视图时,我正在体验分段控件的选定段中文本的以下动画: 这是一个错误/功能还是有办法消除这种行为? 这是重现效果的代码: import SwiftUI struct ContentView: View { let colorNames1 = ["Red", "Green", "Blue"] @State private var color1 = 0 let colorNames2 = ["Yellow", "Purple", "Oran

在更改视图中的某些其他数据后刷新视图时,我正在体验分段控件的选定段中文本的以下动画:

这是一个错误/功能还是有办法消除这种行为?

这是重现效果的代码:

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()
        }
    }
}
导入快捷界面
结构ContentView:View{
让colorNames1=[“红色”、“绿色”、“蓝色”]
@国家私有变量color1=0
让colorNames2=[“黄色”、“紫色”、“橙色”]
@国家私有变量2=0
var body:一些观点{
VStack{
VStack{
选择器(选择:$color1,标签:文本(“颜色”)){

ForEach(0..重新排列代码库…(这有助于SwiftUI仅“刷新”必要的视图)

导入快捷界面
结构ContentView:View{
让colorNames1=[“红色”、“绿色”、“蓝色”]
@国家私有变量color1=0
让colorNames2=[“黄色”、“紫色”、“橙色”]
@国家私有变量2=0
var body:一些观点{
VStack{
MyPicker(颜色名称:colorNames1,颜色:$color1)
.padding()
MyPicker(颜色名称:colorNames2,颜色:$color2)
.padding()
}
}
}
结构MyPicker:视图{
让颜色名称:[字符串]
@绑定变量颜色:Int
var body:一些观点{
VStack{
选择器(选择:$color,标签:Text(“color”)){

ForEach(0..我创建了一个自定义分段控件来解决此问题:

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)
    }
}
导入快捷界面
结构MyTextPreferenceKey:PreferenceKey{
typealias值=[MyTextPreferenceData]
静态变量defaultValue:[MyTextPreferenceData]=[]
静态函数reduce(值:inout[MyTextPreferenceData],nextValue:()->[MyTextPreferenceData]){
append(contentsOf:nextValue())
}
}
结构MyTextPreferenceData:Equalable{
让viewIndex:Int
let rect:CGRect
}
结构分段控件:视图{
@绑定变量selectedIndex:Int
@绑定变量rects:[CGRect]
@绑定变量标题:[字符串]
var body:一些观点{
ZStack(对齐:。顶部引导){
SelectedView()
.frame(宽度:rects[selectedIndex].size.width-4,高度:rects[selectedIndex].size.height-4)
.offset(x:rects[selectedIndex].minX+2,y:rects[selectedIndex].minY+2)
.animation(.easeInOut(持续时间:0.5))
VStack{
self.addTitles()
}.onPreferenceChange(MyTextPreferenceKey.self){中的首选项
对于首选项中的p{
self.rects[p.viewIndex]=p.rect
}
}
}.background(颜色(.red)).clipShape(胶囊()).coordinateSpace(名称:“CustomSegmentedControl”)
}
func totalSize()->CGSize{
变量totalSize:CGSize=.0
对于矩形中的矩形{
totalSize.width+=矩形宽度
totalSize.height=矩形高度
}
返回总大小
}
func addTitles()->一些视图{
HStack(对齐:。中心,间距:8,内容:{
ForEach(0..Binding{
返回绑定(获取:{
返回selectedIndex==分段索引
}){(值)in}
}
}
结构段视图:视图{
@绑定变量selectedIndex:Int
让标签:字符串
let索引:Int
@绑定变量:Bool
var body:一些观点{
文本(标签)
.padding(.vertical,6)
.padding(.卧式,10)
.foregroundColor(颜色(.label))
.background(MyPreferenceViewSetter(索引:index)).ontaps{
self.selectedIndex=self.index
}
}
}
结构MyPreferenceViewSetter:视图{
let索引:Int
var body:一些观点{
GeometryReader{中的几何体
矩形()
.填充(颜色.透明)
.preference(键:MyTextPreferenceKey.self,
值:[MyTextPreferenceData(viewIndex:self.index,rect:geometry.frame(in:.named(“CustomSegmentedControl”))]))
}
}
}
struct SelectedView:View{
var body:一些观点{
胶囊()
.fill(颜色(.systemBackground))
.边缘识别安全区域(.水平)
}
}

是的,值得向苹果提交反馈。我没有使用SwiftUI(只是将小部件放在情节提要中)当我更新片段中的文本时,也会看到同样的问题。我在片段标题中显示了一些项目,因此它们需要定期更新。我还没有找到解决方案。谢谢。您的解决方法解决了上述问题,因此我将接受答案。但是,我实际的问题是将核心数据对象作为Observ传递的视图ableObject和分段控件用于设置该对象的属性;因此,如果可能的话,我需要找到一种方法使这种方法在那里工作。尽管如此,我还是提交了反馈,希望苹果能解决这个问题。
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)
    }
}