使用SwiftUI客製化Alert

閱讀時間約 14 分鐘

UIAlertController應該是大家都用過吧,最簡單的用法就是創造一個UIAlertController設定標題、內容跟按鈕

UIAlertConrtoller

UIAlertConrtoller



let alert = UIAlertController(title: "這是標題", message: "這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容這是內容", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default)
alert.addAction(action)
self.present(alert, animated: true)


但是今天有一個需求要客製化字體大小、內嵌連接在裡面🫨 同時要保留原生Alert高度隨著公告字數增長的特性,增長到最大範圍後才提供scroll操作

原生Alert高度會隨字數增長

原生Alert高度會隨字數增長


使用SwiftUI客製

覺得客製化的需求實在很難改,官方也講了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重刻一個

基本UI

基本UI


這邊為了方便看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

加上scrollView


長的內容可以捲動了,但是scrollView的高度卻固定在最高限制🫨

去查SwiftUI的scrollView要如何設定捲動範圍,查到了fixedSize這個屬性

這邊官方範例就解釋得很清楚,會幫你把元件調整成適合的大小
在scrollView加上這個屬性,並固定最高高度為500

ScrollView {
   // ....
}
.frame(maxHeight: 500)
.fixedSize(horizontal: false, vertical: true)
//...


完美啦

完美啦

完整code

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
完整Alert

完整Alert




