UIAlertController應該是大家都用過吧,最簡單的用法就是創造一個UIAlertController設定標題、內容跟按鈕
let alert = UIAlertController(title: "這是標題", message: "這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default)
alert.addAction(action)
self.present(alert, animated: true)
但是今天有一個需求要客製化字體大小、內嵌連接在裡面🫨 同時要保留原生Alert高度隨著公告字數增長的特性,增長到最大範圍後才提供scroll操作
覺得客製化的需求實在很難改,官方也講了UIAlertController不接受繼承
The UIAlertController
class is intended to be used as-is and doesn’t support subclassing. The view hierarchy for this class is private and must not be modified.
直接用SwiftUI重刻一個
這邊為了方便看Alert都先把背景設成粉紅色
先做了標題、內容、連結、分隔線跟按鈕,並且固定寬度,把高度設在一定範圍之間
struct BasicAlert: View {
let title: String
let message: String
let link: String?
let buttonTitle: String
let action: (()->Void)?
var body: some View {
VStack {
VStack {
// 標題
Text(title)
.font(.system(size: 20, weight: .bold))
// 內容
Text(message)
.font(.system(size: 18))
// 連結
if let link, let url = URL(string: link) {
Link("點擊這裡前往網站", destination: url)
.foregroundColor(.blue)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 9)
}
}.padding()
// 一條分隔線
Divider()
// 按鈕
createButton(title: buttonTitle).frame(height: 40.0)
}
.background(Color.pink)
.cornerRadius(15.0)
.frame(width: 270.0) // 固定寬度
.frame(minHeight: 161, maxHeight: 603) // 設定高度範圍
}
}
但是測試在高度超出範圍後,文字並沒有變成可以捲動,加上ScrollView看看
VStack {
ScrollView { // 新增scrollView
VStack {
// 標題
Text(title)
.font(.system(size: 20, weight: .bold))
// ....
長的內容可以捲動了,但是scrollView的高度卻固定在最高限制🫨
去查SwiftUI的scrollView要如何設定捲動範圍,查到了fixedSize這個屬性
這邊官方範例就解釋得很清楚,會幫你把元件調整成適合的大小
在scrollView加上這個屬性,並固定最高高度為500
ScrollView {
// ....
}
.frame(maxHeight: 500)
.fixedSize(horizontal: false, vertical: true)
//...
struct BasicAlert: View {
let title: String
let message: String
let link: String?
let buttonTitle: String
let action: (()->Void)?
var body: some View {
VStack {
ScrollView {
VStack {
// 標題
Text(title)
.font(.system(size: 20, weight: .bold))
// 內容
Text(message)
.font(.system(size: 18))
// 連結
if let link, let url = URL(string: link) {
Link("點擊這裡前往網站", destination: url)
.foregroundColor(.blue)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 9)
}
}.padding()
}
.frame(maxHeight: 500)
.fixedSize(horizontal: false, vertical: true)
// 一條分隔線
Divider()
// 按鈕
createButton(title: buttonTitle)
}
.background(Color.white)
.cornerRadius(15.0)
.frame(width: 270.0) // 固定寬度
}
}
extension BasicAlert {
func createButton(title: String) -> some View {
Button {
action?()
} label: {
Text(title)
.frame(height: 40.0)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
}
}
}
這邊有很多種做法,因為我的專案大部分都是UIKit,所以我是用UIHostingController轉接再放到UIWindow上
class BasicAlertVC: UIViewController {
let alertTitle: String
let message: String
let buttonTitle: String
let link: String?
let action: (()->Void)?
lazy var basicAlertView = makeBasicAlertView()
init(title: String, message: String, link: String?, buttonTitle: String, action: (()->Void)?) {
self.alertTitle = title
self.message = message
self.link = link
self.buttonTitle = buttonTitle
self.action = action
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChild(basicAlertView)
basicAlertView.view.backgroundColor = .clear
basicAlertView.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(basicAlertView.view)
basicAlertView.didMove(toParent: self)
NSLayoutConstraint.activate([
basicAlertView.view.widthAnchor.constraint(equalTo: view.widthAnchor),
basicAlertView.view.topAnchor.constraint(equalTo: view.topAnchor),
basicAlertView.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
basicAlertView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func makeBasicAlertView() -> UIHostingController<BasicAlert> {
let basicAlert = BasicAlert(title: alertTitle, message: message, link: link, buttonTitle: buttonTitle, action: action)
let controller = UIHostingController(rootView: basicAlert)
return controller
}
}
加到window上
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.backgroundColor = UIColor(white: 0, alpha: 0.6)
alertWindow.windowLevel = .alert
alertWindow.rootViewController = BasicAlertVC(title: title, message: message, link: link, buttonTitle: buttonTitle, action: action)
alertWindow.makeKeyAndVisible()
alertWindow.isHidden = false