[SwiftUI]系统弹窗和自定义弹窗

news2024/12/25 0:25:19

一、系统弹窗

在 SwiftUI 中,.alert 是一个修饰符,用于在某些条件下显示一个警告对话框。Alert 可以配置标题、消息和一系列的按钮。每个按钮可以是默认样式、取消样式,或者是破坏性的样式,它们分别对应不同的用户操作。

1.Alert

基本用法

只显示一个按钮,通常用于确认消息。

struct ContentView: View {
    @State private var showAlert = false

    var body: some View {
        Button("Show Basic Alert") {
            showAlert = true
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Basic Alert"),
                  message: Text("This is a basic alert with a single button."),
                  dismissButton: .default(Text("OK")))
        }
    }
}

示意图:

注意:

Alert 的 titlemessage 和 dismissButton 的样式(如颜色、字体)是由系统控制的,不可以直接通过修改 Text 视图的属性来改变。这是为了确保 Alert 对话框保持一致的系统外观和行为,也为了确保它符合当前的操作系统主题,无论是暗模式还是亮模式。

因此,即使你尝试在 Text 视图中使用 .foregroundColor 或 .font 等修饰符,这些样式也不会应用到 Alert 中的 Text 上。

如果你需要定制的弹窗样式,需要创建一个自定义的弹窗视图,并使用 .sheet 或者其他视图容器来显示它。这样就可以完全控制弹窗视图的外观和布局,见下面自定义弹窗部分。

多个按钮

展示多个按钮,包括取消按钮和其他操作。

struct ContentView: View {
    @State private var showAlert = false

    var body: some View {
        Button("Show Alert with Multiple Buttons") {
            showAlert = true
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("Multiple Buttons"),
                  message: Text("This alert has multiple buttons."),
                  primaryButton: .destructive(Text("Delete")) {
                    // Handle delete action
                  },
                  secondaryButton: .cancel())
        }
    }
}

示意图:

.alert(isPresented: $showAlert) {
    Alert(title: Text("Multiple Buttons"),
          message: Text("This alert has multiple buttons."),
          primaryButton: .cancel(Text("NO")) {
             
          },
          secondaryButton: .default(Text("Logout")) {
             
          }
    )
}

示意图:

注意:

Alert中最多只能添加两个按钮,被.cancel修饰的按钮一定会加粗并展示在左边,

.cancel最多只能修饰一个按钮,同时修饰两个按钮时会报错
"UIAlertController can only have one action with a style of UIAlertActionStyleCancel"

使用枚举来显示不同的Alerts

使用 Identifiable 协议来区分不同的警告类型,根据不同的情况显示不同的警告。

struct ContentView: View {
    @State private var alertType: AlertType? = nil
    enum AlertType: Identifiable {
        case first, second
        
        var id: Int {
            hashValue
        }
    }

    var body: some View {
        VStack {
            Button("Show First Alert") {
                alertType = .first
            }

            Button("Show Second Alert") {
                alertType = .second
            }
        }
        .alert(item: $alertType) { type -> Alert in
            switch type {
                case .first:
                    return Alert(
                        title: Text("First Alert"),
                        message: Text("This is the first alert."),
                        dismissButton: .default(Text("OK"))
                    )
                case .second:
                    return Alert(title: Text("Second Alert"),
	                   	message: Text("This is the second alert."),
	                  	primaryButton: .default(Text("NO")) {
	                          
	                  	},
	                   	secondaryButton: .default(Text("YES")) {
	                          
	                  	}
					)
            }
        }
    }
}

这种方式也比较常用,比如同一个页面有多个弹窗时,总不至于傻兮兮去定义多个@State吧。

2.ActionSheet

ActionSheet 是一种展示给用户一组操作或选择列表的方式。它与 Alert 类似,但通常用于提供两个或更多选项。ActionSheet 在 iPad 上以弹出的形式呈现,在 iPhone 上则从屏幕底部滑出。

import SwiftUI

struct ContentView: View {
    @State private var showActionSheet = false

    var body: some View {
        Button("Show Action Sheet") {
            showActionSheet = true
        }
        .actionSheet(isPresented: $showActionSheet) {
            ActionSheet(
                title: Text("What do you want to do?"),
                message: Text("There's only one option..."),
                buttons: [
                    .default(Text("Option 1")) {
                        // Handle Option 1 action
                    },
                    .destructive(Text("Delete")) {
                             
                    },
                    .cancel()
                ]
            )
        }
    }
}

示意图:

注意:

与Alert一样,被.cancel修饰的按钮会被加粗并固定在底部,且最多只能有一个按钮被.cancel修饰。

