如何在SwiftUI中移除或隐藏一个开关(Toggle)的标签

33

我正在寻找一种方法来删除SwiftUI中的标签...

我尝试过使用,但似乎并不是正确的方法:

Toggle(isOn: $isBlinky) {
    Text("DO NOT DISPLAY").color(.red)
}
.toggleStyle(.switch)
似乎标签被包含在类型本身中(struct Toggle<Label>),因此可能没有办法只有开关自己... 顺便说一下,如果我使用Text(""),然后scaledToFit(),开关仍然有点靠右并且不是真正居中... 总之,如果有人有想法! PS:在等待解决方案的同时,我包装了一个好用的老式UISwitch,但那不是我的想法...
struct Switch : UIViewRepresentable {
    @Binding var isOn : Bool

    func makeUIView(context: Context) -> UISwitch {
        let uiView = UISwitch()
        uiView.addTarget(
            context.coordinator,
            action: #selector(Coordinator.didChange(sender:)),
            for: .valueChanged)

        return uiView
    }

    func updateUIView(_ uiView: UISwitch, context: Context) {
        uiView.isOn = isOn
    }

    // MARK:- Coordinator

    func makeCoordinator() -> Switch.Coordinator {
        return Coordinator(self)
    }

    class Coordinator: NSObject {
        var control: Switch

        init(_ control: Switch) {
            self.control = control
        }

        @objc func didChange(sender: UISwitch) {
            control.isOn = sender.isOn
        }
    }
}
5个回答

64

SwiftUI 1.0

使用labelsHidden修饰符隐藏标签/标题

应该这样做。

Toggle("Turn alarm on", isOn: $isToggleOn)
    .labelsHidden() // Hides the label/title

注意:即使标签被隐藏,出于可访问性的目的,仍应添加一个标签。

示例:

没有标签的切换示例


