我有一个简单的List
,其中每一行都是一个Toggle
,其中Text
和Text
作为副标题的VStack
。一切正常,直到我开始显示或隐藏一些行。 Toggle
视图的开关以某种方式未对齐,并置于其标题上方。这只会在设备上发生,而不会在模拟器上运行。
在运行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))
}
}
答案 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中切换自己