二、自定义弹窗

1.使用 Overlay 创建自定义弹窗

Overlay 是一个视图修饰符,它可以用来在现有视图上层添加一个新的视图层。

import SwiftUI

struct ContentView: View {
    // 弹窗的显示状态
    @State private var showingPopup = false

    var body: some View {
        VStack {
            // 主视图内容
            Button("Show Popup") {
                withAnimation {
                    showingPopup.toggle()
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.white)
        }
        // 在这里使用 .overlay 添加弹窗
        .overlay(
            // 判断是否显示弹窗
            showingPopup ? popupOverlayView : nil
        )
    }
     
    // 弹窗的视图
    var popupOverlayView: some View {
         VStack {
             Spacer()

             // 弹窗内容
             VStack {
                 Text("Basic Alert")
                     .font(.headline)
                     .padding()
                 
                 Text("This is a basic alert with a single button.")
                     .multilineTextAlignment(.center)
                     .padding(.horizontal, 10)


                 Button("Dismiss") {
                     withAnimation {
                         showingPopup = false
                     }
                 }
                 .padding()
             }
             .frame(maxWidth: .infinity, minHeight: 200)
             .background(Color.white)
             .cornerRadius(12)
             .shadow(radius: 8)
             .padding(.horizontal, 30)

             Spacer()
         }
         .background(
             // 背景遮罩
             Color.black.opacity(0.5)
                 .edgesIgnoringSafeArea(.all)
                 .onTapGesture {
                     withAnimation {
                         showingPopup = false
                     }
                 }
         )
     }
}

示意图:

2.使用 ZStack 创建自定义弹窗

ZStack 是一个用来叠加视图的容器,它可以让你在同一个屏幕坐标空间中放置多个视图。当你使用 ZStack 创建弹窗时,你通常会在同一视图层次中添加弹窗视图和背景遮罩。这种方式直观且容易理解,尤其是当你的弹窗视图需要位于内容的正中央时。

基础用法

import SwiftUI

struct ContentView: View {
    @State private var showingPopup = false

    var body: some View {
        ZStack {
            // 主视图内容
            Button("Show Popup") {
                showingPopup.toggle()
            }

            if showingPopup {
                // 弹窗背景
                Color.black.opacity(0.4)
                    .edgesIgnoringSafeArea(.all)
                    .onTapGesture {
                        showingPopup = false
                    }
                // 弹窗内容
                CustomPopupView(showingPopup: $showingPopup)
                    .frame(width: 300, height: 200)
                    .background(Color.white)
                    .cornerRadius(10)
                    .shadow(radius: 10)
                    .position(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2)
                    .transition(.scale)
            }
        }
        .animation(.easeInOut, value: showingPopup)
    }
}

struct CustomPopupView: View {
    @Binding var showingPopup: Bool

    var body: some View {
        VStack {
            Text("Basic Alert")
                .font(.headline)
                .padding()
            Text("This is a basic alert with a single button.")
                .frame(alignment: .center)
                .multilineTextAlignment(.center)
                .font(.body)
                .padding()
            Spacer()

            Button("Dismiss") {
                // 传递动作以关闭弹窗
                showingPopup = false
                // 可能需要使用一个绑定变量或闭包
            }
            .padding(.bottom)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.white)
        .cornerRadius(20)
        .shadow(radius: 20)
    }
}

示意图:

 

封装后使用

import SwiftUI
 
struct ContentView: View {
  
    @State private var showAlertType: CustomAlertType? = nil
    
    var body: some View {
        VStack {
             Button("Show Alert") {
                showAlertType = .Alert
            }
        }
        .customAlert($showAlertType, message: "toastText")
    }
    
}
enum CustomAlertType: Int {
    case None = -1
    
    case Loading = 100
    case Toast = 101
    case Alert = 102
}

extension View {
    
    /// 自定义弹窗
    func customAlert(_ alertType: Binding<CustomAlertType?>, message: String = "" , finish: ((String?) -> ())? = nil) -> some View {
        ZStack {
            self
            let type = alertType.wrappedValue
            if type != nil && type != .None {
                if type == .Alert {
                    Color.black.opacity(0.3).edgesIgnoringSafeArea(.all)
                        .onTapGesture {
                            alertType.wrappedValue = .None
                        }
                    CustomPopupView(showAlertType: alertType, message: message, finish: finish)
                        .popupAnimation()
                } else if type == .Toast {
                    
                }
            }
        }
    }
    
}

struct PopupAnimationModifier: ViewModifier {
    @State private var isVisible: Bool = false

