iOS 8 起 UIActionSheet 正式棄用 (Deprecated)。 若在 iOS 8+ 繼續使用 UIActionSheet 添加子視圖 (如 UIPickerView),視圖層級結構改變導致自定義內容**完全不顯示** (僅剩空白底框)。
UIAlertController 統一了 Alert 和 ActionSheet,且**不支援直接添加自定義子視圖** (如 addSubview(pickerView))。需改用原生輸入方式或自定義 Container View Controller。
// 1. 文字選單
let alert = UIAlertController(title: "選擇城市", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "台北", style: .default) { _ in /* 處理 */ })
alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: nil))
// iPad 必須設定 popoverPresentationController 防崩潰
if let popover = alert.popoverPresentationController {
popover.sourceView = self.view
popover.sourceRect = sender.frame // 或 view.bounds
}
present(alert, animated: true)
這是**官方推薦**的替代 UIActionSheet + addSubview 的標準做法。
class PickerActionSheetController: UIViewController {
let pickerView = UIPickerView()
var onSelect: ((Int) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
pickerView.dataSource = self
pickerView.delegate = self
view.addSubview(pickerView)
// Auto Layout 约束 pickerView 底部、左右、高度固定
pickerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
pickerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
pickerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
pickerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
pickerView.heightAnchor.constraint(equalToConstant: 216)
])
// 頂部工具列 (完成/取消按鈕)
let toolbar = UIToolbar()
toolbar.items = [
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
UIBarButtonItem(title: "完成", style: .done, target: self, action: #selector(doneTapped))
]
view.addSubview(toolbar)
toolbar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
toolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
toolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
toolbar.bottomAnchor.constraint(equalTo: pickerView.topAnchor),
toolbar.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func doneTapped() {
let row = pickerView.selectedRow(inComponent: 0)
onSelect?(row)
dismiss(animated: true)
}
}
// 使用時:
let pickerVC = PickerActionSheetController()
pickerVC.modalPresentationStyle = .actionSheet // iOS 13+ 自動呈現為底部彈出
pickerVC.onSelect = { row in print("選中第 \(row) 行") }
present(pickerVC, animated: true)
// iPad 適配 (同 UIAlertController)
if let popover = pickerVC.popoverPresentationController {
popover.sourceView = view
popover.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
popover.permittedArrowDirections = []
}
棄用 UIActionSheet + addSubview(picker) 這套舊範式。
- 簡單選單 → UIAlertController(style: .actionSheet)。
- 複雜自定義視圖 (Picker、表單、列表) → **自定義 UIViewController + modalPresentationStyle = .actionSheet (iOS 13+) 或 .overCurrentContext/.custom (舊版) + 轉場動畫**。
- 圖省事 → 接入成熟第三方庫。
這樣在 iOS 8 到 iOS 17+ 所有版本都能完美、穩定運行。