【问题标题】:SwiftUI: Button without Label/String in the initializer but with ButtonStyleSwiftUI:初始化程序中没有标签/字符串但带有 ButtonStyle 的按钮
【发布时间】:2022-01-12 03:45:30
【问题描述】:

SwiftUI 有一些 Button 初始化器,但它们都需要 Stringsome View 作为参数以及 action

不过,按钮的外观也可以在ButtonStyles 的帮助下进行自定义,ButtonStyles 可以为其添加自定义视图。

让我们考虑一个带有以下图标的复制按钮:

我为按钮制作的样式如下:

struct CopyButtonStyle: ButtonStyle {

    init() {}

    func makeBody(configuration: Configuration) -> some View {
        let copyIconSize: CGFloat = 24
        return Image(systemName: "doc.on.doc")
            .renderingMode(.template)
            .resizable()
            .frame(width: copyIconSize, height: copyIconSize)
            .accessibilityIdentifier("copy_button")
            .opacity(configuration.isPressed ? 0.5 : 1)
    }
}

它工作得很好,但是,我必须在调用站点用一个空字符串初始化Button

Button("") {
    print("copy")
}
.buttonStyle(CopyButtonStyle())

那么,问题是如何去掉按钮初始化参数中的空字符串?

可能的解决方案

我能够创建一个简单的扩展来完成我需要的工作:

import SwiftUI

extension Button where Label == Text {
    init(_ action: @escaping () -> Void) {
        self.init("", action: action)
    }
}

调用站点:

Button() { // Note: no initializer parameter
    print("copy")
}
.buttonStyle(CopyButtonStyle())

但很好奇,我是否错误地使用了 Button 结构并且已经有一个用例,所以我可以摆脱这个扩展。

【问题讨论】:

    标签: ios swift button swiftui


    【解决方案1】:

    比进行ButtonStyle 配置更简单的方法是直接传入标签:

    Button {
        print("copy")
    } label: {
        Label("Copy", systemImage: "doc.on.doc")
            .labelStyle(.iconOnly)
    }
    

    这也带来了一些好处:

    1. 默认情况下,按钮为蓝色,表示可以点击
    2. 不会对您当前拥有的图像进行奇怪的拉伸
    3. 无需实现按下时不透明度如何变化

    您也可以将其重构为自己的视图:

    struct CopyButton: View {
        let action: () -> Void
    
        var body: some View {
            Button(action: action) {
                Label("Copy", systemImage: "doc.on.doc")
                    .labelStyle(.iconOnly)
            }
        }
    }
    

    这样称呼:

    CopyButton {
        print("copy")
    }
    

    整体看起来更干净。

    【讨论】:

    • 我有一个其他 ButtonStyles 的调色板,它们可能不使用 SF 符号或使用文本,所以这将是一个过于定制的解决方案。请注意,我想摆脱初始化参数,而不是样式支持或创建一个完全自定义的类型。不过谢谢你的回答!
    【解决方案2】:

    这是您尝试做的正确方法,您不需要为每种按钮创建一个新的 ButtonStyle,您可以只创建一个并将其重用于您想要的任何其他按钮。我还用.scaledToFit() 解决了您的图像拉伸问题。

    struct CustomButtonView: View {
        
        let imageString: String
        let size: CGFloat
        let identifier: String
        let action: (() -> Void)?
        
        init(imageString: String, size: CGFloat = 24.0, identifier: String = String(), action: (() -> Void)? = nil) {
            self.imageString = imageString
            self.size = size
            self.identifier = identifier
            self.action = action
        }
    
        var body: some View {
            
            return Button(action: { action?() } , label: {
                
                Image(systemName: imageString)
                   .renderingMode(.template)
                   .resizable()
                   .scaledToFit()
                   .frame(width: size, height: size)
                   .accessibilityIdentifier(identifier)
                
            })
            .buttonStyle(CustomButtonStyle())
    
        }
        
    }
    
    struct CustomButtonStyle: ButtonStyle {
    
        func makeBody(configuration: Configuration) -> some View {
            return configuration.label
                .opacity(configuration.isPressed ? 0.5 : 1.0)
                .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
        }
    }
    

    用例:

    struct ContentView: View {
        var body: some View {
            
            CustomButtonView(imageString: "doc.on.doc", identifier: "copy_button", action: { print("copy") })
    
        }
    }
    

    【讨论】:

    • “我也解决了您的图像拉伸问题” -> 不需要,我使用的图标是自定义图标,该图标没有拉伸问题。这里显示的图标仅供参考,仅关于API(编程)的问题
    • 我只能看到有问题的代码,问题中存在拉伸问题! Ps:我正在尝试解决或重构任何有问题的问题,这是我的回答方式。你可以的。
    • 没问题,感谢您解决它并提出您的版本。对于这个问题,我刚刚用默认图标替换了我的自定义图标(非常适合),所以仍然清楚问题的全部内容。
    • 我已经在我上次关于我的回答风格的评论中回答了你!因此,您大胆地谈论问题的全部内容是毫无意义的!我还提到:你做你!这意味着做你的风格!我做我的风格。
    【解决方案3】:

    标签可以使用EmptyView,比如

        Button(action: { // Note: no initializer parameter
             print("copy")
        }, label: { EmptyView() })
        .buttonStyle(CopyButtonStyle())
    

    但从重用和代码可读性的角度来看,将其包装在自定义按钮类型中(如其他答案所示)更可取。

    【讨论】:

    • 仍然需要在呼叫站点添加EmptyView。我只是更愿意摆脱它。看起来扩展和 init 方法是这里最简单的解决方案。
    猜你喜欢
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-25
    • 2020-12-30
    • 2021-03-14
    • 2022-11-19
    • 1970-01-01
    相关资源
    最近更新 更多