    func body(content: Content) -> some View {
        content
            .scaleEffect(isVisible ? 1 : 0.9)
            .onAppear {
                withAnimation(.linear(duration: 0.15)) { // easeIn easeInOut easeOut linear
                    isVisible = true
                }
            }
    }
}

extension View {
    
    func popupAnimation() -> some View {
          self.modifier(PopupAnimationModifier())
    }
    
}
import SwiftUI

struct CustomPopupView: View {
    @Binding var showAlertType: CustomAlertType?
    @State var message: String
    var finish: ((String?) -> ())? = nil

    var body: some View {
        VStack {
            Text("Alert")
                .font(.headline)
                .padding()
            Text(message)
                .frame(alignment: .center)
                .multilineTextAlignment(.center)
                .font(.body)
                .padding()
            Spacer()

            Button("OK") {
                showAlertType = nil
                finish?(nil)
            }
            .padding(.bottom)
        }
        .frame(maxWidth: 300, maxHeight: 200)
        .background(Color.white)
        .cornerRadius(20)
        .shadow(radius: 20)
    }
}

示意图:

3. 使用 sheet 创建半屏自定义弹窗

sheet 是一个用来展示一个新视图的修饰符,这个新视图会覆盖在当前视图上。通常 sheet 用于导航到另一个视图,比如详情页、编辑表单或者是分享菜单等。

sheet 修饰符可以通过多种方式使用,其中包括基于布尔值的呈现、使用可选的绑定对象来管理呈现以及使用标识符进行呈现。

.sheet(isPresented:)

基于布尔值的呈现

import SwiftUI

struct ContentView: View {
    @State private var showingSheet = false

    var body: some View {
        Button("Show Sheet") {
            showingSheet.toggle()
        }
        .sheet(isPresented: $showingSheet) {
            // Sheet 的内容
            SheetView()
        }
    }
}

struct SheetView: View {
    var body: some View {
        Text("Here's the sheet!")
    }
}

.sheet(item:) 

使用可选的绑定对象
import SwiftUI

struct ContentView: View {
    @State private var selectedUser: User? = nil

    var body: some View {
        Button("Show Sheet") {
            selectedUser = User(name: "John Doe") // 假设 User 是一个简单的数据模型
        }
        .sheet(item: $selectedUser) { user in
            // Sheet 的内容
            UserDetailsView(user: user)
        }
    }
}

struct User: Identifiable {
    let id = UUID()
    let name: String
}

struct UserDetailsView: View {
    var user: User

    var body: some View {
        Text("User Details for \(user.name)")
    }
}
使用标识符进行呈现
import SwiftUI

struct ContentView: View {
    @State private var activeSheet: SheetType? = nil

    var body: some View {
        VStack {
            Button("Show First Sheet") {
                activeSheet = .first
            }
            Button("Show Second Sheet") {
                activeSheet = .second
            }
        }
        .sheet(item: $activeSheet) { item in
            // 根据不同的标识符显示不同的视图
            switch item {
            case .first:
                FirstSheetView()
            case .second:
                SecondSheetView()
            }
        }
    }
}

enum SheetType: Identifiable {
    case first, second

    var id: Self { self }
}

struct FirstSheetView: View {
    var body: some View {
        Text("This is the first sheet")
    }
}

struct SecondSheetView: View {
    var body: some View {
        Text("This is the second sheet")
    }
}

示意图:

 

4.使用 fullScreenCover 创建全屏自定义弹窗

fullScreenCover 是一个视图修饰符,它用于展示一个全屏的覆盖视图。这个修饰符通常用于呈现一个全屏弹窗,比如登录页面、介绍页面或者任何需要从当前视图完全转移焦点的场景。

.fullScreenCover(isPresented:)

基于布尔值的呈现

import SwiftUI

struct ContentView: View {
    // 管理全屏弹窗的显示状态
    @State private var showingFullScreenPopup = false

    var body: some View {
        // 主视图的内容
        Button("Show Full Screen Popup") {
            // 显示全屏弹窗
            showingFullScreenPopup = true
        }
        // 使用 fullScreenCover 修饰符来显示全屏弹窗
        .fullScreenCover(isPresented: $showingFullScreenPopup) {
            // 传递 isPresented 绑定到弹窗视图,以便可以关闭弹窗
            FullScreenPopupView(isPresented: $showingFullScreenPopup)
        }
    }
}

// 自定义全屏弹窗视图
struct FullScreenPopupView: View {
    // 绑定变量,用于控制弹窗的显示与隐藏
    @Binding var isPresented: Bool
    
