SwiftUI中如何判断Label的configuration.icon是否有效?
问题:SwiftUI Label图标有效性判断与优雅降级实现
我在App的左上角设置了一个带齿轮图标的按钮,用于唤起设置面板,代码如下:
Button { withAnimation { showSettingsSheet.toggle() }} label: { Label("Settings", systemImage: "gearshape") .labelStyle(.iconOnly) }
在iOS 17首个测试版中,有用户反馈该按钮既不显示也无法响应点击,推测是SF Symbol加载失败,且因使用.iconOnly标签样式,导致无内容可展示或响应触摸。该问题在iOS 17 beta 2中已修复,但我希望未来能更优雅地处理此类情况,计划创建SafeIconOnlyLabelStyle,正常情况下显示图标,当图标失效时回退显示标题。
struct SafeIconOnlyLabelStyle: LabelStyle { @ViewBuilder func makeBody(configuration: Configuration) -> some View { if configuration.icon == <<WHAT SHOULD configuration.icon BE COMPARED TO>> { Label(configuration) .labelStyle(.iconOnly) } else { Label(configuration) } } }
请问如何判断configuration.icon是否存在且有效?
更新说明
与UIImage、NSImage、CGImage返回可选值不同,SwiftUI始终返回Image视图。若传入错误的图标名称,仍会将Image视图加入层级,只是其宽高为0。例如:
Label("Title", systemName: "badName")
与
Label { Text("Title") } icon: { Image(systemImage: "badName") }
效果等价。在iOS上使用.titleAndIcon样式时,等价于:
HStack { Image(systemImage: "badName").frame(width: .zero, height: .zero) Text("Title") }
使用.iconOnly样式时,简化后的标签仅包含图标:
Image(systemImage: "badName").frame(width: .zero, height: .zero)
当此层级作为Button的Label时,按钮会无标题和图标,若问题原因不可控则会引发问题。
我最初的方案是创建SafeIconOnlyLabelStyle,用GeometryReader计算configuration.icon的尺寸,当尺寸为.zero时显示标题,该方案虽可行,但若Apple后续修改实现可能引发bug。
最终解决方案
实现自定义SafeLabel视图,通过UIKit/AppKit的API提前判断系统图标是否存在,当图标不存在时使用标题替代:
public struct SafeLabel: View { #if os(macOS) typealias SystemImage = NSImage #elseif os(iOS) typealias SystemImage = UIImage #endif private var title: Text private var icon: Image? public init(_ titleKey: LocalizedStringKey, systemImage name: String) { self.title = Text(titleKey) if let image = fetchImage(systemName: name) { #if os(macOS) icon = Image(nsImage: image) #elseif os(iOS) icon = Image(uiImage: image) #endif } } @MainActor public var body: some View { Label { title } icon: { if let icon { icon } else { Text("\(title)") } } } private func fetchImage(systemName name: String) -> SystemImage? { #if os(macOS) guard let image = NSImage(systemImageName: name) else { return nil } #elseif os(iOS) guard let image = UIImage(systemName: name) else { return nil } #endif return image } }
该方案解决了图标不可用时Label及按钮消失的问题。
内容的提问来源于stack exchange,提问作者Steven Marcotte
相关产品推荐
相关产品推荐

