What's new in UIKit
Swift & UI 进阶 30m

UIKit 的新特性

What's new in UIKit

2021年6月11日

在 Apple 官方观看视频

一句话判断

UIKit 在 iOS 15 没有革命性变化,但拿到了一堆实用的小改进——UIButton 配置化、Sheet 自适应尺寸、系统按钮样式——这些改动加在一起让 UIKit 的日常开发体验上了一个台阶。

这场 Session 讲了什么

UIKit 作为 iOS 开发的老牌框架,在 iOS 15 获得了一系列渐进式改进。Session 覆盖了 UI 组件、SwiftUI 互操作和开发体验三个方面。

UI 组件方面,UIButton 新增了 UIButton.Configuration API,用声明式的方式配置按钮的样式、图标、标题和间距。替代了之前 button.titleLabelbutton.imageView 的零散设置方式。系统提供了几种预设配置:.filled().tinted().gray().bordered() 等。

Sheet 展示方面,UISheetPresentationController 是新的 API,可以创建自适应尺寸的 Sheet——用户可以通过拖拽在半屏和全屏之间切换。这在地图 app、音乐 app 中已经被广泛使用。

UIListContentConfiguration 在 iOS 15 得到了增强,支持设置附属视图(accessory)的样式。配合 UICollectionLayoutListConfiguration,用 UICollectionView 构建列表的体验更接近 UITableView。

值得深挖的点

UIButton.Configuration 的状态驱动更新

UIButton.Configuration 是一个值类型(struct),你每次修改都会创建一个新的配置。配合 configurationUpdateHandler,你可以在按钮状态变化时(highlighted、disabled、selected)更新配置。这种方式比 setBackgroundColor(_:for:) 更灵活——你可以同时修改背景色、标题颜色、图标 tint 和阴影。

UISheetPresentationController 的 detent 系统

Sheet 的尺寸通过 detent(锚点)来定义。系统提供了 .medium()(大约半屏)和 .large()(全屏)两种预设 detent。你也可以自定义 detent 来设置特定的高度。用户可以在 detent 之间拖拽切换。

代码片段

// 使用 UIButton.Configuration 创建按钮
class LoginViewController: UIViewController {
    func createLoginButton() -> UIButton {
        var config = UIButton.Configuration.filled()
        config.title = "登录"
        config.subtitle = "使用 Apple ID"
        config.image = UIImage(systemName: "applelogo")
        config.imagePlacement = .leading
        config.imagePadding = 8
        config.baseBackgroundColor = .systemBlue
        config.cornerStyle = .capsule
        config.buttonSize = .large
        
        let button = UIButton(configuration: config)
        
        // 状态驱动的配置更新
        button.configurationUpdateHandler = { button in
            var config = button.configuration
            switch button.state {
            case .highlighted:
                config?.baseBackgroundColor = .systemBlue.withAlphaComponent(0.8)
            case .disabled:
                config?.baseBackgroundColor = .systemGray
                config?.title = "登录中..."
            default:
                config?.baseBackgroundColor = .systemBlue
            }
            button.configuration = config
        }
        
        return button
    }
}
// 使用 UISheetPresentationController 创建自适应 Sheet
func presentSettingsSheet() {
    let settingsVC = SettingsViewController()
    
    if let sheet = settingsVC.sheetPresentationController {
        // 设置可用的 detent
        sheet.detents = [.medium(), .large()]
        
        // 初始显示在半屏
        sheet.selectedDetentIdentifier = .medium
        
        // 显示 grabber(拖拽指示器)
        sheet.prefersGrabberVisible = true
        
        // 在 detent 变化时获取回调
        sheet.delegate = self
    }
    
    present(settingsVC, animated: true)
}

// UISheetPresentationControllerDelegate
func sheetPresentationControllerDidChangeSelectedDetentIdentifier(
    _ sheetPresentationController: UISheetPresentationController
) {
    let isExpanded = sheetPresentationController.selectedDetentIdentifier == .large
    print("Sheet 展开: \(isExpanded)")
}
// 使用 UICollectionLayoutListConfiguration 创建列表
func createListLayout() -> UICollectionViewCompositionalLayout {
    var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
    
    // 配置滑动操作
    config.leadingSwipeActionsConfigurationProvider = { indexPath in
        let action = UIContextualAction(
            style: .normal,
            title: "置顶"
        ) { _, _, completion in
            self.pinItem(at: indexPath)
            completion(true)
        }
        action.backgroundColor = .systemBlue
        return UISwipeActionsConfiguration(actions: [action])
    }
    
    return UICollectionViewCompositionalLayout.list(using: config)
}

最佳实践

UIButton.Configuration 是 iOS 15+ only 的 API。如果你的 app 需要支持 iOS 14,用 #available 检查后 fallback 到旧的 UIButton API。但长远来看,新的 Configuration API 是未来方向,建议新代码全部用 Configuration 写。

UISheetPresentationController 在紧凑宽度(iPhone)上表现为 Sheet,在常规宽度(iPad)上会自动变成 Popover。这个自适应行为很好,但如果你需要强制 Sheet 行为,设置 sheet.prefersEdgeAttachedInCompactHeight = true

还有什么值得关注

  • UIActivityIndicatorView 新增了 .large 样式,替代了旧的 UIActivityIndicatorView.Style.whiteLarge
  • UINavigationBar 的新 appearance API 支持设置标题的颜色和字体。
  • UITextView 支持了 UISuggestionSelection API,配合键盘的自动补全建议使用。
WWDC 2021