    var body: some View {
        ZStack {
            // 弹窗的背景
            Color.blue.edgesIgnoringSafeArea(.all)
            
            // 弹窗的内容
            VStack {
                Text("This is a full screen popup!")
                    .font(.largeTitle)
                    .foregroundColor(.white)
                    .padding()

                Button("Dismiss") {
                    // 关闭弹窗
                    isPresented = false
                }
                .font(.title)
                .padding()
                .background(Color.white)
                .foregroundColor(.blue)
                .cornerRadius(10)
            }
        }
    }
}

.fullScreenCover(item:)

import SwiftUI

struct ContentView: View {
    // 用于控制全屏弹窗的状态
    @State private var selectedFullScreenItem: FullScreenItem?
    
    var body: some View {
        VStack(spacing: 20) {
            // 触发全屏弹窗的按钮
            Button("Show FullScreen Cover") {
                selectedFullScreenItem = FullScreenItem(id: 2)
            }
        }
        // 全屏弹窗修饰符
        .fullScreenCover(item: $selectedFullScreenItem) { item in
            FullScreenCoverView(fullScreenItem: item)
        }
    }
}

// 用于全屏弹窗的数据模型
struct FullScreenItem: Identifiable {
    let id: Int
}

// 用于全屏弹窗的视图
struct FullScreenCoverView: View {
    var fullScreenItem: FullScreenItem
    
    var body: some View {
        VStack {
            Text("FullScreen Cover View with item id: \(fullScreenItem.id)")
            Spacer()
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.blue)
    }
}

示意图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1415936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Power ModeII 插件的下载与使用-----idea

下载 Marketplace里面搜索下载即可 使用 下载后重启软件就可以用了 下面是一些关于Power ModeII &#xff0c;我的个性化设置截图 以及相关设置解释 插件或扩展的设置面板【用于给代码编辑器或集成开发环境&#xff08;IDE&#xff09;添加视觉效果】 主要设置 ENTER POWE…

【控制算法笔记】卡尔曼滤波(二)——基于状态空间表达的KF基本计算流程以及Python实现

本文是个人学习笔记&#xff0c;包含个人理解&#xff0c;如有错误欢迎指正。 KF算法更多的情况下会用来处理复杂的非线性数据&#xff0c;尤其是当对象特征或检测的状态量不止一个时就得使用状态方程的方法&#xff0c;利用线性代数的计算方式来解决噪声的估计问题。这其中涉及…

uniapp微信小程序-请求二次封装(直接可用)

一、请求封装优点 代码重用性&#xff1a;通过封装请求&#xff0c;你可以在整个项目中重用相同的请求逻辑。这样一来&#xff0c;如果 API 发生变化或者需要进行优化&#xff0c;你只需在一个地方修改代码&#xff0c;而不是在每个使用这个请求的地方都进行修改。 可维护性&a…

Java基础数据结构之哈希表

概念 顺序结构以及平衡树 中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在 查找一个元素时&#xff0c;必须要经过关键 码的多次比较 。 顺序查找时间复杂度为 O(N) &#xff0c;平衡树中为树的高度&#xff0c;即 O( log2N ) &#xff0c;搜索的效…

应急响应-内存分析

在应急响应过程中&#xff0c;除了上述几个通用的排查项&#xff0c;有时也需要对应响应服务器进行内存的提权&#xff0c;从而分析其中的隐藏进程。 内存的获取 内存的获取方法有如下几种&#xff1a; 基于用户模式程序的内存获取&#xff1b;基于内核模式程序的内存获取&a…

SparkSql---用户自定义函数UDFUDAF

文章目录 1.UDF2.UDAF2.1 UDF函数实现原理2.2需求:计算用户平均年龄2.2.1 使用RDD实现2.2.2 使用UDAF弱类型实现2.2.3 使用UDAF强类型实现 1.UDF 用户可以通过 spark.udf 功能添加自定义函数&#xff0c;实现自定义功能。 如&#xff1a;实现需求在用户name前加上"Name:…

lv14 内核内存管理、动态分频及IO访问 12

一、内核内存管理框架 内核将物理内存等分成N块4KB&#xff0c;称之为一页&#xff0c;每页都用一个struct page来表示&#xff0c;采用伙伴关系算法维护 补充&#xff1a; Linux内存管理采用了虚拟内存机制&#xff0c;这个机制可以在内存有限的情况下提供更多可用的内存空…

docker compose实现mysql一主多从

参考了很多博客&#xff0c;死磕了几天&#xff0c;最终跑起来了&#xff0c;不容易&#xff0c;晚上喝瓶82年可乐庆祝下。 1、整体文件结构&#xff0c;这里忽略log、conf、data映射目录 2、docker-compose.yml文件内容如下&#xff1a; version: 3.3 services:mysql-master…

Android App开发-简单控件(3)——常用布局

3.3 常用布局 本节介绍常见的几种布局用法&#xff0c;包括在某个方向上顺序排列的线性布局&#xff0c;参照其他视图的位置相对排列的相对布局&#xff0c;像表格那样分行分列显示的网格布局&#xff0c;CommonLayouts以及支持通过滑动操作拉出更多内容的滚动视图。 3.3.1 线…

代码随想录算法训练DAY29|回溯5

算法训练DAY29|回溯5 491.递增子序列 力扣题目链接 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] 说…