6會員
35內容數
紀錄iOS開發上遇到的問題或是一些流程筆記。主要都是Swift。
留言0
查看全部
發表第一個留言支持創作者!
Michelle Chen的沙龍 的其他內容
本文介紹瞭如何在SwiftUI中調整元件的對齊方式,包括置中、向左/向右/向上/向下對齊的方法。透過調整HStack、VStack以及frame的maxWidth、maxHeight和alignment屬性,可以達到想要的對齊效果。
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
UIEdgeInsets 是一個結構體,用來表示矩形的邊距。它通常在視圖佈局中使用,用於修改視圖的框架(frame)。邊距值可以是正數或負數,這會對矩形的大小產生不同的影響。 UIEdgeInsets 結構體 UIEdgeInsets 結構體包含四個屬性: top:矩形頂部的邊距 left:
iOS15推出了新的按鈕外觀設定功能,讓設定變得更加直觀。使用不同的圓角設定和圖片位置可以輕鬆創建不同風格的按鈕。另外,新的選取效果變化功能也讓按鈕設計更加靈活。本文將介紹這些新功能的使用方法和效果。
本文介紹如何使用UINavigationBarAppearance調整四種場景下的UI外觀,並探討客製化返回鍵UI又保留返回手勢的做法,可以有效地客製化NavigationBar的外觀,並避免一些NG作法。
這篇文章介紹了 Swift 中字串的比較方法,並討論了使用日期字串進行比較的結果。同時也介紹了數字字串、符號字串和表情符號字串的比較原理。最後指出比較日期字串還是要轉成Date才是安全的做法。
本文介紹瞭如何在SwiftUI中調整元件的對齊方式,包括置中、向左/向右/向上/向下對齊的方法。透過調整HStack、VStack以及frame的maxWidth、maxHeight和alignment屬性,可以達到想要的對齊效果。
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
UIEdgeInsets 是一個結構體,用來表示矩形的邊距。它通常在視圖佈局中使用,用於修改視圖的框架(frame)。邊距值可以是正數或負數,這會對矩形的大小產生不同的影響。 UIEdgeInsets 結構體 UIEdgeInsets 結構體包含四個屬性: top:矩形頂部的邊距 left:
iOS15推出了新的按鈕外觀設定功能,讓設定變得更加直觀。使用不同的圓角設定和圖片位置可以輕鬆創建不同風格的按鈕。另外,新的選取效果變化功能也讓按鈕設計更加靈活。本文將介紹這些新功能的使用方法和效果。
本文介紹如何使用UINavigationBarAppearance調整四種場景下的UI外觀,並探討客製化返回鍵UI又保留返回手勢的做法,可以有效地客製化NavigationBar的外觀,並避免一些NG作法。
這篇文章介紹了 Swift 中字串的比較方法,並討論了使用日期字串進行比較的結果。同時也介紹了數字字串、符號字串和表情符號字串的比較原理。最後指出比較日期字串還是要轉成Date才是安全的做法。
你可能也想看
Thumbnail
「設計不僅僅是外觀和感覺。設計是其運作的方式。」 — Steve Jobs 身為一個獨立文案,許多人會以為我們的生活只需要面對電腦,從無到有,用精巧的文字填滿空白的螢幕,呈現心目中獨具風格的作品。 ——有的時候可以如此,但其實這是我們夢寐以求的偶發日常。 更多的時候,白天的工作時間總被各種繁雜
Thumbnail
台股、美股近期明顯回檔,市場敘事發生改變,壞消息一樁接一樁出現,下一步該怎麼走呢?本文將探討近期的宏觀經濟事件,並分享個人的操作思考。
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式學會調節中介模式。本文將介紹四種類型的變項,並解釋調節式中介的公式,還有如何操作最4.0版本的PROCESS macro for SPSS。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式使用調節分析。本文將介紹三種類型的變項,還有如何操作最4.2版本的PROCESS macro for SPSS進行調節模式。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式進中介模式。本文將介紹三種類型的變項,還有如何操作最4.0版本的PROCESS macro for SPSS。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
Potato Media雖然和方格子及Matters同樣歸類為寫作平台,同樣強調將內容變現,前者卻與後面兩者完全不同,當然,所獲得的收入報酬也不會一樣,更清楚一點來說,連獲得收益的方式也大不相同。
Thumbnail
我們將介紹各種類型的信度和統計方法,包含Cohen Kappa 係數、組內相關係數、α係數的SPSS教學。信度的可以使用不同的評估方法來評估。信度對於確定評分標準或量表的一致性和穩定度至關重要。
Thumbnail
如果依變項並非連續變項時,就可以改用羅吉斯迴歸。接下來本文將介紹勝算、勝算比、計算範例、二元/順序/多項式羅吉斯迴歸分析範例和SPSS操作方法。
Thumbnail
通常我們對於類別變項就直接看敘述統計大小,但如果我們想要用檢定確定兩者差距是達到統計顯著,就要用卡方檢定(Chi-square test)是一種統計學方法,獨立性考驗用於檢驗兩個類別變項各組別之間是否有顯著關聯。本文將介紹卡方檢定並介紹上機操作和事後比較方法。
Thumbnail
本篇介紹Mplus的「結構方程模型(Structural Equation Modelling, SEM)」之語法內容,並透過例題向大家示範如何分析撰寫SEM的語法。本文為新手教學,輸入方式可能不是最有效率,但是比較簡單且不太會犯錯
Thumbnail
當樣本有所關聯時,就不能使用獨立樣本t檢定,而是需要使用相依樣本t檢定,本文檢定介紹使用時機,並教導如何使用SPSS進行相依樣本t檢定
Thumbnail
「設計不僅僅是外觀和感覺。設計是其運作的方式。」 — Steve Jobs 身為一個獨立文案,許多人會以為我們的生活只需要面對電腦,從無到有,用精巧的文字填滿空白的螢幕,呈現心目中獨具風格的作品。 ——有的時候可以如此,但其實這是我們夢寐以求的偶發日常。 更多的時候,白天的工作時間總被各種繁雜
Thumbnail
台股、美股近期明顯回檔,市場敘事發生改變,壞消息一樁接一樁出現,下一步該怎麼走呢?本文將探討近期的宏觀經濟事件,並分享個人的操作思考。
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式學會調節中介模式。本文將介紹四種類型的變項,並解釋調節式中介的公式,還有如何操作最4.0版本的PROCESS macro for SPSS。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式使用調節分析。本文將介紹三種類型的變項,還有如何操作最4.2版本的PROCESS macro for SPSS進行調節模式。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
PROCESS macro for SPSS 可以用非常簡單方式進中介模式。本文將介紹三種類型的變項,還有如何操作最4.0版本的PROCESS macro for SPSS。文末也會附上所有所有Process模型圖例,提供給讀者方便分析~
Thumbnail
Potato Media雖然和方格子及Matters同樣歸類為寫作平台,同樣強調將內容變現,前者卻與後面兩者完全不同,當然,所獲得的收入報酬也不會一樣,更清楚一點來說,連獲得收益的方式也大不相同。
Thumbnail
我們將介紹各種類型的信度和統計方法,包含Cohen Kappa 係數、組內相關係數、α係數的SPSS教學。信度的可以使用不同的評估方法來評估。信度對於確定評分標準或量表的一致性和穩定度至關重要。
Thumbnail
如果依變項並非連續變項時,就可以改用羅吉斯迴歸。接下來本文將介紹勝算、勝算比、計算範例、二元/順序/多項式羅吉斯迴歸分析範例和SPSS操作方法。
Thumbnail
通常我們對於類別變項就直接看敘述統計大小,但如果我們想要用檢定確定兩者差距是達到統計顯著,就要用卡方檢定(Chi-square test)是一種統計學方法,獨立性考驗用於檢驗兩個類別變項各組別之間是否有顯著關聯。本文將介紹卡方檢定並介紹上機操作和事後比較方法。
Thumbnail
本篇介紹Mplus的「結構方程模型(Structural Equation Modelling, SEM)」之語法內容,並透過例題向大家示範如何分析撰寫SEM的語法。本文為新手教學,輸入方式可能不是最有效率,但是比較簡單且不太會犯錯
Thumbnail
當樣本有所關聯時,就不能使用獨立樣本t檢定,而是需要使用相依樣本t檢定,本文檢定介紹使用時機,並教導如何使用SPSS進行相依樣本t檢定