Adopt the new look of macOS
Swift & UI 进阶 26m

适配 macOS 的全新外观

Adopt the new look of macOS

2020年6月24日

在 Apple 官方观看视频

一句话判断

macOS Big Sur 做了十年来最大规模的 UI 改版,所有 App 都需要适配新的圆角、半透明、全高侧边栏等视觉风格。如果你的 App 使用标准控件,大部分变化是自动的;如果用了大量自定义 UI,这个 Session 能帮你理清适配清单。

这场 Session 讲了什么

macOS Big Sur 引入了全新的设计语言,统一的圆角矩形风格、更大的间距、更丰富的半透明效果、以及全新的控件外观。Session 逐一讲解了这些变化对开发者的影响。

重点变化包括:窗口工具栏(Toolbar)采用了更紧凑的布局,支持全高侧边栏(Full-height Sidebar);控件(Button、Slider、Progress Bar)的视觉风格更新,带有更多的动画反馈;新的 tint color 系统让 App 可以更灵活地定制配色;标题栏和工具栏的融合使得窗口看起来更加一体化。Session 还讨论了 Mac Catalyst App 如何利用新的优化选项来获得更原生的外观。

值得深挖的点

侧边栏的三种模式

macOS Big Sur 的侧边栏支持三种显示模式:全高侧边栏(Full-height Sidebar)是新的默认模式,侧边栏从窗口顶部延伸到底部;正常侧边栏在工具栏下方开始;以及紧凑侧边栏。通过 NSWindowtoolbarStyle 属性可以切换模式。全高侧边栏配合 NSToolbarItemitemIdentifier 排序可以创建类似 Finder 和 Mail 的三栏布局。

材质和半透明效果

Big Sur 大量使用了材质(Material)来创建半透明的 UI 层。NSVisualEffectView 的材质选项更新了,新增了 .windowBackground.sidebar 等语义化的材质类型。Apple 建议使用这些语义化材质而不是固定的颜色,因为它们会自动适配浅色/深色模式和窗口层级。

代码片段

配置窗口工具栏样式

场景:为文档编辑 App 配置 Big Sur 风格的工具栏。

import AppKit

class DocumentWindowController: NSWindowController {
    
    func configureWindowStyle() {
        guard let window = window else { return }
        
        // 设置工具栏样式为统一(unified)
        // 这会让工具栏和标题栏融为一体
        window.toolbarStyle = .unified
        
        // 如果需要全高侧边栏
        // window.toolbarStyle = .unifiedCompact
        
        // 创建工具栏
        let toolbar = NSToolbar(identifier: "mainToolbar")
        toolbar.delegate = self
        toolbar.displayMode = .iconAndLabel
        toolbar.allowsUserCustomization = true
        
        window.toolbar = toolbar
        
        // 启用标题栏透明效果
        window.titlebarAppearsTransparent = true
    }
}

extension DocumentWindowController: NSToolbarDelegate {
    func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
        return [
            .toggleSidebar,
            .flexibleSpace,
            .print,
            .share,
            .trackingSeparator  // Big Sur 新增的追踪分隔符
        ]
    }
    
    func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
        return [
            .toggleSidebar,
            .flexibleSpace,
            .print,
            .share
        ]
    }
}

使用新的 NSToolbarItem

场景:创建带有搜索功能的工具栏项。

// Big Sur 中推荐使用标准的工具栏搜索项
func toolbar(_ toolbar: NSToolbar,
             itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
             willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
    
    switch itemIdentifier {
    case .search:
        // 使用标准的搜索工具栏项
        let searchItem = NSSearchToolbarItem(itemIdentifier: .search)
        searchItem.searchField.delegate = self
        searchItem.searchField.placeholderString = "搜索文档..."
        return searchItem
        
    case NSToolbarItem.Identifier("customAction"):
        // 自定义工具栏项
        let item = NSToolbarItem(itemIdentifier: itemIdentifier)
        item.label = "操作"
        item.paletteLabel = "自定义操作"
        item.image = NSImage(systemSymbolName: "star.fill",
                               accessibilityDescription: "收藏")
        item.target = self
        item.action = #selector(customActionTapped)
        return item
        
    default:
        return nil
    }
}

适配材质和颜色

场景:正确使用 Big Sur 的材质系统。

import AppKit

class SidebarViewController: NSViewController {
    
    private var outlineView: NSOutlineView!
    private var visualEffectView: NSVisualEffectView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
    }
    
    private func setupViews() {
        // 使用语义化的材质
        let material: NSVisualEffectView.Material = .sidebar
        let blendingMode: NSVisualEffectView.BlendingMode = .behindWindow
        
        visualEffectView = NSVisualEffectView()
        visualEffectView.material = material
        visualEffectView.blendingMode = blendingMode
        visualEffectView.state = .active
        visualEffectView.autoresizingMask = [.width, .height]
        
        // 使用新的 tint color
        view.wantsLayer = true
        // 不要硬编码颜色,使用语义化的系统颜色
        // 正确:
        let backgroundColor = NSColor.controlBackgroundColor
        let textColor = NSColor.labelColor
        let secondaryTextColor = NSColor.secondaryLabelColor
        
        // Big Sur 新增的 accent color 支持
        // 用户可以在系统偏好设置中自定义强调色
        let accentColor = NSColor.controlAccentColor
    }
}

Mac Catalyst 的 Big Sur 适配

场景:让 Mac Catalyst App 获得 macOS 原生外观。

// 在 Mac Catalyst App 中启用 macOS 原生控件样式
// 在 Info.plist 中添加:
// UIApplicationSupportsIndirectInputEvents = YES

// UIKit 映射到 AppKit 的对照:
// UISlider -> NSSlider
// UIProgressView -> NSProgressIndicator
// UISwitch -> NSSwitch (Big Sur 新增)

#if targetEnvironment(macCatalyst)
// Mac Catalyst 特有的优化
func configureMacAppearance() {
    // 使用 macOS 风格的侧边栏
    // 需要在 UISplitViewController 中设置
    let splitVC = UISplitViewController()
    splitVC.preferredDisplayMode = .twoBesideSecondary
    splitVC.primaryBackgroundStyle = .sidebar  // 使用材质背景
}
#endif

最佳实践

已有项目:在 macOS Big Sur 上全面测试你的 App。如果你的 App 使用标准 AppKit 控件,大部分视觉更新是自动的。重点检查自定义绘制的视图、硬编码的颜色值、以及工具栏布局。将所有硬编码的颜色替换为 NSColor 的语义化属性(如 labelColorcontrolBackgroundColor)。

新项目:从第一天就使用 Big Sur 的设计模式。使用 NSToolbarunified 样式,采用全高侧边栏布局,使用语义化的材质和颜色。Mac Catalyst App 应该启用 primaryBackgroundStyle = .sidebar 来获得原生的侧边栏外观。

还有什么值得关注

  • macOS Big Sur 的图标也做了全面重新设计,采用了圆角矩形形状,需要更新你的 App 图标。
  • 新的 NSTrackingSeparatorToolbarItem 可以在侧边栏和内容区之间创建可拖拽的分隔线。
  • Big Sur 新增了 NSColor.controlAccentColor,跟随用户在系统设置中选择的强调色。
WWDC 2020