SwiftUI:为 .toolbar ToolbarItem 设置单独的颜色?

Posted

技术标签:

【中文标题】SwiftUI:为 .toolbar ToolbarItem 设置单独的颜色?【英文标题】:SwiftUI: Setting individual colors for .toolbar ToolbarItem? 【发布时间】:2020-09-20 13:06:26 【问题描述】:

一般来说,出于可访问性的原因,我喜欢坚持使用默认颜色选项,但我有一个特定的用例,我无法更改单个 .toolbar ToolbarItem 颜色。

我可以使用我的 sn-p 顶部的注释掉代码来覆盖 所有 ToolbarItem 颜色,但我想为单个图标着色。

import SwiftUI

struct ContentView: View 
    
//    init() 
//        UIBarButtonItem.appearance(whenContainedInInstancesOf: [UIToolbar.self]).tintColor = .systemRed
//    
    
    var body: some View 
        
        NavigationView 
            
            Text("Hello, world!")
                .toolbar 
                    
                    // To be colored RED
                    ToolbarItem(placement: .bottomBar) 
                        Button(action: , label: Label("Icon One", systemImage: "stop.fill"))
                    
                    
                    // To be colored BLACK
                    ToolbarItem(placement: .bottomBar) 
                        Button(action: , label: Label("Icon Two", systemImage: "play.fill"))
                    
                    
                
        
    


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

【问题讨论】:

【参考方案1】:

是的,我一直在为同样的事情苦苦挣扎。看起来Buttons 正在使用系统色调颜色及其整体风格。

这是我的看法:

content
    .toolbar 
        ToolbarItem(placement: .navigationBarLeading) 
            HStack 
                StyledButton(image: .system("arrow.left"))  ... 
                Spacer(minLength: 0)    // important to not fell button to default styling
            
        
    

我们的想法是不只显示 Button,而是与其他 UI 元素一起显示。对我来说看起来像一个 SwiftUI 错误。 (或者只是想比你聪明。)

【讨论】:

很奇怪,但就像一个魅力!!,请注意,这个问题只发生在 iphone 11 设备上,onTapGesture 不起作用。我们需要使用一个按钮。【参考方案2】:

我找到的最简单的解决方案是放弃 Button 并改用 .onTapGesture

struct ContentView: View 
    
    var body: some View 
        NavigationView 
            Text("Hello World!")
                .toolbar 
                    
                    // To be colored RED
                    ToolbarItem(placement: .bottomBar) 
                        Label("Icon One", systemImage: "stop.fill")
                            .foregroundColor(.red)
                            .onTapGesture 
                                // Respond to tap
                            
                    
                    
                    // To be colored BLACK
                    ToolbarItem(placement: .bottomBar) 
                        Label("Icon One", systemImage: "play.fill")
                            .foregroundColor(.black)
                            .onTapGesture 
                                // Respond to tap
                            
                    
                    
                
        
    

输出:

【讨论】:

ToolbarItem 似乎忽略了 .onTapGesture。换句话说,我认为您的解决方案行不通。颜色正确更改,但您的新“按钮”不会调用任何操作。此外,标签在点击期间不会像普通按钮那样变暗。【参考方案3】:

看起来所有标准类型(按钮、图像、文本等)都被 ToolbarItem 拦截并转换为适当的内部表示。但自定义视图(例如基于形状)......不是。因此,请参阅下面的可能方法演示。

使用 Xcode 12 / ios 14 准备和测试演示。

ToolbarItem(placement: .bottomBar) 
    ShapeButton(shape: Rectangle(), color: .red) 
        print(">> works")
    

以及简单的基于形状的自定义按钮

struct ShapeButton<S:Shape>: View 
    var shape: S
    var color: Color
    var action: () -> ()

    @GestureState private var tapped = false
    var body: some View 
        shape
            .fill(color).opacity(tapped ? 0.4 : 1)
            .animation(.linear(duration: 0.15), value: tapped)
            .frame(width: 18, height: 18)
            .gesture(DragGesture(minimumDistance: 0)
                .updating($tapped)  value, state, _ in
                    state = true
                
                .onEnded  _ in
                    action()
                )
    

【讨论】:

【参考方案4】:

如果你使用 UIKit,它对我有用。

struct ButtonRepresentation: UIViewRepresentable 
    let sfSymbolName: String
    let titleColor: UIColor
    let action: () -> ()
    
    func makeUIView(context: Context) -> UIButton 
        let b = UIButton()
        let largeConfig = UIImage.SymbolConfiguration(scale: .large)
        let image = UIImage(systemName: sfSymbolName, withConfiguration: largeConfig)
        b.setImage(image, for: .normal)
        b.tintColor = titleColor
        b.addTarget(context.coordinator, action: #selector(context.coordinator.didTapButton(_:)), for: .touchUpInside)
        return b
    
    
    func updateUIView(_ uiView: UIButton, context: Context) 
    
    func makeCoordinator() -> Coordinator 
        return Coordinator(action: action)
    
    
    typealias UIViewType = UIButton
    
    class Coordinator 
        let action: () -> ()
        
        @objc
        func didTapButton(_ sender: UIButton) 
            self.action()
        
        
        init(action: @escaping () -> ()) 
            self.action = action
        
    

然后您可以添加不同颜色的单独 ButtonRepresentations。

.toolbar 
    ToolbarItem(placement: .cancellationAction) 
        ButtonRepresentation(sfSymbolName: closeIcon, titleColor: closeIconColor)  
        presentationMode.wrappedValue.dismiss()
    

【讨论】:

【参考方案5】:

遗憾的是,这个技巧只适用于 iOS。

但从好的方面来说,它也适用于菜单:

Menu 
    Button("A title") 
 label : 
    HStack 
            Label("Star"), systemImage: "star")
                .labelStyle(.iconOnly)
                .foregroundColor(.red)
                Spacer(minLength: 0)
    


【讨论】:

以上是关于SwiftUI:为 .toolbar ToolbarItem 设置单独的颜色?的主要内容,如果未能解决你的问题,请参考以下文章

安卓Design包之Toolbar控件的使用

SwiftUI .toolbar ToolbarItem 自定义按钮设计

iOS 16 中 SwiftUI 4.0 Toolbar 自定义背景色以及 List 自动编辑操作的原生支持

iOS 16 中 SwiftUI 4.0 Toolbar 自定义背景色以及 List 自动编辑操作的原生支持

iOS 16 中 SwiftUI 4.0 Toolbar 自定义背景色以及 List 自动编辑操作的原生支持

SwipeActions 按钮和 ToolBar 按钮触发的多个工作表(项目:)在 SwiftUI 中首次获取 nil 对象