【QT】QPainter基本绘图

目录 1 QPainter绘图系统 1.1 QPainter与QPaintDevice 1.2 paintEvent事件和绘图区 1.3 QPainter绘图的主要属性 1.4 创建实例 2 QPen的主要功能 2.1 线条样式 2.2 线条端点样式 2.3 线条连接样式 3 QBrush的主要功能 4 渐变填充 5 QPainter绘制基本图形元件 5.1 基本图形元件 …

burp靶场--身份认证漏洞

burp靶场–身份认证漏洞 https://portswigger.net/web-security/authentication#what-is-authentication 1.身份认证漏洞&#xff1a; ### 身份验证漏洞 从概念上讲&#xff0c;身份验证漏洞很容易理解。然而&#xff0c;由于身份验证和安全性之间的明确关系&#xff0c;它们…

基于Java SSM框架实现学校招生信息网系统项目【项目源码+论文说明】

基于java的SSM框架实现学生招生信息网系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;学校招生信息网当然也不能排除在外。学校招生信息网是以实际运用为开发背…

【大数据】Flink 中的状态管理

Flink 中的状态管理 1.算子状态2.键值分区状态3.状态后端4.有状态算子的扩缩容4.1 带有键值分区状态的算子4.2 带有算子列表状态的算子4.3 带有算子联合列表状态的算子4.4 带有算子广播状态的算子 在前面的博客中我们指出&#xff0c;大部分的流式应用都是有状态的。很多算子都…

OpenCV 0 - VS2019配置OpenCV

1 配置好环境变量 根据自己的opencv的安装目录配置 2 新建一个空项目 3 打开 视图->工具栏->属性管理器 4 添加新项目属性表 右键项目名(我这是opencvdemo)添加新项目属性表,如果有配置好了的属性表选添加现有属性表 5 双击选中Debug|x64的刚添加的属性表 6 (重点)添…

[LVGL] 可点击的文字label

LVGL8.x 自带的label 是没有点击响应的功能&#xff0c;即使加了lv_obj_add_event_cb 也不起作用&#xff0c;为了解决这个问题&#xff0c;我们使用了按钮控件去模拟纯label的效果&#xff1b;有了这个demo用户就可以实现类似超链接 点击一个文字就跳转到某个页面的功能。 st…

Kotlin 教程(环境搭建)

Kotlin IntelliJ IDEA环境搭建 IntelliJ IDEA 免费的社区版下载地址&#xff1a;Download IntelliJ IDEA – The Leading Java and Kotlin IDE 下载安装后&#xff0c;我们就可以使用该工具来创建项目&#xff0c;创建过程需要选择 SDK&#xff0c; Kotlin 与 JDK 1.6 一起使…

【大数据】详解 Flink 中的 WaterMark

详解 Flink 中的 WaterMark 1.基础概念1.1 流处理1.2 乱序1.3 窗口及其生命周期1.4 Keyed vs Non-Keyed1.5 Flink 中的时间 2.Watermark2.1 案例一2.2 案例二2.3 如何设置最大乱序时间2.4 延迟数据重定向 3.在 DDL 中的定义3.1 事件时间3.2 处理时间 1.基础概念 1.1 流处理 流…

1.【Vue3】前端开发引入、Vue 简介

1. 前端开发引入 1.1 前端开发前置知识 通过之前的学习&#xff0c;已经通过 SpringBoot 和一些三方技术完成了大事件项目的后端开发。接下来开始学习大事件项目的前端开发&#xff0c;前端部分借助两个框架实现&#xff1a; Vue3&#xff08;一个 JS 框架&#xff09;基于 …

Vue-Router: 如何使用路由元信息来管理路由?

Vue-Router是Vue.js官方的路由管理器&#xff0c;它可以帮助我们快速构建单页应用程序&#xff08;SPA&#xff09;。除了常见的路由功能外&#xff0c;Vue-Router还支持使用路由元信息来管理和控制路由。路由元信息是可以附加到路由上的自定义属性&#xff0c;它可以帮助我们实…