在不执行弹出操作的情况下更改SwiftUI工具栏图标

人工智能 2026-07-11

我希望工具栏项在处于活动状态时能显示徽章。我认为实现起来最好看的方式是使用自定义的SF Symbol,因为这与我的应用的其他部分相匹配。

但显然 .toolbar 标签忽略了你符号的文本对齐规则?因为导出时它们看起来很完美,但当我切换符号时,它们就会上下跳动:

当徽章被添加时,工具栏图标上下跳动

.toolbar {
    ToolbarItem(placement: .topBarTrailing) {
        Button {
            isFilterSheetPresented = true
        } label: {
            Image(
                systemName: activeFilterCount == 0 ? "tray" : "tray.badge"
            )
//          .contentTransition(.symbolEffect(.replace))
            .symbolRenderingMode(activeFilterCount > 0 ? .palette: .none)
            .foregroundStyle(
                activeFilterCount > 0 ? Color.accentColor : Color.primary,
                Color.primary
            )
            }
            .accessibilityLabel("Filter")
        }
}

在我的应用中,我使用自定义符号:

activeFilterCount==0 ? "custom.line.3.horizontal.decrease" : "custom.line.3.horizontal.decrease.badge"

tray.badge 的表现也一样,且它并非自定义——徽章会让符号变高,因此底层的符号在加上徽章时会向下弹出。

我已经禁用了 .contentTransition,因为一方面它只是让它在上下之间淡入淡出,而不是弹出;另一方面随着其他属性在随机时间间隔改变,会引发一堆乱象。

每个AI工具都建议我把它包在一个框架里,但那样只会阻止图标尺寸变化干扰工具栏的其他部分。我希望符号按其文本指南来布局,而不是按它的边界框来布局,而框架根本解决不了这个问题。

如果没有“正确”的答案,我可以用自定义让它基本可用

.offset(y: activeFilterCount > 0 ? -3.5 : 0)

并且在每个文本大小上大致估算一个偏移量,但这看起来深感愚蠢。

我真的希望有办法使用 .alignmentGuide() 和/或 .firstTextBaseline 来实际锁定它,但我认为这些在工具栏中并不工作。而且我也不认为有办法在里面放一个 HStack,可以避免同样的跳动。

我也在继续努力让它与 .badge() 一起工作,但那样也不会是理想的外观。

解决方案

这个问题类似于 SF Symbol replace animation size is off

我的回答 对那个问题给出了解法。我认为你可以在这里使用相同的技术,但重要的一点是要用带徽章的图像版本作为隐藏图像来建立基线:

ToolbarItem(placement: .topBarTrailing) {
    Button {
        activeFilterCount = activeFilterCount == 0 ? 1 : 0
        // isFilterSheetPresented = true
    } label: {
        ZStack(alignment: .centerFirstTextBaseline) {
            Image(systemName: "tray.badge")
                .hidden()
            Image(systemName: activeFilterCount == 0 ? "tray" : "tray.badge")
                .symbolRenderingMode(activeFilterCount > 0 ? .palette: .none)
                .foregroundStyle(
                    activeFilterCount > 0 ? Color.accentColor : Color.primary,
                    Color.primary
                )
        }
    }
    .accessibilityLabel("Filter")
}

动画演示

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章