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

一、系统弹窗

在 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.mfbz.cn/a/352895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring 的存储和获取Bean

文章目录 获取 Spring 上下文对象的方式存储 Bean 对象的方式类注解配置扫描路径&#xff08;必须&#xff09;Controller&#xff08;控制器存储&#xff09;Service&#xff08;服务&#xff09;Repository&#xff08;持久层&#xff09;Component&#xff08;工具&#xff…

【Spring】Spring简介、IOC、DI

目录 Spring简介 Spring Framework五大功能模块 IOC容器 IOC思想 IOC容器在Spring中的实现 基于XML管理bean 配置bean 获取bean 依赖注入之setter注入 依赖注入之构造器注入 特殊值处理 字面量赋值 null值 xml实体 CDATA节 为类类型属性赋值 为数组类型属性赋值 为集合类型属性…

JavaScript 学习笔记(JS进阶 Day1)

「写在前面」 本文为 b 站黑马程序员 pink 老师 JavaScript 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. JavaScript 学习笔记&#xff08;Day1&#xff09; 2. JavaSc…

适用于 Windows 11 的 12 个最佳免费 PDF 编辑器

除了绘图等基本功能外&#xff0c;一些适用于 Windows 11 的免费 PDF 编辑器还具有 AI、OCR 识别和书签等高级功能。 我们的列表包含易于立即下载的 PDF 编辑软件工具。 这些工具不仅可以帮助转换 PDF、编辑、上传、删除、裁剪、分割、提取等。 PDF 是指便携式文档格式&…

单片机学习笔记---独立按键控制LED亮灭

直接进入正题&#xff01; 今天开始我们要学习一个新的模块&#xff1a;独立按键&#xff01; 先说独立按键的内部结构&#xff1a; 它相当于一种电子开关&#xff0c;按下时开关接通&#xff0c;松开时开关断开&#xff0c;实现原理是通过轻触按键内部的金属弹片受力弹动来实…

剧本杀小程序开发:打造沉浸式推理体验

随着社交娱乐形式的多样化&#xff0c;剧本杀逐渐成为年轻人喜爱的聚会活动。而随着技术的发展&#xff0c;剧本杀小程序的开发也成为了可能。本文将探讨剧本杀小程序开发的必要性、功能特点、开发流程以及市场前景。 一、剧本杀小程序开发的必要性 剧本杀是一种角色扮演的推…

鸿蒙端云一体化简单项目

文章目录 前言端云一体化服务端客户端云数据库总结 一、前言 鸿蒙系统在不断地成熟&#xff0c;现在有了鸿蒙端云一体化开发模式。什么是端云一体化呢&#xff0c;简单点就是你原本是客户端开发的&#xff0c;项目中只是客户端的代码&#xff0c;端云一体化呢&#xff0c;就…

【MyBatis】#{} 和 ${}

目录 1. #{} 使用示例&#xff1a; 2. ${} 使用示例&#xff1a; SQL注入 使用#{}的情况&#xff1a; 使用${}的情况&#xff1a; MyBatis是一种用于Java语言的持久层框架&#xff0c;它简化了数据库操作的过程。在MyBatis中&#xff0c;我们经常会看到两种不同的参数占…

华为云WAF,开启web网站的专属反爬虫防护罩

背景 从保护原创说起 作为一个原创技术文章分享博主&#xff0c;日常除了Codeing就是总结Codeing中的技术经验。 之前并没有对文章原创性的保护意识&#xff0c;直到在某个非入驻的平台看到了我的文章&#xff0c;才意识到&#xff0c;辛苦码字、为灵感反复试验创作出来的文…

苹果macOS 恶意软件家族被曝光:通过破解软件分发,可窃取敏感信息

卡巴斯基安全实验室近日发布博文&#xff0c;发现了一种针对苹果 macOS 设备的新型恶意软件家族&#xff0c;并提醒苹果 Mac 用户谨慎下载破解软件。 报告称这种新型恶意软件家族高度复杂&#xff0c;主要伪装成为各种知名 macOS 软件的破解版分发&#xff0c;用户下载恶意 PKG…

ISO 14229和UDS:汽车诊断的黄金标准

UDS简介&#xff1a; UDS是Unified Diagnostic Services的缩写&#xff0c;全名统一诊断服务。它是一种用于汽车电子控制单元&#xff08;ECU&#xff09;之间进行诊断和通信的标准协议&#xff0c;属于ISO 14229标准的一部分。 UDS的起源和背景&#xff1a; UDS的起源可以追…

HarmonyOS 鸿蒙应用开发 (七、HTTP网络组件 axios 介绍及封装使用)

在HarmonyOS应用开发中&#xff0c;通过HTTP访问网络&#xff0c;可以使用官方提供的ohos.net.http模块。但是官方提供的直接使用不太好使用&#xff0c;需要封装下才好。推荐使用前端开发中流行的axios网络客户端库&#xff0c;如果是前端开发者&#xff0c;用 axios也会更加顺…

Java笔记(死锁、线程通信、单例模式)

一、死锁 1.概述 死锁 : 死锁是指两个或两个以上的进程在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成的一种阻塞的现象&#xff0c;若无外力作用&#xff0c;它们都将无法往下执行。此时称系统处于死锁状态或系统产生了死锁&#xff0c;这些永远在互相等待的进…

vusui css 使用,简单明了 适合后端人员 已解决

vusui-cssopen in new window 免除开发者繁复的手写 CSS 样式&#xff0c;让 WEB 前端开发更简单、灵活、便捷&#xff01;如果喜欢就点个 ★Staropen in new window 吧。 移动设备优先&#xff1a; vusui-css 包含了贯穿于整个库的移动设备优先的样式。浏览器支持&#xff1a…

【每日一题】最大合金数

文章目录 Tag题目来源解题思路方法一&#xff1a;二分枚举答案 写在最后 Tag 【二分枚举答案】【数组】【2024-01-27】 题目来源 2861. 最大合金数 解题思路 方法一&#xff1a;二分枚举答案 思路 如果我们可以制造 x 块合金&#xff0c;那么一定也可以制造 x-1 块合金。于…

《合成孔径雷达成像算法与实现》Figure5.18

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

【C++干货铺】C++中的IO流和文件操作

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 C语言的输入输出 流是什么&#xff1f; C的IO流 C标准IO流 C文件IO流 文本文件读写 二进制文件的读写 stringstream的简单介绍 将数值类型数据格式化为字…

JS中的try...catch

一、定义和结构 作用&#xff1a;捕获同步执行代码下的异常错误 在没有使用try...catch的情况下&#xff0c;同步代码执行遇到异常会报错&#xff0c;并中断后续代码执行&#xff1b; 在使用try...catch的情况下&#xff0c;同步代码执行遇到异常会抛出异常&#xff0c;并继续…

线性代数----------学习记录

线性代数发展历程 &#xff08;1&#xff09;线性方程组&#xff1a;例如二元一次方程组&#xff1b; &#xff08;2&#xff09;行列式&#xff1a;determinant,克莱默&#xff0c;莱布尼兹&#xff1b; &#xff08;3&#xff09;矩阵&#xff1a;方程个数与未知数的个数可…

【前端工程化】环境搭建 nodejs npm

文章目录 前端工程化是什么&#xff1f;前端工程化实现技术栈前端工程化环境搭建 &#xff1a;什么是Nodejs如何安装nodejsnpm 配置和使用npm 介绍npm 安装和配置npm 常用命令 总结 前端工程化是什么&#xff1f; 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块…