├── .gitignore ├── LICENSE ├── README.md ├── XYZAlert.podspec ├── XYZAlert.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── XYZAlert ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Classes ├── Dispatch │ ├── XYZAlertDispatch.h │ ├── XYZAlertDispatch.m │ ├── XYZAlertProtocol.h │ ├── XYZAlertQueue.h │ └── XYZAlertQueue.m ├── UIViewController+XYZAlert.h ├── UIViewController+XYZAlert.m ├── XYZAlertView.h ├── XYZAlertView.m ├── XYZSystemAlertView.h └── XYZSystemAlertView.m ├── Info.plist ├── SceneDelegate.h ├── SceneDelegate.m ├── ViewController.h ├── ViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> macOS 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two \r 7 | Icon 8 | 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | 28 | # ---> Xcode 29 | # Xcode 30 | # 31 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 32 | 33 | ## Build generated 34 | build/ 35 | DerivedData 36 | 37 | ## Various settings 38 | *.pbxuser 39 | !default.pbxuser 40 | *.mode1v3 41 | !default.mode1v3 42 | *.mode2v3 43 | !default.mode2v3 44 | *.perspectivev3 45 | !default.perspectivev3 46 | xcuserdata 47 | 48 | ## Other 49 | *.xccheckout 50 | *.moved-aside 51 | *.xcuserstate 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 XYZAlert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XYZAlertView 2 | 3 | SheetView见 https://github.com/DFTT/XYZSheetView 4 | 5 | ### 是什么 6 | - 实现了一个调度逻辑,用来对单页面(VC)中多个弹窗的管理```XYZAlertDispatch``` , 可以通过```vc.alertDispah```方便的访问. 7 | - 实现了一个UI基类```XYZAlertView```, 内部实现了调度协议```XYZAlertEnableDispatchProtocal```用来支持各种调度 8 | - 由于各项目UI差异, 可以继承实现自己的AlertView基类, AlertView可以单独脱离调度类使用(在无多弹窗场景, 可以直接用来展示/移除) 9 | - 继承```XYZAlertView```实现了一个类似系统的通用Alert```XYZSystemAlertView``` 10 | 11 | 12 | #### 已支持: 13 | 1. 调度队列颗粒度ViewController 14 | 2. 支持按照```优先级```顺序展示 (如果是后添加的高优先级弹窗, 可以根据配置 覆盖/隐藏 已显示的低优先级弹窗) 15 | 3. 支持各弹窗之间添加```依赖```, 依赖弹窗全部展示结束后, 再展示自己 16 | 4. 支持先添加进队列, 完成异步操作后再展示(比如请求数据后), 只需要在合适的时机调用```XYZAlertView: -setReadyAndTryDispath```即可, 状态可参见```XYZAlertView: curStete``` 17 | 5. 支持ViewController消失后, 自动隐藏当前VC绑定的弹窗 (目前直接hidden相应的Alert), ViewController再次出现时, 自动恢复未结束的Alert 18 | 6. 支持弹窗覆盖全屏, 即可以展示在非绑定的vc.view上, 可以自动控制弹窗仅覆盖当前VC.View/全屏/任意其他view(提前设置```XYZAlertView: showOnView```为一个可以可以覆盖全屏的View即可, 比如NavVC.view / TabVC.view / KeyWindow ...) 19 | 7. 支持自动躲避键盘, 自动向上偏移/恢复 (可修正偏移距离) 20 | 21 | #### 使用: 22 | - 源码安装: 拖拽XYZAlert文件夹到项目中即可 23 | - cocoapods: 24 | ``` 25 | source "https://github.com/DFTT/XYZPodspecs.git" 26 | pod 'XYZAlert' 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /XYZAlert.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint XYZAd.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | 10 | 11 | 12 | Pod::Spec.new do |s| 13 | s.name = 'XYZAlert' 14 | s.version = '1.1.8' 15 | s.summary = 'XYZAlert...' 16 | s.description = <<-DESC 17 | XYZAlert Description... 18 | DESC 19 | 20 | s.homepage = 'https://github.com/DFTT' 21 | s.license = { :type => 'MIT', :file => 'LICENSE' } 22 | s.author = { 'XYZAlert' => 'lidong@021.com' } 23 | s.source = { :git => 'https://github.com/DFTT/XYZAlertView.git', :tag => s.version.to_s } 24 | 25 | s.ios.deployment_target = '9.0' 26 | s.frameworks = 'Foundation', 'UIKit' 27 | 28 | # s.user_target_xcconfig = {'OTHER_LDFLAGS' => ['-lObjC']} 29 | # s.pod_target_xcconfig = {'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64'} 30 | 31 | s.requires_arc = true 32 | 33 | s.source_files = 'XYZAlert/Classes/**/*.{h,m}' 34 | s.public_header_files = 'XYZAlert/Classes/**/*.{h}' 35 | 36 | end 37 | -------------------------------------------------------------------------------- /XYZAlert.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5B02F3A42625812B0023FC84 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B02F3A32625812B0023FC84 /* AppDelegate.m */; }; 11 | 5B02F3A72625812B0023FC84 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B02F3A62625812B0023FC84 /* SceneDelegate.m */; }; 12 | 5B02F3AA2625812C0023FC84 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B02F3A92625812C0023FC84 /* ViewController.m */; }; 13 | 5B02F3AD2625812C0023FC84 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B02F3AB2625812C0023FC84 /* Main.storyboard */; }; 14 | 5B02F3AF2625812E0023FC84 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B02F3AE2625812E0023FC84 /* Assets.xcassets */; }; 15 | 5B02F3B22625812E0023FC84 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B02F3B02625812E0023FC84 /* LaunchScreen.storyboard */; }; 16 | 5B02F3B52625812E0023FC84 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B02F3B42625812E0023FC84 /* main.m */; }; 17 | 5B148BED272C0A6E00C01ABF /* XYZAlertQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B148BE1272C0A6E00C01ABF /* XYZAlertQueue.m */; }; 18 | 5B148BEE272C0A6E00C01ABF /* XYZAlertDispatch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B148BE3272C0A6E00C01ABF /* XYZAlertDispatch.m */; }; 19 | 5B148BEF272C0A6E00C01ABF /* UIViewController+XYZAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B148BE5272C0A6E00C01ABF /* UIViewController+XYZAlert.m */; }; 20 | 5B148BF0272C0A6E00C01ABF /* XYZAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B148BE7272C0A6E00C01ABF /* XYZAlertView.m */; }; 21 | 5B148BF1272C0A6E00C01ABF /* XYZSystemAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B148BE8272C0A6E00C01ABF /* XYZSystemAlertView.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 5B02F39F2625812B0023FC84 /* XYZAlert.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XYZAlert.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 5B02F3A22625812B0023FC84 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 27 | 5B02F3A32625812B0023FC84 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 28 | 5B02F3A52625812B0023FC84 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 29 | 5B02F3A62625812B0023FC84 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 30 | 5B02F3A82625812C0023FC84 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | 5B02F3A92625812C0023FC84 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | 5B02F3AC2625812C0023FC84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 5B02F3AE2625812E0023FC84 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 5B02F3B12625812E0023FC84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | 5B02F3B32625812E0023FC84 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 5B02F3B42625812E0023FC84 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | 5B148BDE272C0A6E00C01ABF /* XYZAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYZAlertView.h; sourceTree = ""; }; 38 | 5B148BE0272C0A6E00C01ABF /* XYZAlertDispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYZAlertDispatch.h; sourceTree = ""; }; 39 | 5B148BE1272C0A6E00C01ABF /* XYZAlertQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYZAlertQueue.m; sourceTree = ""; }; 40 | 5B148BE2272C0A6E00C01ABF /* XYZAlertQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYZAlertQueue.h; sourceTree = ""; }; 41 | 5B148BE3272C0A6E00C01ABF /* XYZAlertDispatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYZAlertDispatch.m; sourceTree = ""; }; 42 | 5B148BE4272C0A6E00C01ABF /* XYZAlertProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYZAlertProtocol.h; sourceTree = ""; }; 43 | 5B148BE5272C0A6E00C01ABF /* UIViewController+XYZAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+XYZAlert.m"; sourceTree = ""; }; 44 | 5B148BE6272C0A6E00C01ABF /* XYZSystemAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYZSystemAlertView.h; sourceTree = ""; }; 45 | 5B148BE7272C0A6E00C01ABF /* XYZAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYZAlertView.m; sourceTree = ""; }; 46 | 5B148BE8272C0A6E00C01ABF /* XYZSystemAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYZSystemAlertView.m; sourceTree = ""; }; 47 | 5B148BE9272C0A6E00C01ABF /* UIViewController+XYZAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+XYZAlert.h"; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 5B02F39C2625812B0023FC84 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 5B02F3962625812B0023FC84 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 5B02F3A12625812B0023FC84 /* XYZAlert */, 65 | 5B02F3A02625812B0023FC84 /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | 5B02F3A02625812B0023FC84 /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 5B02F39F2625812B0023FC84 /* XYZAlert.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 5B02F3A12625812B0023FC84 /* XYZAlert */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 5B148BDD272C0A6E00C01ABF /* Classes */, 81 | 5B02F3A22625812B0023FC84 /* AppDelegate.h */, 82 | 5B02F3A32625812B0023FC84 /* AppDelegate.m */, 83 | 5B02F3A52625812B0023FC84 /* SceneDelegate.h */, 84 | 5B02F3A62625812B0023FC84 /* SceneDelegate.m */, 85 | 5B02F3A82625812C0023FC84 /* ViewController.h */, 86 | 5B02F3A92625812C0023FC84 /* ViewController.m */, 87 | 5B02F3AB2625812C0023FC84 /* Main.storyboard */, 88 | 5B02F3AE2625812E0023FC84 /* Assets.xcassets */, 89 | 5B02F3B02625812E0023FC84 /* LaunchScreen.storyboard */, 90 | 5B02F3B32625812E0023FC84 /* Info.plist */, 91 | 5B02F3B42625812E0023FC84 /* main.m */, 92 | ); 93 | path = XYZAlert; 94 | sourceTree = ""; 95 | }; 96 | 5B148BDD272C0A6E00C01ABF /* Classes */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 5B148BDF272C0A6E00C01ABF /* Dispatch */, 100 | 5B148BE9272C0A6E00C01ABF /* UIViewController+XYZAlert.h */, 101 | 5B148BE5272C0A6E00C01ABF /* UIViewController+XYZAlert.m */, 102 | 5B148BDE272C0A6E00C01ABF /* XYZAlertView.h */, 103 | 5B148BE7272C0A6E00C01ABF /* XYZAlertView.m */, 104 | 5B148BE6272C0A6E00C01ABF /* XYZSystemAlertView.h */, 105 | 5B148BE8272C0A6E00C01ABF /* XYZSystemAlertView.m */, 106 | ); 107 | path = Classes; 108 | sourceTree = ""; 109 | }; 110 | 5B148BDF272C0A6E00C01ABF /* Dispatch */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 5B148BE0272C0A6E00C01ABF /* XYZAlertDispatch.h */, 114 | 5B148BE3272C0A6E00C01ABF /* XYZAlertDispatch.m */, 115 | 5B148BE4272C0A6E00C01ABF /* XYZAlertProtocol.h */, 116 | 5B148BE2272C0A6E00C01ABF /* XYZAlertQueue.h */, 117 | 5B148BE1272C0A6E00C01ABF /* XYZAlertQueue.m */, 118 | ); 119 | path = Dispatch; 120 | sourceTree = ""; 121 | }; 122 | /* End PBXGroup section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | 5B02F39E2625812B0023FC84 /* XYZAlert */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = 5B02F3B82625812E0023FC84 /* Build configuration list for PBXNativeTarget "XYZAlert" */; 128 | buildPhases = ( 129 | 5B02F39B2625812B0023FC84 /* Sources */, 130 | 5B02F39C2625812B0023FC84 /* Frameworks */, 131 | 5B02F39D2625812B0023FC84 /* Resources */, 132 | ); 133 | buildRules = ( 134 | ); 135 | dependencies = ( 136 | ); 137 | name = XYZAlert; 138 | productName = XYZAlert; 139 | productReference = 5B02F39F2625812B0023FC84 /* XYZAlert.app */; 140 | productType = "com.apple.product-type.application"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | 5B02F3972625812B0023FC84 /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastUpgradeCheck = 1240; 149 | TargetAttributes = { 150 | 5B02F39E2625812B0023FC84 = { 151 | CreatedOnToolsVersion = 12.4; 152 | }; 153 | }; 154 | }; 155 | buildConfigurationList = 5B02F39A2625812B0023FC84 /* Build configuration list for PBXProject "XYZAlert" */; 156 | compatibilityVersion = "Xcode 9.3"; 157 | developmentRegion = en; 158 | hasScannedForEncodings = 0; 159 | knownRegions = ( 160 | en, 161 | Base, 162 | ); 163 | mainGroup = 5B02F3962625812B0023FC84; 164 | productRefGroup = 5B02F3A02625812B0023FC84 /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | 5B02F39E2625812B0023FC84 /* XYZAlert */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXResourcesBuildPhase section */ 174 | 5B02F39D2625812B0023FC84 /* Resources */ = { 175 | isa = PBXResourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 5B02F3B22625812E0023FC84 /* LaunchScreen.storyboard in Resources */, 179 | 5B02F3AF2625812E0023FC84 /* Assets.xcassets in Resources */, 180 | 5B02F3AD2625812C0023FC84 /* Main.storyboard in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 5B02F39B2625812B0023FC84 /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 5B02F3AA2625812C0023FC84 /* ViewController.m in Sources */, 192 | 5B148BF1272C0A6E00C01ABF /* XYZSystemAlertView.m in Sources */, 193 | 5B02F3A42625812B0023FC84 /* AppDelegate.m in Sources */, 194 | 5B148BEE272C0A6E00C01ABF /* XYZAlertDispatch.m in Sources */, 195 | 5B02F3B52625812E0023FC84 /* main.m in Sources */, 196 | 5B148BEF272C0A6E00C01ABF /* UIViewController+XYZAlert.m in Sources */, 197 | 5B02F3A72625812B0023FC84 /* SceneDelegate.m in Sources */, 198 | 5B148BED272C0A6E00C01ABF /* XYZAlertQueue.m in Sources */, 199 | 5B148BF0272C0A6E00C01ABF /* XYZAlertView.m in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXSourcesBuildPhase section */ 204 | 205 | /* Begin PBXVariantGroup section */ 206 | 5B02F3AB2625812C0023FC84 /* Main.storyboard */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | 5B02F3AC2625812C0023FC84 /* Base */, 210 | ); 211 | name = Main.storyboard; 212 | sourceTree = ""; 213 | }; 214 | 5B02F3B02625812E0023FC84 /* LaunchScreen.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 5B02F3B12625812E0023FC84 /* Base */, 218 | ); 219 | name = LaunchScreen.storyboard; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 5B02F3B62625812E0023FC84 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_ANALYZER_NONNULL = YES; 230 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 232 | CLANG_CXX_LIBRARY = "libc++"; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | CLANG_ENABLE_OBJC_WEAK = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 249 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 253 | CLANG_WARN_STRICT_PROTOTYPES = YES; 254 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 255 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | COPY_PHASE_STRIP = NO; 259 | DEBUG_INFORMATION_FORMAT = dwarf; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | ENABLE_TESTABILITY = YES; 262 | GCC_C_LANGUAGE_STANDARD = gnu11; 263 | GCC_DYNAMIC_NO_PIC = NO; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_PREPROCESSOR_DEFINITIONS = ( 267 | "DEBUG=1", 268 | "$(inherited)", 269 | ); 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 277 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 278 | MTL_FAST_MATH = YES; 279 | ONLY_ACTIVE_ARCH = YES; 280 | SDKROOT = iphoneos; 281 | }; 282 | name = Debug; 283 | }; 284 | 5B02F3B72625812E0023FC84 /* Release */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_ENABLE_OBJC_WEAK = YES; 295 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_COMMA = YES; 298 | CLANG_WARN_CONSTANT_CONVERSION = YES; 299 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 300 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 301 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 302 | CLANG_WARN_EMPTY_BODY = YES; 303 | CLANG_WARN_ENUM_CONVERSION = YES; 304 | CLANG_WARN_INFINITE_RECURSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 307 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 308 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 310 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 319 | ENABLE_NS_ASSERTIONS = NO; 320 | ENABLE_STRICT_OBJC_MSGSEND = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu11; 322 | GCC_NO_COMMON_BLOCKS = YES; 323 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 324 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 325 | GCC_WARN_UNDECLARED_SELECTOR = YES; 326 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 327 | GCC_WARN_UNUSED_FUNCTION = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 330 | MTL_ENABLE_DEBUG_INFO = NO; 331 | MTL_FAST_MATH = YES; 332 | SDKROOT = iphoneos; 333 | VALIDATE_PRODUCT = YES; 334 | }; 335 | name = Release; 336 | }; 337 | 5B02F3B92625812E0023FC84 /* Debug */ = { 338 | isa = XCBuildConfiguration; 339 | buildSettings = { 340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 341 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 342 | CODE_SIGN_STYLE = Automatic; 343 | DEVELOPMENT_TEAM = S4QBH8P8Y9; 344 | INFOPLIST_FILE = XYZAlert/Info.plist; 345 | LD_RUNPATH_SEARCH_PATHS = ( 346 | "$(inherited)", 347 | "@executable_path/Frameworks", 348 | ); 349 | MARKETING_VERSION = 1.1.3; 350 | PRODUCT_BUNDLE_IDENTIFIER = com.xm.common.XYZAlert; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | TARGETED_DEVICE_FAMILY = "1,2"; 353 | }; 354 | name = Debug; 355 | }; 356 | 5B02F3BA2625812E0023FC84 /* Release */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 361 | CODE_SIGN_STYLE = Automatic; 362 | DEVELOPMENT_TEAM = S4QBH8P8Y9; 363 | INFOPLIST_FILE = XYZAlert/Info.plist; 364 | LD_RUNPATH_SEARCH_PATHS = ( 365 | "$(inherited)", 366 | "@executable_path/Frameworks", 367 | ); 368 | MARKETING_VERSION = 1.1.3; 369 | PRODUCT_BUNDLE_IDENTIFIER = com.xm.common.XYZAlert; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | TARGETED_DEVICE_FAMILY = "1,2"; 372 | }; 373 | name = Release; 374 | }; 375 | /* End XCBuildConfiguration section */ 376 | 377 | /* Begin XCConfigurationList section */ 378 | 5B02F39A2625812B0023FC84 /* Build configuration list for PBXProject "XYZAlert" */ = { 379 | isa = XCConfigurationList; 380 | buildConfigurations = ( 381 | 5B02F3B62625812E0023FC84 /* Debug */, 382 | 5B02F3B72625812E0023FC84 /* Release */, 383 | ); 384 | defaultConfigurationIsVisible = 0; 385 | defaultConfigurationName = Release; 386 | }; 387 | 5B02F3B82625812E0023FC84 /* Build configuration list for PBXNativeTarget "XYZAlert" */ = { 388 | isa = XCConfigurationList; 389 | buildConfigurations = ( 390 | 5B02F3B92625812E0023FC84 /* Debug */, 391 | 5B02F3BA2625812E0023FC84 /* Release */, 392 | ); 393 | defaultConfigurationIsVisible = 0; 394 | defaultConfigurationName = Release; 395 | }; 396 | /* End XCConfigurationList section */ 397 | }; 398 | rootObject = 5B02F3972625812B0023FC84 /* Project object */; 399 | } 400 | -------------------------------------------------------------------------------- /XYZAlert.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XYZAlert.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /XYZAlert/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /XYZAlert/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | #pragma mark - UISceneSession lifecycle 24 | 25 | 26 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 30 | } 31 | 32 | 33 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /XYZAlert/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XYZAlert/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /XYZAlert/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /XYZAlert/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /XYZAlert/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /XYZAlert/Classes/Dispatch/XYZAlertDispatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertDispatch.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "XYZAlertProtocol.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface XYZAlertDispatch : NSObject 13 | 14 | // 使用此方法 添加Alert 并尝试展示 15 | - (void)addAlerts:(NSArray> *)alerts; 16 | 17 | // 根据alertID找到AlertView (可能不存在, 或已经结束展示销毁, 也可能存在多个重名的, 所以要确保id不重复) 18 | - (NSArray> *)findAlertWithID:(NSString *)alertID; 19 | 20 | @end 21 | 22 | 23 | 24 | /// 内部私有方法 勿直接调用 25 | @interface XYZAlertDispatch (PrivateInternal) 26 | 27 | /// 创建一个调度器 绑定 28 | /// @param block 用来检测VC是否显示中, 如果显示中返回一个vc.view用来承载alert 29 | + (instancetype)distachWithVerifyBlock:(UIView *_Nullable (^)(void))block; 30 | 31 | /// 调度合适的Alert进行展示 32 | /// 规则: 33 | /// 第一步: 按照优先级高->低进行判断 34 | /// 第二部: 已经ready & 无未展示dependency -> 展示 35 | - (void)bindedVCDidAppear; 36 | 37 | /// 暂时隐藏当前VC绑定的Alerts 38 | - (void)bindedVCDidDisappear; 39 | 40 | @end 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /XYZAlert/Classes/Dispatch/XYZAlertDispatch.m: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertDispatch.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "XYZAlertDispatch.h" 9 | #import "XYZAlertQueue.h" 10 | 11 | @implementation XYZAlertDispatch 12 | { 13 | XYZAlertQueue *_queue; 14 | // 显示在上层的 即高优先级的 排序在前面 15 | NSMutableArray> *_showingAlerts; 16 | 17 | UIView * (^_verifyBlock)(void); // 展示前需要验证 当前VC/Window是否在展示中 18 | 19 | } 20 | - (instancetype)init { 21 | self = [super init]; 22 | if (self) { 23 | _queue = [[XYZAlertQueue alloc] init]; 24 | _showingAlerts = [[NSMutableArray alloc] init]; 25 | } 26 | return self; 27 | } 28 | 29 | - (NSArray> *)findAlertWithID:(NSString *)alertID { 30 | if (!alertID || alertID.length == 0) { 31 | return @[]; 32 | } 33 | NSMutableArray *marr = [NSMutableArray arrayWithCapacity:2]; 34 | [_showingAlerts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 35 | if ([obj.alertID isEqualToString:alertID]) { 36 | [marr addObject:obj]; 37 | } 38 | }]; 39 | [marr addObjectsFromArray:[_queue findItemsWithID:alertID]]; 40 | 41 | return marr; 42 | } 43 | 44 | - (void)addAlerts:(NSArray> *)alerts { 45 | for (id tmp in alerts) { 46 | tmp.weakDispatch = self; 47 | } 48 | [_queue addItems:alerts]; 49 | [self p__tryDispatch]; 50 | } 51 | 52 | #pragma mark - Private M 53 | - (void)p__restoreShowingAlert { 54 | if (_showingAlerts.count == 0) { 55 | return; 56 | } 57 | 58 | __block BOOL thisAfterNeedHidden = NO; 59 | [_showingAlerts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 60 | 61 | if (idx == 0) { 62 | // 第一个直接恢复显示 63 | [obj dispatchAlertTmpHidden:NO]; 64 | }else { 65 | // 根据上一个的排它行为 决定自己是否可以取消隐藏 66 | [obj dispatchAlertTmpHidden:thisAfterNeedHidden]; 67 | } 68 | // 更新 (一旦有一个高优先级HiddenOther, 后续全部隐藏即可 不需要在判断排它行为) 69 | if (thisAfterNeedHidden == NO) { 70 | thisAfterNeedHidden = obj.exclusiveBehavior == XYZAlertExclusiveBehaviorHiddenOther; 71 | } 72 | }]; 73 | } 74 | 75 | - (void)p__tryDispatch { 76 | dispatch_async(dispatch_get_main_queue(), ^{ 77 | // 主要是为了延迟一次runloop 78 | // 其次保证主线程 79 | [self p___dispatch]; 80 | }); 81 | } 82 | 83 | - (void)p___dispatch { 84 | UIView *view = _verifyBlock(); 85 | if (nil == view) { 86 | return; 87 | } 88 | id alert = nil; 89 | if (_showingAlerts.count > 0) { 90 | 91 | [self p__restoreShowingAlert]; 92 | __weak typeof(self) weakSelf = self; 93 | alert = [_queue next:^BOOL(id _Nonnull obj) { 94 | __strong typeof(weakSelf) strongSelf = weakSelf; 95 | if (strongSelf == nil) { 96 | return NO; 97 | } 98 | return [strongSelf p___isCanShowCheck:obj]; 99 | }]; 100 | if (alert == nil) { return; } 101 | 102 | [_queue removeItem:alert]; 103 | 104 | // 即将显示 是否需要隐藏其它显示中的 105 | if (alert.exclusiveBehavior == XYZAlertExclusiveBehaviorHiddenOther) { 106 | for (id tmp in _showingAlerts) { 107 | [tmp dispatchAlertTmpHidden:YES]; 108 | } 109 | } 110 | }else { 111 | 112 | alert = [_queue popItem]; 113 | if (alert == nil) { return; } 114 | } 115 | 116 | if (alert) { 117 | [alert dispatchAlertViewShowOn:view]; 118 | [_showingAlerts insertObject:alert atIndex:0]; 119 | } 120 | } 121 | 122 | - (BOOL)p___isCanShowCheck:(id)alert { 123 | for (id showingObj in _showingAlerts) { 124 | if ([alert.dependencyAlertIDSet containsObject:showingObj.alertID]) { 125 | // 依赖展示中的 放弃 126 | return NO; 127 | } 128 | if (alert.priority <= showingObj.priority) { 129 | // 优先级不高于当前展示中的 放弃 130 | return NO; 131 | } 132 | } 133 | 134 | // 判断排它 135 | switch (alert.exclusiveBehavior) { 136 | case XYZAlertExclusiveBehaviorNone: 137 | // 不能排它 放弃展示 138 | return NO; 139 | case XYZAlertExclusiveBehaviorHiddenOther: 140 | { 141 | // 需要隐藏其他的 显示时再隐藏其他的 142 | } 143 | break; 144 | 145 | case XYZAlertExclusiveBehaviorCoverOther: 146 | // nothing 直接展示即可覆盖 147 | break; 148 | default: 149 | break; 150 | } 151 | return YES; 152 | } 153 | #pragma mark - XYZAlertLifeProtocal 154 | - (void)alertDidReady:(id)alert { 155 | [self p__tryDispatch]; 156 | } 157 | - (void)alertDidRemoveFromSuperView:(id)alert { 158 | [_showingAlerts removeObject:alert]; 159 | [_queue removeItem:alert]; // 这行其实是为了容错 可以不加 160 | 161 | [self p__tryDispatch]; 162 | } 163 | 164 | @end 165 | 166 | 167 | 168 | 169 | 170 | 171 | @implementation XYZAlertDispatch (PrivateInternal) 172 | 173 | + (instancetype)distachWithVerifyBlock:(UIView * _Nonnull (^)(void))block { 174 | XYZAlertDispatch *tmp = [[XYZAlertDispatch alloc] init]; 175 | tmp->_verifyBlock = [block copy]; 176 | return tmp; 177 | } 178 | 179 | - (void)bindedVCDidAppear { 180 | 181 | [self p__restoreShowingAlert]; 182 | 183 | [self p__tryDispatch]; 184 | } 185 | 186 | - (void)bindedVCDidDisappear { 187 | for (id tmp in _showingAlerts) { 188 | [tmp dispatchAlertTmpHidden:YES]; 189 | } 190 | } 191 | @end 192 | 193 | -------------------------------------------------------------------------------- /XYZAlert/Classes/Dispatch/XYZAlertProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertProtocol.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/20. 6 | // 7 | 8 | #import 9 | 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol XYZAlertDispatchAble; 14 | 15 | 16 | @protocol XYZAlertLifeProtocal 17 | 18 | // 通知调度器 -> 准备结束的主动回调, 19 | /// @param alert alert 调度管理将立即开始尝试展示 20 | - (void)alertDidReady:(id)alert; 21 | 22 | // 通知调度器 -> 展示完毕已经移除/取消展示 的主动回调 23 | - (void)alertDidRemoveFromSuperView:(id)alert; 24 | @end 25 | 26 | 27 | 28 | 29 | 30 | typedef NS_ENUM(NSInteger, XYZAlertExclusiveBehaviorType) { 31 | XYZAlertExclusiveBehaviorNone = 0, // 不能排它, 即当前有展示中的alert 自己不能被展示 32 | XYZAlertExclusiveBehaviorCoverOther, // 覆盖其它, 即当前有展示中的alert 自己覆盖在上层 33 | XYZAlertExclusiveBehaviorHiddenOther,// 隐藏其它, 即当前有展示中的alert 自己把下层的hidden掉 34 | }; 35 | 36 | 37 | typedef NS_ENUM(NSInteger, XYZAlertState) { 38 | XYZAlertStatePrepare = 0, // 准备中 39 | XYZAlertStateReady, // 已经准备好 可以接受展现调度 40 | XYZAlertStateShowing, // 展现中 41 | XYZAlertStateTmpHidden, // 绑定的页面消失/被其它弹窗排斥 所以临时被隐藏 42 | XYZAlertStateEnd, // 结束销毁 43 | }; 44 | 45 | 46 | 47 | @protocol XYZAlertDispatchAble 48 | 49 | // 唯一标示 50 | @property (nonatomic, copy, nullable) NSString *alertID; 51 | 52 | // 状态 53 | @property (nonatomic, assign, readonly) XYZAlertState curState; 54 | // 标记已准备好, 通知调度器, 立即尝试进行展示 55 | - (void)setReadyAndTryDispath; 56 | // 标记需要结束, 通知调度器, 放弃展示并从队列中移除 57 | - (void)setCancelAndRemoveFromDispatch; 58 | 59 | // 优先级 60 | /// 1. 调度展现时, 高优先级的会优先处理展示 61 | /// 2. 展示时互斥, 高优先级的会互斥掉其它低优先级的Alert 62 | @property (nonatomic, assign) int priority; 63 | 64 | // 发生互斥时的行为 65 | /// 自身展示时, 如果已经存在其它比自己优先级低且展示中的alert, 会按照此行为进行排它操作 66 | @property (nonatomic, assign) XYZAlertExclusiveBehaviorType exclusiveBehavior; 67 | 68 | // 依赖其他弹窗的id组 69 | /// 如果依赖的弹窗未能结束展示, 则自己将不会被展示 70 | @property (nonatomic, readonly) NSSet *dependencyAlertIDSet; 71 | // 添加展示前对其他弹窗的依赖 (依赖的alert展示结束后 自己才允许展示) 72 | - (void)addDependencyAlertID:(NSString *)alertid; 73 | 74 | 75 | // 弱引用持有调度器 76 | @property (nonatomic, weak ) id weakDispatch; 77 | 78 | // 被调度->展现 79 | - (void)dispatchAlertViewShowOn:(UIView *)view; 80 | 81 | // 被调度->临时隐藏/展现 82 | - (void)dispatchAlertTmpHidden:(BOOL)hidden; 83 | @end 84 | 85 | 86 | NS_ASSUME_NONNULL_END 87 | -------------------------------------------------------------------------------- /XYZAlert/Classes/Dispatch/XYZAlertQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertQueue.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | #import "XYZAlertProtocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | 14 | @interface XYZAlertQueue : NSObject 15 | 16 | - (BOOL)isEmpty; 17 | 18 | - (int)count; 19 | 20 | /// 下个展示的 21 | /// - Parameter canShowCheck: 和显示中的那部分比较 22 | - (nullable id)next:(BOOL(^)(id))canShowCheck; 23 | 24 | // 根据优先级 取出 25 | - (nullable id)popItem; 26 | 27 | // 移除 28 | - (void)removeItem:(id)item; 29 | 30 | // 根据优先级 进入队列 31 | - (void)addItem:(id)item; 32 | - (void)addItems:(NSArray> *)items; 33 | 34 | /// 根据alertID找到AlertView (可能不存在, 或已经结束展示销毁, 也可能存在多个重名的, 所以要确保id不重复) 35 | - (NSArray> *)findItemsWithID:(NSString *)alertID; 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /XYZAlert/Classes/Dispatch/XYZAlertQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertQueue.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "XYZAlertQueue.h" 9 | 10 | @implementation XYZAlertQueue 11 | { 12 | NSMutableArray> *_items; 13 | dispatch_semaphore_t _lock; 14 | } 15 | - (instancetype)init { 16 | self = [super init]; 17 | if (self) { 18 | _items = [[NSMutableArray alloc] init]; 19 | _lock = dispatch_semaphore_create(1); 20 | } 21 | return self; 22 | } 23 | 24 | - (int)count { 25 | return (int)_items.count; 26 | } 27 | - (BOOL)isEmpty { 28 | return [self count] == 0; 29 | } 30 | 31 | - (NSArray> *)findItemsWithID:(NSString *)alertID { 32 | if (!alertID || alertID.length == 0) { 33 | return @[]; 34 | } 35 | NSMutableArray *marr = [NSMutableArray arrayWithCapacity:2]; 36 | [_items enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 37 | if ([obj.alertID isEqualToString:alertID]) { 38 | [marr addObject:obj]; 39 | } 40 | }]; 41 | return marr; 42 | } 43 | 44 | - (void)addItem:(id)item { 45 | if (!item || NO == [item conformsToProtocol:@protocol(XYZAlertDispatchAble)]) { 46 | return; 47 | } 48 | 49 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 50 | // 添加 51 | [_items addObject:item]; 52 | // 按照优先级"降序"排列 53 | [_items sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 54 | 55 | if (obj1.priority < obj2.priority) { 56 | return NSOrderedDescending; 57 | }else if ((obj1.priority > obj2.priority)) { 58 | return NSOrderedAscending; 59 | } 60 | return NSOrderedSame; 61 | }]; 62 | dispatch_semaphore_signal(_lock); 63 | } 64 | - (void)addItems:(NSArray> *)items { 65 | if (!items || 0 == items.count) { 66 | return; 67 | } 68 | 69 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 70 | // 添加 71 | [_items addObjectsFromArray:items]; 72 | // 按照优先级"降序"排列 73 | [_items sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 74 | 75 | if (obj1.priority < obj2.priority) { 76 | return NSOrderedDescending; 77 | }else if ((obj1.priority > obj2.priority)) { 78 | return NSOrderedAscending; 79 | } 80 | return NSOrderedSame; 81 | }]; 82 | dispatch_semaphore_signal(_lock); 83 | } 84 | - (void)removeItem:(id)item { 85 | if (nil == item || [self isEmpty]) { 86 | return ; 87 | } 88 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 89 | [_items removeObject:item]; 90 | dispatch_semaphore_signal(_lock); 91 | } 92 | - (id)next:(BOOL (^)(id _Nonnull))canShowCheck { 93 | if ([self isEmpty]) { 94 | return nil; 95 | } 96 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 97 | __block id target = nil; 98 | [_items enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 99 | 100 | if (XYZAlertStateReady != obj.curState) { 101 | // 未准备好 跳过 102 | return; 103 | } 104 | if (obj.dependencyAlertIDSet.count <= 0) { 105 | if (NO == canShowCheck(obj)) { 106 | return; 107 | } 108 | // 准备好了 & 无依赖 直接使用 109 | target = obj; 110 | *stop = YES; 111 | return; 112 | } 113 | for (id alert in _items) { 114 | if ([obj.dependencyAlertIDSet containsObject:alert.alertID]) { 115 | // 有依赖未展示 跳过 116 | return; 117 | } 118 | } 119 | if (NO == canShowCheck(obj)) { 120 | return; 121 | } 122 | // 准备好了 & 有依赖但都已完成 直接使用 123 | target = obj; 124 | *stop = YES; 125 | }]; 126 | dispatch_semaphore_signal(_lock); 127 | return target; 128 | } 129 | - (id)popItem { 130 | if ([self isEmpty]) { 131 | return nil; 132 | } 133 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 134 | __block NSInteger targetIdx = -1; 135 | __block id target = nil; 136 | [_items enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 137 | 138 | if (XYZAlertStateReady != obj.curState) { 139 | // 未准备好 跳过 140 | return; 141 | } 142 | if (obj.dependencyAlertIDSet.count <= 0) { 143 | // 准备好了 & 无依赖 直接使用 144 | target = obj; 145 | targetIdx = idx; 146 | *stop = YES; 147 | return; 148 | } 149 | for (id alert in _items) { 150 | if ([obj.dependencyAlertIDSet containsObject:alert.alertID]) { 151 | // 有依赖未展示 跳过 152 | return; 153 | } 154 | } 155 | // 准备好了 & 有依赖但都已完成 直接使用 156 | target = obj; 157 | targetIdx = idx; 158 | *stop = YES; 159 | }]; 160 | if (targetIdx >= 0) { 161 | [_items removeObjectAtIndex:targetIdx]; 162 | } 163 | dispatch_semaphore_signal(_lock); 164 | 165 | return target; 166 | } 167 | @end 168 | -------------------------------------------------------------------------------- /XYZAlert/Classes/UIViewController+XYZAlert.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+XYZAlert.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | #import "XYZAlertDispatch.h" 10 | 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface UIViewController (XYZAlert) 15 | 16 | @property (nonatomic, strong, readonly) XYZAlertDispatch *alertDispah; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /XYZAlert/Classes/UIViewController+XYZAlert.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+XYZAlert.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "UIViewController+XYZAlert.h" 9 | #import 10 | 11 | @implementation UIViewController (XYZAlert) 12 | 13 | + (void)load { 14 | static dispatch_once_t onceToken; 15 | dispatch_once(&onceToken, ^{ 16 | 17 | void(^__swizzleSel)(Class, SEL, SEL) = ^(Class aClass, SEL originalSelector, SEL swizzledSelector) { 18 | 19 | Method originalMethod = class_getInstanceMethod(aClass, originalSelector); 20 | Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector); 21 | BOOL didAddMethod = class_addMethod(aClass, 22 | originalSelector, 23 | method_getImplementation(swizzledMethod), 24 | method_getTypeEncoding(swizzledMethod)); 25 | if (didAddMethod) { 26 | class_replaceMethod(aClass, 27 | swizzledSelector, 28 | method_getImplementation(originalMethod), 29 | method_getTypeEncoding(originalMethod)); 30 | } else { 31 | method_exchangeImplementations(originalMethod, swizzledMethod); 32 | } 33 | }; 34 | 35 | SEL originalSelector_1 = @selector(viewDidAppear:); 36 | SEL swizzledSelector_1 = @selector(xyzAlert_viewDidAppear:); 37 | __swizzleSel([self class], originalSelector_1, swizzledSelector_1); 38 | 39 | 40 | SEL originalSelector_2 = @selector(viewDidDisappear:); 41 | SEL swizzledSelector_2 = @selector(xyzAlert_viewDidDisappear:); 42 | __swizzleSel([self class], originalSelector_2, swizzledSelector_2); 43 | }); 44 | } 45 | 46 | 47 | #pragma mark - Visiable 48 | - (BOOL)canShowAlert { 49 | if (self.isAppear == NO) { 50 | return NO; 51 | } 52 | if (self.view.window == nil) { 53 | return NO; 54 | } 55 | BOOL transitioning = NO; 56 | @try { 57 | transitioning = [[self.navigationController valueForKey:@"_isTransitioning"] boolValue]; 58 | } 59 | @catch (NSException *exception) { 60 | 61 | } 62 | if (transitioning) { 63 | // 转场中 64 | return NO; 65 | } 66 | return YES; 67 | } 68 | 69 | - (BOOL)isAppear { 70 | return [objc_getAssociatedObject(self, @selector(isAppear)) boolValue]; 71 | } 72 | - (void)setIsAppear:(BOOL)appear { 73 | objc_setAssociatedObject(self, @selector(isAppear), @(appear), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 74 | } 75 | #pragma mark - VC life 76 | - (void)xyzAlert_viewDidAppear:(BOOL)animation { 77 | [self xyzAlert_viewDidAppear:animation]; 78 | 79 | [self setIsAppear:YES]; 80 | 81 | [[self alertDispahIfExist] bindedVCDidAppear]; 82 | } 83 | 84 | - (void)xyzAlert_viewDidDisappear:(BOOL)animation { 85 | [self xyzAlert_viewDidDisappear:animation]; 86 | 87 | [self setIsAppear:NO]; 88 | 89 | [[self alertDispahIfExist] bindedVCDidDisappear]; 90 | } 91 | 92 | #pragma mark - Lazy 93 | - (nullable XYZAlertDispatch *)alertDispahIfExist { 94 | return objc_getAssociatedObject(self, @selector(alertDispah)); 95 | } 96 | 97 | - (XYZAlertDispatch *)alertDispah { 98 | XYZAlertDispatch *dispath = objc_getAssociatedObject(self, @selector(alertDispah)); 99 | if (nil == dispath) { 100 | __weak typeof(self) weakSelf = self; 101 | dispath = [XYZAlertDispatch distachWithVerifyBlock:^UIView *{ 102 | return [weakSelf canShowAlert] ? weakSelf.view : nil; 103 | }]; 104 | objc_setAssociatedObject(self, @selector(alertDispah), dispath, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 105 | } 106 | return dispath; 107 | } 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /XYZAlert/Classes/XYZAlertView.h: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertView.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/19. 6 | // 7 | 8 | #import 9 | #import "XYZAlertProtocol.h" 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | typedef NS_ENUM(NSUInteger, XYZAlertViewShowAnimatonType) { 13 | XYZAlertViewShowAnimatonSystem, // 仿系统 14 | XYZAlertViewShowAnimatonBounces, // 中间方法带抖动 15 | XYZAlertViewShowAnimatonDown, // 从上落下 16 | }; 17 | 18 | 19 | @interface XYZAlertView : UIView 20 | 21 | /// 容器Alert 22 | @property (nonatomic, strong) UIView *containerAlertView; 23 | 24 | /// Alert出现/消失的动画类型 25 | @property (nonatomic, assign) XYZAlertViewShowAnimatonType animationType UI_APPEARANCE_SELECTOR; 26 | 27 | /// 容器Alert最大size 28 | /// defult screenWitdh == 320 ? CGSizeMake(280, 400) : CGSizeMake(310, 500); 29 | @property (nonatomic, assign) CGSize containerAlertViewMaxSize UI_APPEARANCE_SELECTOR; 30 | 31 | /// 容器Alert圆角半径 32 | /// /// defult 10 33 | @property (nonatomic, assign) CGFloat containerAlertViewRoundValue UI_APPEARANCE_SELECTOR; 34 | 35 | /// 背景透明度 36 | /// defult 0.3 37 | @property (nonatomic, assign) CGFloat backAlpha UI_APPEARANCE_SELECTOR; 38 | 39 | /// 点击空白区域时自动隐藏 40 | /// defult YES 41 | @property (nonatomic, assign) BOOL hideOnTouchOutside UI_APPEARANCE_SELECTOR; 42 | 43 | /// 自动躲避键盘 (键盘弹出时 自动往上便宜键盘的高度) 44 | /// defult YES 45 | @property (nonatomic, assign) BOOL autoAvoidKeyboard UI_APPEARANCE_SELECTOR; 46 | 47 | /// 自动躲避键盘时修正偏移量 48 | /// 键盘弹出时 默认自动往上偏移键盘的高度 此值<0时 会增加向上偏移的距离, 此值>0时 会减小向上偏移的距离 49 | /// defult 0 50 | @property (nonatomic, assign) CGFloat avoidKeyboardOffsetY; 51 | 52 | 53 | /// 可以提前指定一个父视图 展示时会优先添加到这个视图上 54 | @property (nonatomic, weak ) UIView *showOnView; 55 | 56 | /// 直接展现 57 | - (void)showOnView:(UIView *)view; 58 | /// 直接消失 59 | - (void)dismissWithAnimation:(BOOL)animation; 60 | 61 | @end 62 | 63 | NS_ASSUME_NONNULL_END 64 | -------------------------------------------------------------------------------- /XYZAlert/Classes/XYZAlertView.m: -------------------------------------------------------------------------------- 1 | // 2 | // XYZAlertView.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/19. 6 | // 7 | 8 | #import "XYZAlertView.h" 9 | 10 | @interface XYZAlertView () 11 | 12 | @end 13 | 14 | @implementation XYZAlertView 15 | 16 | #pragma mark - XYZAlertEnableDispatchProtocal 17 | 18 | @synthesize weakDispatch; 19 | @synthesize alertID; 20 | @synthesize priority; 21 | @synthesize curState; 22 | @synthesize exclusiveBehavior; 23 | @synthesize dependencyAlertIDSet; 24 | 25 | - (void)setReadyAndTryDispath { 26 | if (curState == XYZAlertStatePrepare) { 27 | curState = XYZAlertStateReady; 28 | [weakDispatch alertDidReady:self]; 29 | } 30 | } 31 | - (void)setCancelAndRemoveFromDispatch { 32 | if (curState != XYZAlertStateEnd) { 33 | [self removeFromSuperview]; 34 | curState = XYZAlertStateEnd; 35 | [weakDispatch alertDidRemoveFromSuperView:self]; 36 | } 37 | } 38 | - (void)addDependencyAlertID:(NSString *)alertid { 39 | if (!alertid || alertid.length == 0) { 40 | return; 41 | } 42 | NSMutableSet *mset = nil; 43 | NSSet *set = dependencyAlertIDSet; 44 | if (set) { 45 | if ([set isKindOfClass:[NSMutableSet class]]) { 46 | mset = (NSMutableSet *)set; 47 | }else { 48 | mset = [set mutableCopy]; 49 | } 50 | }else { 51 | mset = [[NSMutableSet alloc] init]; 52 | } 53 | [mset addObject:alertid]; 54 | dependencyAlertIDSet = mset; 55 | } 56 | - (void)dispatchAlertViewShowOn:(UIView *)view { 57 | [self showOnView:view]; 58 | curState = XYZAlertStateShowing; 59 | } 60 | - (void)dispatchAlertTmpHidden:(BOOL)hidden { 61 | if (hidden) { 62 | curState = XYZAlertStateTmpHidden; 63 | self.hidden = YES; 64 | }else { 65 | curState = XYZAlertStateShowing; 66 | self.hidden = NO; 67 | } 68 | } 69 | #pragma mark - avoidKeyBoard 70 | - (void)setAutoAvoidKeyboard:(BOOL)autoAvoidKeyboard { 71 | if (_autoAvoidKeyboard != autoAvoidKeyboard) { 72 | _autoAvoidKeyboard = autoAvoidKeyboard; 73 | if (autoAvoidKeyboard) { 74 | [self observeKeyboardNotify]; 75 | }else { 76 | [self rmKeyboardNotifyObserver]; 77 | } 78 | } 79 | } 80 | - (void)observeKeyboardNotify { 81 | [self rmKeyboardNotifyObserver]; 82 | 83 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_willShow:) name:UIKeyboardWillShowNotification object:nil]; 84 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_willHidden:) name:UIKeyboardWillHideNotification object:nil]; 85 | } 86 | - (void)rmKeyboardNotifyObserver { 87 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 88 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 89 | } 90 | - (void)kb_willShow:(NSNotification *)notify { 91 | [self p_reciveKeyboardNotification:notify frWillShow:YES]; 92 | } 93 | - (void)kb_willHidden:(NSNotification *)notify { 94 | [self p_reciveKeyboardNotification:notify frWillShow:NO]; 95 | } 96 | - (void)p_reciveKeyboardNotification:(NSNotification *)notify frWillShow:(BOOL)willShow { 97 | if (self.window == nil) { 98 | return; 99 | } 100 | 101 | CGRect kbToRect = [notify.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 102 | CGFloat animationDuration = [notify.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]; 103 | 104 | CGFloat offsetY = CGRectGetHeight(kbToRect); 105 | 106 | CGAffineTransform transf = CGAffineTransformIdentity; 107 | if (willShow) { 108 | if (offsetY <= 0) { 109 | return; 110 | } 111 | transf = CGAffineTransformMakeTranslation(0, -offsetY + self.avoidKeyboardOffsetY); 112 | } 113 | 114 | [UIView animateWithDuration:animationDuration > 0 ? animationDuration : 0.2 animations:^{ 115 | self.containerAlertView.transform = transf; 116 | }]; 117 | } 118 | 119 | 120 | - (void)reciveKeyboardNotification:(NSNotification *)notify { 121 | if (self.window == nil) { 122 | return; 123 | } 124 | CGRect kbToRect = [notify.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 125 | CGFloat animationDuration = [notify.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]; 126 | BOOL kbToShow = CGRectGetMinY(kbToRect) < [UIScreen mainScreen].bounds.size.height; 127 | CGRect alertRect = [self.containerAlertView convertRect:self.containerAlertView.bounds toView:self.window]; 128 | 129 | CGFloat offsetY = CGRectGetMaxY(alertRect) - CGRectGetMinY(kbToRect); 130 | CGAffineTransform transf = CGAffineTransformIdentity; 131 | if (kbToShow) { 132 | if (offsetY <= 0) { 133 | return; 134 | } 135 | transf = CGAffineTransformMakeTranslation(0, -offsetY); 136 | } 137 | 138 | [UIView animateWithDuration:animationDuration > 0 ? animationDuration : 0.2 animations:^{ 139 | self.containerAlertView.transform = transf; 140 | }]; 141 | } 142 | #pragma mark - init 143 | - (instancetype)initWithFrame:(CGRect)frame { 144 | self = [super initWithFrame:frame]; 145 | if (self) { 146 | 147 | if ([UIScreen mainScreen].bounds.size.width == 320) { 148 | _containerAlertViewMaxSize = CGSizeMake(280, 400); 149 | }else { 150 | _containerAlertViewMaxSize = CGSizeMake(310, 500); 151 | } 152 | curState = XYZAlertStatePrepare; 153 | _containerAlertViewRoundValue = 10; 154 | _backAlpha = 0.3; 155 | 156 | _hideOnTouchOutside = YES; 157 | self.autoAvoidKeyboard = YES; 158 | } 159 | return self; 160 | } 161 | 162 | 163 | 164 | - (void)showOnView:(UIView *)view { 165 | if (_showOnView) { 166 | view = _showOnView; 167 | } 168 | if (!view) { 169 | return; 170 | } 171 | [view addSubview:self]; 172 | 173 | 174 | self.frame = view.bounds; 175 | self.backgroundColor = UIColor.clearColor; 176 | 177 | // 宽高 178 | self.containerAlertView.translatesAutoresizingMaskIntoConstraints = NO; 179 | [self.containerAlertView.widthAnchor constraintEqualToConstant:_containerAlertViewMaxSize.width].active =YES; 180 | [self.containerAlertView.heightAnchor constraintLessThanOrEqualToConstant:_containerAlertViewMaxSize.height].active =YES; 181 | 182 | // 动画 183 | if (_animationType == XYZAlertViewShowAnimatonBounces) { 184 | // 位置 185 | [self.containerAlertView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES; 186 | [self.containerAlertView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; 187 | self.containerAlertView.transform = CGAffineTransformMakeScale(0.7, 0.7); 188 | 189 | [self layoutIfNeeded]; 190 | 191 | [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0.8 192 | options:UIViewAnimationOptionCurveEaseInOut 193 | animations:^{ 194 | self.containerAlertView.transform = CGAffineTransformIdentity; 195 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:self.backAlpha]; 196 | } 197 | completion:nil]; 198 | 199 | }else if (_animationType == XYZAlertViewShowAnimatonDown) { 200 | // 位置 201 | NSLayoutConstraint *btLC = [self.containerAlertView.bottomAnchor constraintEqualToAnchor:self.topAnchor]; 202 | btLC.active = YES; 203 | [self.containerAlertView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; 204 | 205 | [self layoutIfNeeded]; 206 | 207 | btLC.active = NO; 208 | [self.containerAlertView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES; 209 | 210 | [UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0.3 211 | options:UIViewAnimationOptionCurveEaseIn 212 | animations:^{ 213 | [self layoutIfNeeded]; 214 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:self.backAlpha]; 215 | } 216 | completion:nil]; 217 | }else { 218 | // 位置 219 | [self.containerAlertView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES; 220 | [self.containerAlertView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; 221 | self.containerAlertView.transform = CGAffineTransformMakeScale(1.1, 1.1); 222 | self.containerAlertView.alpha = 0.2; 223 | 224 | [self layoutIfNeeded]; 225 | 226 | [UIView animateWithDuration:0.25 animations:^{ 227 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:self.backAlpha]; 228 | self.containerAlertView.transform = CGAffineTransformIdentity; 229 | self.containerAlertView.alpha = 1.0; 230 | }]; 231 | } 232 | } 233 | 234 | - (void)dismissWithAnimation:(BOOL)animation { 235 | void(^completion)(void) = ^(){ 236 | [self removeFromSuperview]; 237 | self->curState = XYZAlertStateEnd; 238 | [self->weakDispatch alertDidRemoveFromSuperView:self]; 239 | }; 240 | 241 | if (animation) { 242 | [UIView animateWithDuration:0.2 animations:^{ 243 | self.backgroundColor = UIColor.clearColor; 244 | self.containerAlertView.transform = CGAffineTransformMakeScale(.8, .8); 245 | self.containerAlertView.alpha = 0.2; 246 | }completion:^(BOOL finished) { 247 | completion(); 248 | }]; 249 | }else { 250 | completion(); 251 | } 252 | } 253 | 254 | 255 | #pragma mark - override 256 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 257 | [self endEditing:YES]; 258 | 259 | if (_hideOnTouchOutside == NO) { 260 | [super touchesBegan:touches withEvent:event]; 261 | return; 262 | } 263 | 264 | CGPoint point = [touches.anyObject locationInView:self]; 265 | if (NO == CGRectContainsPoint(_containerAlertView.frame, point)) { 266 | [self dismissWithAnimation:YES]; 267 | } 268 | } 269 | 270 | - (void)layoutSubviews { 271 | [super layoutSubviews]; 272 | 273 | if (_containerAlertView.frame.size.height > 0 && _containerAlertView.layer.cornerRadius != _containerAlertViewRoundValue) { 274 | _containerAlertView.layer.cornerRadius = _containerAlertViewRoundValue; 275 | } 276 | } 277 | 278 | #pragma mark - Lazy 279 | - (UIView *)containerAlertView { 280 | if (!_containerAlertView) { 281 | _containerAlertView = [[UIView alloc] init]; 282 | _containerAlertView.clipsToBounds = YES; 283 | _containerAlertView.backgroundColor = UIColor.whiteColor; 284 | [self addSubview:_containerAlertView]; 285 | } 286 | return _containerAlertView; 287 | } 288 | 289 | @end 290 | -------------------------------------------------------------------------------- /XYZAlert/Classes/XYZSystemAlertView.h: -------------------------------------------------------------------------------- 1 | // 2 | // XYZSystemAlertView.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/20. 6 | // 7 | 8 | #import "XYZAlertView.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface XYZSystemAlertViewActionBtn : UIButton 13 | 14 | @property (nonatomic, copy) dispatch_block_t action; 15 | + (instancetype)actionWithName:(NSString *)name clickCallback:(dispatch_block_t)action; 16 | @end 17 | 18 | 19 | @interface XYZSystemAlertView : XYZAlertView 20 | 21 | @property (nonatomic, copy) NSAttributedString *attributeTitle; 22 | @property (nonatomic, copy) NSAttributedString *attributeMessage; 23 | 24 | - (instancetype)initWithTitle:(nullable NSString *)title msg:(nullable NSString *)msg; 25 | 26 | // MAX = 2 27 | - (void)addActionBtn:(XYZSystemAlertViewActionBtn *)action; 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /XYZAlert/Classes/XYZSystemAlertView.m: -------------------------------------------------------------------------------- 1 | // 2 | // XYZSystemAlertView.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/20. 6 | // 7 | 8 | #import "XYZSystemAlertView.h" 9 | 10 | @implementation XYZSystemAlertViewActionBtn 11 | { 12 | CALayer *_topLine; 13 | CALayer *_leftLine; 14 | CALayer *_righrLine; 15 | } 16 | - (void)layoutSubviews { 17 | [super layoutSubviews]; 18 | 19 | CGFloat onePointLine = 1 / [UIScreen mainScreen].scale; 20 | 21 | if (_topLine) _topLine.frame = CGRectMake(0, 0, self.frame.size.width, onePointLine); 22 | 23 | if (_leftLine) _leftLine.frame = CGRectMake(0, 0, onePointLine, self.frame.size.height); 24 | 25 | if (_righrLine) _righrLine.frame = CGRectMake(0, self.frame.size.width - onePointLine, onePointLine, self.frame.size.height); 26 | } 27 | 28 | + (instancetype)actionWithName:(NSString *)name clickCallback:(dispatch_block_t)action { 29 | XYZSystemAlertViewActionBtn *act = [XYZSystemAlertViewActionBtn buttonWithType:UIButtonTypeCustom]; 30 | [act setTitle:name forState:UIControlStateNormal]; 31 | [act setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal]; 32 | [act addTarget:act action:@selector(p_clickAction) forControlEvents:UIControlEventTouchUpInside]; 33 | act.action = action; 34 | return act; 35 | } 36 | 37 | 38 | - (void)addLineWith:(UIRectEdge)edges { 39 | if (edges & UIRectEdgeTop) { 40 | [_topLine removeFromSuperlayer]; 41 | _topLine = [self crearLayer]; 42 | [self.layer addSublayer:_topLine]; 43 | 44 | } 45 | if (edges & UIRectEdgeRight) { 46 | [_righrLine removeFromSuperlayer]; 47 | _righrLine = [self crearLayer]; 48 | [self.layer addSublayer:_righrLine]; 49 | } 50 | if (edges & UIRectEdgeLeft) { 51 | [_leftLine removeFromSuperlayer]; 52 | _leftLine = [self crearLayer]; 53 | [self.layer addSublayer:_leftLine]; 54 | } 55 | } 56 | - (void)p_clickAction { 57 | _action(); 58 | } 59 | - (CALayer *)crearLayer { 60 | CALayer *layer = [CALayer layer]; 61 | layer.backgroundColor = [UIColor lightGrayColor].CGColor; 62 | return layer; 63 | } 64 | 65 | @end 66 | 67 | 68 | 69 | 70 | @interface XYZSystemAlertView() 71 | { 72 | NSMutableArray *_actions; 73 | } 74 | @property (nonatomic) UILabel *titleLabel; 75 | @property (nonatomic) UILabel *msgLabel; 76 | 77 | @property (nonatomic, copy) NSString *title; 78 | @property (nonatomic, copy) NSString *message; 79 | @end 80 | @implementation XYZSystemAlertView 81 | 82 | 83 | - (instancetype)initWithTitle:(NSString *)title msg:(NSString *)msg { 84 | if (self = [self initWithFrame:CGRectZero]) { 85 | _title = title; 86 | _message = msg; 87 | } 88 | return self; 89 | } 90 | 91 | - (void)addActionBtn:(XYZSystemAlertViewActionBtn *)action { 92 | if (!action) { 93 | return; 94 | } 95 | if (!_actions) { 96 | _actions = [[NSMutableArray alloc] initWithCapacity:2]; 97 | } 98 | [_actions addObject:action]; 99 | if (_actions.count > 2) { 100 | [_actions removeObjectAtIndex:0]; 101 | } 102 | } 103 | 104 | #pragma mark - override 105 | 106 | - (void)showOnView:(UIView *)view { 107 | UIView *bgview = self.containerAlertView; 108 | 109 | UIStackView *stack = [[UIStackView alloc] init]; 110 | stack.axis = UILayoutConstraintAxisVertical; 111 | stack.spacing = 15; 112 | stack.distribution = UIStackViewDistributionEqualSpacing; 113 | 114 | if (_title.length > 0 || _attributeTitle.length > 0) { 115 | if (_attributeTitle.length > 0) { 116 | self.titleLabel.attributedText = _attributeTitle; 117 | }else { 118 | self.titleLabel.text = _title; 119 | } 120 | [stack addArrangedSubview:self.titleLabel]; 121 | 122 | self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; 123 | [self.titleLabel.heightAnchor constraintEqualToConstant:20].active = YES; 124 | } 125 | 126 | if (_message.length > 0 || _attributeMessage.length > 0) { 127 | if (_attributeMessage.length > 0) { 128 | self.msgLabel.attributedText = _attributeMessage; 129 | }else { 130 | self.msgLabel.text = _message; 131 | } 132 | [stack addArrangedSubview:self.msgLabel]; 133 | } 134 | 135 | [bgview addSubview:stack]; 136 | stack.translatesAutoresizingMaskIntoConstraints = NO; 137 | [stack.topAnchor constraintEqualToAnchor:bgview.topAnchor constant:20].active = YES; 138 | [stack.leftAnchor constraintEqualToAnchor:bgview.leftAnchor constant:15].active = YES; 139 | [stack.rightAnchor constraintEqualToAnchor:bgview.rightAnchor constant:-15].active = YES; 140 | [stack.bottomAnchor constraintLessThanOrEqualToAnchor:bgview.bottomAnchor constant:-20].active = YES; 141 | 142 | if (_actions.count > 0) { 143 | UIStackView *hstack = [[UIStackView alloc] init]; 144 | hstack.spacing = 0; 145 | hstack.distribution = UIStackViewDistributionFillEqually; 146 | [_actions enumerateObjectsUsingBlock:^(XYZSystemAlertViewActionBtn * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 147 | 148 | [hstack addArrangedSubview:obj]; 149 | if (idx == 0) { 150 | [obj addLineWith:UIRectEdgeTop]; 151 | }else { 152 | [obj addLineWith:UIRectEdgeTop | UIRectEdgeLeft]; 153 | } 154 | }]; 155 | [bgview addSubview:hstack]; 156 | hstack.translatesAutoresizingMaskIntoConstraints = NO; 157 | [hstack.topAnchor constraintEqualToAnchor:stack.bottomAnchor constant:15].active = YES; 158 | [hstack.leftAnchor constraintEqualToAnchor:bgview.leftAnchor].active = YES; 159 | [hstack.rightAnchor constraintEqualToAnchor:bgview.rightAnchor].active = YES; 160 | [hstack.bottomAnchor constraintEqualToAnchor:bgview.bottomAnchor].active = YES; 161 | [hstack.heightAnchor constraintEqualToConstant:49].active = YES; 162 | } 163 | 164 | 165 | [super showOnView:view]; 166 | } 167 | 168 | #pragma mark - get 169 | - (UILabel *)titleLabel { 170 | if (!_titleLabel) { 171 | UILabel *label = [[UILabel alloc] init]; 172 | label.textAlignment = NSTextAlignmentCenter; 173 | label.font = [UIFont boldSystemFontOfSize:18]; 174 | label.textColor = UIColor.blackColor; 175 | 176 | _titleLabel = label; 177 | } 178 | return _titleLabel; 179 | } 180 | 181 | - (UILabel *)msgLabel { 182 | if (!_msgLabel) { 183 | UILabel *label = [[UILabel alloc] init]; 184 | label.numberOfLines = 0; 185 | label.font = [UIFont boldSystemFontOfSize:17]; 186 | label.textColor = [UIColor colorWithRed:0x33/255.0 green:0x33/255.0 blue:0x33/255.0 alpha:1]; 187 | _msgLabel = label; 188 | } 189 | return _msgLabel; 190 | } 191 | 192 | 193 | @end 194 | -------------------------------------------------------------------------------- /XYZAlert/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /XYZAlert/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /XYZAlert/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /XYZAlert/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /XYZAlert/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "UIViewController+XYZAlert.h" 10 | #import "XYZSystemAlertView.h" 11 | @interface ViewController () 12 | { 13 | int _aa ; 14 | XYZSystemAlertView *_alert; 15 | } 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | self.view.backgroundColor = UIColor.whiteColor; 24 | 25 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem]; 26 | btn.frame = CGRectMake(0, 0, 200, 50); 27 | [btn setTitle:@"SimpleAlet样式 点击循环展示" forState:UIControlStateNormal]; 28 | [btn addTarget:self action:@selector(_____XYZSimpleAlet) forControlEvents:UIControlEventTouchUpInside]; 29 | [self.view addSubview:btn]; 30 | btn.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 3 * 1); 31 | 32 | 33 | UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeSystem]; 34 | btn2.frame = CGRectMake(0, 0, 200, 50); 35 | [btn2 setTitle:@"depend 展示" forState:UIControlStateNormal]; 36 | [btn2 addTarget:self action:@selector(_____dependAlert) forControlEvents:UIControlEventTouchUpInside]; 37 | [self.view addSubview:btn2]; 38 | btn2.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 3 * 2); 39 | 40 | 41 | 42 | UITextField *tf = [[UITextField alloc] initWithFrame:CGRectMake(30, 0, 300, 50)]; 43 | tf.placeholder = @"我是输入框"; 44 | [self.navigationController.navigationBar addSubview:tf]; 45 | 46 | UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(hiddenKB)]; 47 | [self.navigationItem setRightBarButtonItem:item]; 48 | } 49 | - (void)hiddenKB { 50 | [self.navigationController.view endEditing:YES]; 51 | } 52 | - (void)_____dependAlert { 53 | XYZSystemAlertViewActionBtn *act1 = [XYZSystemAlertViewActionBtn actionWithName:@"点击跳转测试页面" clickCallback:^{ 54 | 55 | [self.navigationController pushViewController:[ViewController new] animated:YES]; 56 | }]; 57 | XYZSystemAlertView *alert1 = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题1" msg:@"我的是描述描述描述描述描述描述"]; 58 | [alert1 addActionBtn:act1]; 59 | alert1.alertID = @"1"; 60 | 61 | XYZSystemAlertView *alert2 = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题2" msg:@"我的是描述描述描述描述描述描述"]; 62 | [alert2 addActionBtn:act1]; 63 | alert2.alertID = @"2"; 64 | 65 | XYZSystemAlertView *alert3 = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题3" msg:@"我的是描述描述描述描述描述描述"]; 66 | [alert3 addActionBtn:act1]; 67 | alert3.alertID = @"3"; 68 | alert3.showOnView = self.navigationController.view; 69 | 70 | 71 | // [alert1 setReadyAndTryDispath]; 72 | // [alert2 setReadyAndTryDispath]; 73 | // [alert3 setReadyAndTryDispath]; 74 | 75 | 76 | // [alert1 addDependencyAlertID:@"2"]; 77 | // [alert2 addDependencyAlertID:@"3"]; 78 | 79 | alert1.priority = 3; 80 | alert2.priority = 2; 81 | alert3.priority = 1; 82 | 83 | alert1.exclusiveBehavior = XYZAlertExclusiveBehaviorHiddenOther; 84 | 85 | // [alert1 addDependencyAlertID:@"3"]; 86 | 87 | [self.alertDispah addAlerts:@[alert1, alert2, alert3]]; 88 | 89 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 90 | [alert1 setCancelAndRemoveFromDispatch]; 91 | }); 92 | 93 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 94 | [alert2 setReadyAndTryDispath]; 95 | }); 96 | 97 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 98 | [alert3 setReadyAndTryDispath]; 99 | }); 100 | 101 | } 102 | - (void)_____XYZSimpleAlet { 103 | [_alert dismissWithAnimation:NO]; 104 | 105 | int maxIdx = 8; 106 | 107 | _aa++; 108 | 109 | if (_aa % maxIdx == 0) { 110 | _aa++; 111 | } 112 | 113 | 114 | if (_aa % maxIdx == 1) { 115 | _alert = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题" msg:nil]; 116 | }else if (_aa % maxIdx == 2) { 117 | _alert = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题" msg:nil]; 118 | XYZSystemAlertViewActionBtn *act = [XYZSystemAlertViewActionBtn actionWithName:@"确认" clickCallback:^{ 119 | NSLog(@"0"); 120 | }]; 121 | [act setImage:UIImage.actionsImage forState:UIControlStateNormal]; 122 | [_alert addActionBtn:act]; 123 | } 124 | else if (_aa % maxIdx == 3) { 125 | _alert = [[XYZSystemAlertView alloc] initWithTitle:nil msg:@"我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述"]; 126 | }else if (_aa % maxIdx == 4) { 127 | _alert = [[XYZSystemAlertView alloc] initWithTitle:nil msg:@"我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述"]; 128 | XYZSystemAlertViewActionBtn *act = [XYZSystemAlertViewActionBtn actionWithName:@"确认" clickCallback:^{ 129 | NSLog(@"0"); 130 | }]; 131 | XYZSystemAlertViewActionBtn *act1 = [XYZSystemAlertViewActionBtn actionWithName:@"取消" clickCallback:^{ 132 | NSLog(@"1"); 133 | }]; 134 | [_alert addActionBtn:act]; 135 | [_alert addActionBtn:act1]; 136 | } 137 | else if (_aa % maxIdx == 5) { 138 | _alert = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题" msg:@"我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述"]; 139 | }else if (_aa % maxIdx == 6) { 140 | _alert = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题" msg:@"我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述"]; 141 | XYZSystemAlertViewActionBtn *act = [XYZSystemAlertViewActionBtn actionWithName:@"确认" clickCallback:^{ 142 | NSLog(@"0"); 143 | }]; 144 | [_alert addActionBtn:act]; 145 | 146 | }else if (_aa % maxIdx == 7) { 147 | _alert = [[XYZSystemAlertView alloc] initWithTitle:@"我是标题" msg:@"我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描述我是描"]; 148 | XYZSystemAlertViewActionBtn *act = [XYZSystemAlertViewActionBtn actionWithName:@"确认" clickCallback:^{ 149 | NSLog(@"0"); 150 | }]; 151 | [act setImage:UIImage.removeImage forState:UIControlStateNormal]; 152 | 153 | XYZSystemAlertViewActionBtn *act1 = [XYZSystemAlertViewActionBtn actionWithName:@"取消" clickCallback:^{ 154 | NSLog(@"1"); 155 | }]; 156 | [_alert addActionBtn:act]; 157 | [_alert addActionBtn:act1]; 158 | } 159 | 160 | _alert.animationType = arc4random() % 3; 161 | [_alert showOnView:self.view]; 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /XYZAlert/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XYZAlert 4 | // 5 | // Created by 大大东 on 2021/4/13. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | --------------------------------------------------------------------------------