SwiftUI按钮选择

4

我正在努力掌握SwiftUI的概念(完成了Apple的SwiftUI教程),但对我来说,自从使用UIKit十年后,它似乎很难理解。

我需要通过点击它们来切换HStack中多个按钮的状态(UIKit的isSelected),并更改它们的字体和文本(在UIKit世界中,我会在@IBAction中使用if语句检查isSelected属性,并使用attributedText属性)。在TouchUpInside)。

我的第一个想法是在其操作块中获取按钮的“引用”,但这感觉不像是SwiftUI的方法(甚至不可能)。我找到了使用Configurator及其isPressed属性的解决方案(这不是我要寻找的),但实际上我需要Button像toggle一样运行。 在SwiftUI中有没有内置的isSelected替代品,还是我必须制作自己的View实现,其中包含@State或@BindableObject,将封装某些手势识别器(看起来相当丑陋)?谢谢提前!


你说得对(关于很多事情),包括SwiftUI在可能的情况下与UI有关。没有“多个按钮状态” - 老实说,“状态”意味着完全不同的东西,请查找@State。按钮“状态”实际上是内置在SwiftUI Button中的。请查看以下两个链接:https://alejandromp.com/blog/2019/06/09/playing-with-swiftui-buttons/ https://alejandromp.com/blog/2019/06/22/swiftui-reusable-button-style/ - user7014451
只要数据来自观察变量,SwiftUI 就能检测到更改按钮标签中字体或文本的任何问题。据我所知,唯一不能做的就是使用按下操作进行更改,我相信动作是由释放触发的。在地标教程中,苹果使用按钮作为收藏夹的切换,还将外观从开放式星形更改为填充式星形。任何状态都可以影响任何视图,因此您可以将收藏夹星号扩展到 5 颗星,并使用 fav >= 3 来影响第三颗星的标签,在其操作中使用 fav = 3 - Michael Salmon
感谢大家的回复,朋友们。对于造成的不便,我很抱歉。我并不是指将每个单独按钮状态简化为“按钮组的全局状态”,而是指像UIKit中一样,每个单独按钮的isSelected状态。我提到多个按钮只是为了说明在我的视图中将状态存储为@State(isSelected = true / false)不是一个选项,因为我有很多按钮,并且每个按钮的状态都是独立的(因此必须由每个特定按钮本身存储)。 - Aft3rmath
@dfd 我已经阅读了您提供的链接,非常感谢。但是我没有找到任何与“Button”状态实际上内置于SwiftUI按钮相关的内容。您能否更具体地说明一下? - Aft3rmath
@MichaelSalmon 感谢您的评论,问题在于Apple的示例使用模型的isFavorite属性来简化其Label闭包中的if-else按钮外观。我能想到的唯一办法就是创建自定义视图,将Button封装起来,并计算每次touch up inside事件为2x isPressed计数器,或者只有带有手势识别器的视图。对我来说,似乎很奇怪,在UIKit中只需要两行代码就可以完成某些事情,而你必须真正理解如何做才能实现它 :/ - Aft3rmath
显示剩余2条评论
4个回答

6
我设计了一个自定义视图,将按钮封装在其中,代码如下:

```

    import SwiftUI

struct SelectableButtonStyle: ButtonStyle {

    var isSelected = false

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .frame(width: 60.0, height: 60.0, alignment: .center)
            .padding()
            .background(Color(#colorLiteral(red: 1, green: 0.8980392157, blue: 0.7058823529, alpha: 1)))
            .clipShape(RoundedRectangle(cornerRadius: isSelected ? 16.0 : 0.0))
            .overlay(RoundedRectangle(cornerRadius: isSelected ? 16.0 : 0.0).stroke(lineWidth: isSelected ? 2.0 : 0.0).foregroundColor(Color.pink))
            .animation(.linear)
    }
}


struct StatedButton<Label>: View where Label: View {


    private let action: (() -> ())?

    private let label: (() -> Label)?

    @State var buttonStyle = SelectableButtonStyle()

    init(action: (() -> ())? = nil, label: (() -> Label)? = nil) {
        self.action = action
        self.label = label
    }

    var body: some View {
        Button(action: {
            self.buttonStyle.isSelected = !self.buttonStyle.isSelected
            self.action?()
            print("isSelected now is \(self.buttonStyle.isSelected ? "true" : "false")")
        }) {
            label?()
        }
        .buttonStyle(buttonStyle)
    }    
}

如果这个解决方案有问题,请让我知道原因,非常感谢。另外,我正在困扰一个相当琐碎的问题:如何将我的模型数组元素映射到按钮(即如何检测确切被点击的按钮),但我认为我需要另外提出一个问题。


感觉需要做很多工作,但我想UIButton有很多功能。为了让它在工具栏中工作,我不得不将协议符合性更改为PrimitiveButtonStyle。 - glotcha

2

您可以创建自定义的ButtonStyle,并根据isPressed修改配置标签。

struct CustomButton: View {
    var body: some View {
        Button {
            // action
        } label: {
            Text("Button")
        }
        .buttonStyle(CustomStyle())
    }
}

struct CustomStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .foregroundColor(.white)
            .background(configuration.isPressed ? .green : .black)
    }
}

2
我有一种简单的方法来实现这个。
    @State var selected = false

    func createButton() -> some View {
        Button(action: {
            self.selected.toggle()
        }, label: {
            Text("Hello World")
                .padding(.all, 5)
                .background(selected ? Color.blue : Color.white)
                .foregroundColor(selected ? Color.white : Color.blue)
        })
            .overlay(
                RoundedRectangle(cornerRadius: 4)
                    .stroke(Color.blue, lineWidth: 1)
        )
    }


self.$selected.wrappedValue.toggle() - Vanya
请问如何使用它来调用这个函数? - Abrcd18

0
我更喜欢使用自定义的 "isSelected" Environment 来实现 ButtonStyle。以下代码中有一些自定义的颜色值等等,但我希望你能理解我的意思;-)
extension ButtonStyle where Self == ButtonStyleGrey {
    internal static var grey: ButtonStyleGrey {
        ButtonStyleGrey()
    }
}

struct ButtonStyleGrey: ButtonStyle {
    @Environment(\.isEnabled) private var isEnabled: Bool
    @Environment(\.isSelected) private var isSelected: Bool

    func makeBody(configuration: ButtonStyle.Configuration) -> some View {
        let font = Font.system(size: UIScreen.titleFontSize, weight: .semibold)
        let text: UIColor = isEnabled ? .titleColor : .systemGray2
        let back: UIColor = isEnabled ? (configuration.isPressed || isSelected ? .graySelected : .grayNormal) : .disabledColor
        
        return configuration
            .label
            .textCase(.uppercase)
            .font(font)
            .foregroundColor(Color(text))
            .padding(.regular)
            .background(Color(back))
    }
}

// MARK: -

private struct isSelectedKey: EnvironmentKey {
    static let defaultValue: Bool = false
}

extension EnvironmentValues {
    var isSelected: Bool {
        get { self[isSelectedKey.self] }
        set { self[isSelectedKey.self] = newValue }
    }
}

extension View {
    func isSelected(_ selected: Bool) -> some View {
        environment(\.isSelected, selected)
    }
}

使用方法就像这样:
   Button("Some action") {
       // run something
   }
   .buttonStyle(.grey)
   .isSelected(viewModel.someActionSelected)

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