VStack中的SwiftUI切换未对齐

时间:2020-02-16 15:41:06

标签: swiftui-list swiftui

我有一个简单的List,其中每一行都是一个Toggle,其中TextText作为副标题的VStack。一切正常,直到我开始显示或隐藏一些行。 Toggle视图的开关以某种方式未对齐,并置于其标题上方。这只会在设备上发生,而不会在模拟器上运行。

enter image description here

在运行iOS 13.3.1的设备上XCode 13.3和13.4 beta都会发生这种情况

完整的示例是

import SwiftUI

struct ContentView: View {

    @State var showDetails = false
    @State var firstToggle = false
    @State var secondToggle = false
    var body: some View {
        NavigationView {
            Form {
                ToggleSubtitleRow(title: "Show Advanced",
                                     text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, se",
                                     isOn: $showDetails)

                if showDetails {
                    ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                         text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                         isOn: $firstToggle)

                    ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                         text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                         isOn: $secondToggle)
                }

            }
            .listStyle(GroupedListStyle())
            .navigationBarTitle("Settings", displayMode: .inline)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

public struct ToggleSubtitleRow: View {
    let title: String
    let text: String
    @Binding var isOn: Bool

    public init(title: String, text: String,
                isOn: Binding<Bool>) {
        self.text = text
        self.title = title
        self._isOn = isOn

    }

    public var body: some View {
        VStack(alignment: .leading) {
            Toggle(isOn: $isOn) {
                Text(title)
            }
            Text(text)
                .fixedSize(horizontal: false, vertical: true)
                .foregroundColor(Color(.secondaryLabel))
                .frame(alignment: .leading)
        }
        .foregroundColor(Color(.label))
    }
}

1 个答案:

答案 0 :(得分:1)

这应该有效

import SwiftUI

struct ContentView: View {

    @State var showDetails = false
    @State var firstToggle = false
    @State var secondToggle = false
    var body: some View {
        NavigationView {
            Form {
                ToggleSubtitleRow(title: "Show Advanced",
                                     text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, se",
                                     isOn: $showDetails)

                if showDetails {
                    ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                         text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                        isOn: $firstToggle).id(UUID())

                    ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                         text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                         isOn: $secondToggle)
                }

            }
            .listStyle(GroupedListStyle())
            .navigationBarTitle("Settings", displayMode: .inline)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

public struct ToggleSubtitleRow: View {
    let title: String
    let text: String
    @Binding var isOn: Bool

    public var body: some View {
        VStack(alignment: .leading) {
            Toggle(isOn: $isOn) {
                Text(title)
            }
            Text(text)
                .fixedSize(horizontal: false, vertical: true)
                .foregroundColor(Color(.secondaryLabel))
                .frame(alignment: .leading)
        }
        .foregroundColor(Color(.label))
    }
}

我在这里显示,如果至少需要重新创建整个条件部分,则将重新创建整个条件部分。

为便于说明,请稍微更改代码(不带任何.id修饰符)

if showDetails {
    Text("\(showDetails.description)")
    ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                  isOn: $firstToggle)

     ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                  isOn: $secondToggle)
 }

它可以按预期运行,因为SwiftUI在条件部分中识别出“某些内容”已更改。

Text("\(showDetails.description)")

具有相同的效果。

.id修饰符怎么样?为什么有效?

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

    /// Returns a view whose identity is explicitly bound to the proxy
    /// value `id`. When `id` changes the identity of the view (for
    /// example, its state) is reset.
    @inlinable public func id<ID>(_ id: ID) -> some View where ID : Hashable

}

基于书面内容

ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                    isOn: $firstToggle).id(showDetails)

也可以!

让这种方式重新排列代码

struct ContentView: View {

    @State var showDetails = false
    @State var firstToggle = false
    @State var secondToggle = false
    var body: some View {
        let g = Group {
            if showDetails {
                ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                  isOn: $firstToggle).id(UUID())

                ToggleSubtitleRow(title: "Lorem ipsum dolor sit amet",
                                  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
                                  isOn: $secondToggle)
            }
        }
        let f = Form {
            ToggleSubtitleRow(title: "Show Advanced",
                                 text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, se",
                                 isOn: $showDetails)
            g

        }
        .listStyle(GroupedListStyle())
        .navigationBarTitle("Settings", displayMode: .inline)
        let v = NavigationView {
            f
        }
        return v
    }
}

并检查g的类型

let g: Group<TupleView<(some View, ToggleSubtitleRow)>?>

我们可以看到SwiftUI如何处理我们的“条件”。实际上是

TupleView<(some View, ToggleSubtitleRow)>?

基于讨论的更新,在多个ToggleSubtitleRow上应用.id修饰符根本不起作用

解决此错误的最佳方法是重新定义

public struct ToggleSubtitleRow: View {
    let title: String
    let text: String
    @Binding var isOn: Bool

    public var body: some View {
        VStack(alignment: .leading) {
            Toggle(isOn: $isOn) {
                Text(title)
            }.id(UUID())
            Text(text)
                .fixedSize(horizontal: false, vertical: true)
                .foregroundColor(Color(.secondaryLabel))
                .frame(alignment: .leading)
        }
        .foregroundColor(Color(.label))
    }
}

不修改您的ContentView中的任何内容,而是直接在ToggleSubtitleRow中切换自己