简单易行的解决方法,感谢。但是这个5像素的常量在所有可访问字体大小设置中都是一样的吗? - Zaphod
@Zaphod,我已经测试过了。我添加了 .border(Color.red) 以确保它居中。然后我运行了它并调整了动态类型的环境覆盖,从最低到最高,它始终保持一致。 - Mark Moeykens
嗯,这是一个很好的解决方案,即使我真的不喜欢可能会在未来更改的魔法数字“5”和“25”... 好吧,在等待Swift UI 2.0或尚未发现的选项的同时,我会给你答案,恭喜... ;) - Zaphod
嘿@Zaphod,我找到了正确的方法并更新了我的答案,所以现在你不需要任何魔法数字了! - Mark Moeykens
即使使用了.labelsHidden(),视图仍然似乎占用了标签所需的额外空间,而不是只适应实际的切换。如果像我一样有人需要这个空间放置其他内容,我发现.labelsHidden()修饰符和.frame(.frame(width: 25).offset(x: -15)的组合效果还不错。 - Will Alexander
@WillAlexander 我正在使用 Xcode 12.4,开关周围没有额外的空间。 - Qbyte

6
您可以使用.labelsHidden()修饰符隐藏标签:
Toggle(isOn: $switchValue) {}
      .labelsHidden()

确实,但是当问题被提出时它还没有可用。 - Zaphod

0

如果您想隐藏文本的显示,这个选项非常适合。

    Toggle(isOn: $isActive) {
        Label("This will hide this text", systemImage: "eye.fill")
            .labelStyle(.iconOnly)
        }

1
这可能需要一些解释,而不仅仅是呈现代码。 - DavidW
问题是如何隐藏“切换”本身的标签。这个答案没有解决这个问题。建议删除。 - burnsi

0

如果要自定义一个 Toggle,您可以创建自己的 ToggleStyle

struct CustomToggleStyle: ToggleStyle {

    func body(configuration: Toggle<Self.Label>) -> Text {
        // Define look and feel for the toggle
        Text("Text toggle (?)")
    }

    typealias Body = Text

}

extension StaticMember where Base: ToggleStyle {

    static var custom: CustomToggleStyle.Member {
        return .init(CustomToggleStyle())
    }

}

并像这样使用它

.toggleStyle(.custom)

这似乎是做到的方法,但 API 还没有准备好

configuration 没有公开我们构建切换所需的值。

通过 dump 它,我可以看到它有一些有用的属性...

SwiftUI.Toggle<SwiftUI.ToggleStyleLabel>
  - label: SwiftUI.ToggleStyleLabel
  - state: SwiftUI.ToggleState.off
  - setOn: (Function)

...但它们似乎是私有的。

一旦我了解更多信息,我会更新这个答案。


谢谢,这不是问题的答案,但这是另一种方法...不幸的是,这种方法会失去与切换相关的所有内容(Catalyst可移植性与macOS风格等)。 - Zaphod
好的,感谢更新...但事实上,似乎今天没有解决方案是不需要以某种方式进行黑客攻击的... - Zaphod

0

我不是很喜欢使用“魔法数字”,所以我会尽量避免使用它们。经过大量的调试(包括 UI 测试视图转储),罪魁祸首是 SwiftUI 的 DefaultToggleStyle() 实现。该样式(不存在的)标签实际占用的空间无法消除,必须手动调整。但是,如果您创建自定义切换样式,则可以消除需要调整切换位置的 shim。关键是从自定义切换样式的视图中完全消除标签。然后,您需要为切换的“打开”和“关闭”状态创建视图并动画过渡。基本上,您需要从头开始重新实现 Switch Control。

我更新了下面的示例,以展示如何在给定适当图形的情况下实现完美对齐。Capsule 和 Toggle 完全对齐。否则,唯一的选择就是希望这些“魔法数字”不会太经常更改。

顺便说一句,默认样式中开关的大小为 57 宽 x 31 高。

import SwiftUI

extension Set {
    subscript(member: Element) -> Bool {
        get {
            return contains(member)
        }
        set {
            if newValue {
                insert(member)
                print("Selecting: \(member)")
            } else {
                remove(member)
                print("Deselecting: \(member)")
            }
        }
    }
}

struct ContentView: View {
    var items:[String] = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
    @State var selection: Set<String> = Set<String>()
    var body: some View {
        List(self.items, id: \.self) { item in
            VStack {
                Capsule().frame(width: 50, height: 30, alignment: .center)
                HStack {
                    Spacer()
                    Toggle(item, isOn: self.$selection[item])
//                        .frame(width: 25)
//                        .offset(x: -5) // Center toggle
                        .toggleStyle(CustomToggleStyle())
                    Spacer()
                }
            }
        }
    }
}

struct CustomToggleStyle: ToggleStyle {
    public func makeBody(configuration: CustomToggleStyle.Configuration) -> some View {
        HStack {
            if configuration.isOn {
                Button(action: { configuration.isOn.toggle() } )
                { Capsule().frame(width: 50, height: 30).foregroundColor(.red) }
            } else {
                Button(action: { configuration.isOn.toggle() } )
                { Capsule().frame(width: 50, height: 30).foregroundColor(.green) }
            }
//            configuration.label
        }
    }
}

谢谢您的更新。不幸的是,它仍然表现得像有两个视图(EmptyView和切换按钮)。例如,如果您创建一个只有一个切换的屏幕:Toggle("", isOn: $blinky).toggleStyle(CustomToggleStyle()),则按钮将右对齐,而不是像预期的那样居中。 - Zaphod
实际上,这导致了与Text("")发现的相同问题,即使使用scaledToFit(),它仍然在前导侧留下了一小段空间... - Zaphod
昨天我也发现了NavigationLink的同样问题。在Toggle之前和之后添加一个Spacer(),并将Toggle的框架大小设置为零似乎可以解决问题。现在一切都居中了。 - Chuck H
我还应该指出,这个解决方案也可以在没有CustomToggleStyle的情况下工作。只需设置框架大小并将间隔器添加到列表项内部的切换中即可。 - Chuck H
不幸的是,如果你仔细看的话,它仍然没有居中。为了确保,请在您的切换按钮上方添加一个小的Capsule().frame(width: 50, height: 30, alignment: .center) - Zaphod
看起来这是在完全自定义切换样式和硬编码常量以调整可能会在未来更改的默认样式之间做出选择。 - Chuck H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接