├── .gitignore ├── .swift-version ├── .travis.yml ├── Assets ├── KOAlertController_1.png ├── KOAlertController_2.png ├── KOAlertController_3.png ├── KOAlertController_4.png ├── KOAlertController_5.png ├── KOAlertController_6.png ├── KOAlertController_7.png ├── KOAlertController_8.png └── KOAlertController_9.png ├── Documentation └── Usage.md ├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── oleksandrkhymych.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── oleksandrkhymych.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── alert_image.imageset │ │ │ ├── Contents.json │ │ │ └── icon_achtung.pdf │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CustomTextField.swift │ ├── Info.plist │ └── ViewController.swift └── Podfile ├── KOAlertController.podspec ├── LICENSE ├── README.md └── Source ├── KOAlertButton.swift ├── KOAlertController.swift ├── KOAlertStyle.swift └── KOTypeButton.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode10.2 3 | script: 4 | - xcodebuild -scheme 'KOAlertController' -sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO test | xcpretty -c 5 | -------------------------------------------------------------------------------- /Assets/KOAlertController_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_1.png -------------------------------------------------------------------------------- /Assets/KOAlertController_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_2.png -------------------------------------------------------------------------------- /Assets/KOAlertController_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_3.png -------------------------------------------------------------------------------- /Assets/KOAlertController_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_4.png -------------------------------------------------------------------------------- /Assets/KOAlertController_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_5.png -------------------------------------------------------------------------------- /Assets/KOAlertController_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_6.png -------------------------------------------------------------------------------- /Assets/KOAlertController_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_7.png -------------------------------------------------------------------------------- /Assets/KOAlertController_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_8.png -------------------------------------------------------------------------------- /Assets/KOAlertController_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Assets/KOAlertController_9.png -------------------------------------------------------------------------------- /Documentation/Usage.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ### Example usage KOAlertController 4 | 5 | ```swift 6 | import KOAlertController 7 | // 8 | let alertController = KOAlertController("title", "message") 9 | self.present(alertController, animated: false) {} 10 | ``` 11 | ### Adds action 12 | 13 | ```swift 14 | let alertController = KOAlertController("title", "message") 15 | alertController.addAction(KOAlertButton(.default, title:"Close")) { 16 | 17 | } 18 | self.present(alertController, animated: false) {} 19 | ``` 20 | ### Adds a text field to an alert. 21 | 22 | ```swift 23 | let alertController = KOAlertController("title", "message") 24 | alertController.addTextField { (textField) in 25 | textField.placeholder = "Placeholder" 26 | textField.backgroundColor = .lightGray 27 | } 28 | alertController.addAction(KOAlertButton(.default, title:"Ok")) { 29 | 30 | } 31 | alertController.addAction(KOAlertButton(.cancel, title:"Cancel")) { 32 | 33 | } 34 | self.present(alertController, animated: false) {} 35 | ``` 36 | ### Add image. 37 | 38 | ```swift 39 | KOAlertController("title", "message", UIImage(named:"alert_image")) 40 | ``` 41 | ### Custom style alert. 42 | ```swift 43 | //Style alert 44 | let style = KOAlertStyle() 45 | style.backgroundColor = UIColor.black 46 | style.cornerRadius = 5 47 | style.messageColor = UIColor.lightGray 48 | style.titleColor = UIColor.white 49 | style.messageFont = UIFont.systemFont(ofSize: 17) 50 | style.titleFont = UIFont.systemFont(ofSize: 30) 51 | //style button 52 | let defButton = KOAlertButton(.default, title:"ok") 53 | defButton.backgroundColor = UIColor.white 54 | defButton.titleColor = UIColor.black 55 | let cancelButton = KOAlertButton(.cancel, title:"cancel") 56 | cancelButton.borderColor = UIColor.white 57 | cancelButton.titleColor = UIColor.white 58 | cancelButton.backgroundColor = UIColor.black 59 | // Alert controller 60 | let alertController = KOAlertController("title", "message", UIImage(named:"alert_image")) 61 | // Add custom alert style 62 | alertController.style = style 63 | // Add action 64 | alertController.addAction(defButton) { 65 | debugPrint("Action:OK") 66 | } 67 | // Add action 68 | alertController.addAction(cancelButton) { 69 | debugPrint("Action:Cancel") 70 | } 71 | self.present(alertController, animated: false) {} 72 | ``` 73 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 08AEE8C11FEBDC0E00D9E7D3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08AEE8C01FEBDC0E00D9E7D3 /* AppDelegate.swift */; }; 11 | 08AEE8C31FEBDC0E00D9E7D3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08AEE8C21FEBDC0E00D9E7D3 /* ViewController.swift */; }; 12 | 08AEE8C61FEBDC0E00D9E7D3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 08AEE8C41FEBDC0E00D9E7D3 /* Main.storyboard */; }; 13 | 08AEE8C81FEBDC0E00D9E7D3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 08AEE8C71FEBDC0E00D9E7D3 /* Assets.xcassets */; }; 14 | 08AEE8CB1FEBDC0E00D9E7D3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 08AEE8C91FEBDC0E00D9E7D3 /* LaunchScreen.storyboard */; }; 15 | 08AEE8DB1FF2850300D9E7D3 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08AEE8DA1FF2850300D9E7D3 /* CustomTextField.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 08AEE8BD1FEBDC0E00D9E7D3 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 08AEE8C01FEBDC0E00D9E7D3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 08AEE8C21FEBDC0E00D9E7D3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 22 | 08AEE8C51FEBDC0E00D9E7D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 23 | 08AEE8C71FEBDC0E00D9E7D3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 24 | 08AEE8CA1FEBDC0E00D9E7D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 25 | 08AEE8CC1FEBDC0E00D9E7D3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | 08AEE8DA1FF2850300D9E7D3 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 08AEE8BA1FEBDC0E00D9E7D3 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 08AEE8B41FEBDC0E00D9E7D3 = { 41 | isa = PBXGroup; 42 | children = ( 43 | 08AEE8BF1FEBDC0E00D9E7D3 /* Example */, 44 | 08AEE8BE1FEBDC0E00D9E7D3 /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | 08AEE8BE1FEBDC0E00D9E7D3 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 08AEE8BD1FEBDC0E00D9E7D3 /* Example.app */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | 08AEE8BF1FEBDC0E00D9E7D3 /* Example */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 08AEE8C01FEBDC0E00D9E7D3 /* AppDelegate.swift */, 60 | 08AEE8C21FEBDC0E00D9E7D3 /* ViewController.swift */, 61 | 08AEE8DA1FF2850300D9E7D3 /* CustomTextField.swift */, 62 | 08AEE8C41FEBDC0E00D9E7D3 /* Main.storyboard */, 63 | 08AEE8C71FEBDC0E00D9E7D3 /* Assets.xcassets */, 64 | 08AEE8C91FEBDC0E00D9E7D3 /* LaunchScreen.storyboard */, 65 | 08AEE8CC1FEBDC0E00D9E7D3 /* Info.plist */, 66 | ); 67 | path = Example; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | 08AEE8BC1FEBDC0E00D9E7D3 /* Example */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = 08AEE8CF1FEBDC0E00D9E7D3 /* Build configuration list for PBXNativeTarget "Example" */; 76 | buildPhases = ( 77 | 08AEE8B91FEBDC0E00D9E7D3 /* Sources */, 78 | 08AEE8BA1FEBDC0E00D9E7D3 /* Frameworks */, 79 | 08AEE8BB1FEBDC0E00D9E7D3 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = Example; 86 | productName = Example; 87 | productReference = 08AEE8BD1FEBDC0E00D9E7D3 /* Example.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | 08AEE8B51FEBDC0E00D9E7D3 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastSwiftUpdateCheck = 0920; 97 | LastUpgradeCheck = 0920; 98 | ORGANIZATIONNAME = "Oleksandr Khymych"; 99 | TargetAttributes = { 100 | 08AEE8BC1FEBDC0E00D9E7D3 = { 101 | CreatedOnToolsVersion = 9.2; 102 | ProvisioningStyle = Automatic; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 08AEE8B81FEBDC0E00D9E7D3 /* Build configuration list for PBXProject "Example" */; 107 | compatibilityVersion = "Xcode 8.0"; 108 | developmentRegion = en; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | Base, 113 | ); 114 | mainGroup = 08AEE8B41FEBDC0E00D9E7D3; 115 | productRefGroup = 08AEE8BE1FEBDC0E00D9E7D3 /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | 08AEE8BC1FEBDC0E00D9E7D3 /* Example */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | 08AEE8BB1FEBDC0E00D9E7D3 /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 08AEE8CB1FEBDC0E00D9E7D3 /* LaunchScreen.storyboard in Resources */, 130 | 08AEE8C81FEBDC0E00D9E7D3 /* Assets.xcassets in Resources */, 131 | 08AEE8C61FEBDC0E00D9E7D3 /* Main.storyboard in Resources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXResourcesBuildPhase section */ 136 | 137 | /* Begin PBXSourcesBuildPhase section */ 138 | 08AEE8B91FEBDC0E00D9E7D3 /* Sources */ = { 139 | isa = PBXSourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 08AEE8DB1FF2850300D9E7D3 /* CustomTextField.swift in Sources */, 143 | 08AEE8C31FEBDC0E00D9E7D3 /* ViewController.swift in Sources */, 144 | 08AEE8C11FEBDC0E00D9E7D3 /* AppDelegate.swift in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin PBXVariantGroup section */ 151 | 08AEE8C41FEBDC0E00D9E7D3 /* Main.storyboard */ = { 152 | isa = PBXVariantGroup; 153 | children = ( 154 | 08AEE8C51FEBDC0E00D9E7D3 /* Base */, 155 | ); 156 | name = Main.storyboard; 157 | sourceTree = ""; 158 | }; 159 | 08AEE8C91FEBDC0E00D9E7D3 /* LaunchScreen.storyboard */ = { 160 | isa = PBXVariantGroup; 161 | children = ( 162 | 08AEE8CA1FEBDC0E00D9E7D3 /* Base */, 163 | ); 164 | name = LaunchScreen.storyboard; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXVariantGroup section */ 168 | 169 | /* Begin XCBuildConfiguration section */ 170 | 08AEE8CD1FEBDC0E00D9E7D3 /* Debug */ = { 171 | isa = XCBuildConfiguration; 172 | buildSettings = { 173 | ALWAYS_SEARCH_USER_PATHS = NO; 174 | CLANG_ANALYZER_NONNULL = YES; 175 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 176 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 177 | CLANG_CXX_LIBRARY = "libc++"; 178 | CLANG_ENABLE_MODULES = YES; 179 | CLANG_ENABLE_OBJC_ARC = YES; 180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_COMMA = YES; 183 | CLANG_WARN_CONSTANT_CONVERSION = YES; 184 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 185 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 186 | CLANG_WARN_EMPTY_BODY = YES; 187 | CLANG_WARN_ENUM_CONVERSION = YES; 188 | CLANG_WARN_INFINITE_RECURSION = YES; 189 | CLANG_WARN_INT_CONVERSION = YES; 190 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 191 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 192 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 193 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 194 | CLANG_WARN_STRICT_PROTOTYPES = YES; 195 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 196 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 197 | CLANG_WARN_UNREACHABLE_CODE = YES; 198 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 199 | CODE_SIGN_IDENTITY = "iPhone Developer"; 200 | COPY_PHASE_STRIP = NO; 201 | DEBUG_INFORMATION_FORMAT = dwarf; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | ENABLE_TESTABILITY = YES; 204 | GCC_C_LANGUAGE_STANDARD = gnu11; 205 | GCC_DYNAMIC_NO_PIC = NO; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_OPTIMIZATION_LEVEL = 0; 208 | GCC_PREPROCESSOR_DEFINITIONS = ( 209 | "DEBUG=1", 210 | "$(inherited)", 211 | ); 212 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 213 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 214 | GCC_WARN_UNDECLARED_SELECTOR = YES; 215 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 216 | GCC_WARN_UNUSED_FUNCTION = YES; 217 | GCC_WARN_UNUSED_VARIABLE = YES; 218 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 219 | MTL_ENABLE_DEBUG_INFO = YES; 220 | ONLY_ACTIVE_ARCH = YES; 221 | SDKROOT = iphoneos; 222 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 223 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 224 | }; 225 | name = Debug; 226 | }; 227 | 08AEE8CE1FEBDC0E00D9E7D3 /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | ALWAYS_SEARCH_USER_PATHS = NO; 231 | CLANG_ANALYZER_NONNULL = YES; 232 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 233 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 234 | CLANG_CXX_LIBRARY = "libc++"; 235 | CLANG_ENABLE_MODULES = YES; 236 | CLANG_ENABLE_OBJC_ARC = YES; 237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_COMMA = YES; 240 | CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES; 249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 251 | CLANG_WARN_STRICT_PROTOTYPES = YES; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 254 | CLANG_WARN_UNREACHABLE_CODE = YES; 255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 256 | CODE_SIGN_IDENTITY = "iPhone Developer"; 257 | COPY_PHASE_STRIP = NO; 258 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 259 | ENABLE_NS_ASSERTIONS = NO; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | GCC_C_LANGUAGE_STANDARD = gnu11; 262 | GCC_NO_COMMON_BLOCKS = YES; 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 270 | MTL_ENABLE_DEBUG_INFO = NO; 271 | SDKROOT = iphoneos; 272 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 273 | VALIDATE_PRODUCT = YES; 274 | }; 275 | name = Release; 276 | }; 277 | 08AEE8D01FEBDC0E00D9E7D3 /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 281 | CODE_SIGN_STYLE = Automatic; 282 | DEVELOPMENT_TEAM = PM85A2KGPM; 283 | INFOPLIST_FILE = Example/Info.plist; 284 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 285 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 286 | PRODUCT_BUNDLE_IDENTIFIER = com.khymych.Example; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | SWIFT_VERSION = 4.0; 289 | TARGETED_DEVICE_FAMILY = "1,2"; 290 | }; 291 | name = Debug; 292 | }; 293 | 08AEE8D11FEBDC0E00D9E7D3 /* Release */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 297 | CODE_SIGN_STYLE = Automatic; 298 | DEVELOPMENT_TEAM = PM85A2KGPM; 299 | INFOPLIST_FILE = Example/Info.plist; 300 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 301 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 302 | PRODUCT_BUNDLE_IDENTIFIER = com.khymych.Example; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_VERSION = 4.0; 305 | TARGETED_DEVICE_FAMILY = "1,2"; 306 | }; 307 | name = Release; 308 | }; 309 | /* End XCBuildConfiguration section */ 310 | 311 | /* Begin XCConfigurationList section */ 312 | 08AEE8B81FEBDC0E00D9E7D3 /* Build configuration list for PBXProject "Example" */ = { 313 | isa = XCConfigurationList; 314 | buildConfigurations = ( 315 | 08AEE8CD1FEBDC0E00D9E7D3 /* Debug */, 316 | 08AEE8CE1FEBDC0E00D9E7D3 /* Release */, 317 | ); 318 | defaultConfigurationIsVisible = 0; 319 | defaultConfigurationName = Release; 320 | }; 321 | 08AEE8CF1FEBDC0E00D9E7D3 /* Build configuration list for PBXNativeTarget "Example" */ = { 322 | isa = XCConfigurationList; 323 | buildConfigurations = ( 324 | 08AEE8D01FEBDC0E00D9E7D3 /* Debug */, 325 | 08AEE8D11FEBDC0E00D9E7D3 /* Release */, 326 | ); 327 | defaultConfigurationIsVisible = 0; 328 | defaultConfigurationName = Release; 329 | }; 330 | /* End XCConfigurationList section */ 331 | }; 332 | rootObject = 08AEE8B51FEBDC0E00D9E7D3 /* Project object */; 333 | } 334 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/xcuserdata/oleksandrkhymych.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Example/Example.xcodeproj/project.xcworkspace/xcuserdata/oleksandrkhymych.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/oleksandrkhymych.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/oleksandrkhymych.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Oleksandr Khymych on 21.12.2017. 6 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | return true 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/alert_image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_achtung.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/alert_image.imageset/icon_achtung.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethSky/KOAlertController/b1ea29bb297d48b00db96345562c211dd7f3c200/Example/Example/Assets.xcassets/alert_image.imageset/icon_achtung.pdf -------------------------------------------------------------------------------- /Example/Example/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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Example/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 | 47 | 48 | -------------------------------------------------------------------------------- /Example/Example/CustomTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTextField.swift 3 | // Example 4 | // 5 | // Created by Oleksandr Khymych on 26.12.2017. 6 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class CustomTextField: UITextField { 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | 16 | self.backgroundColor = UIColor.green 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | super.init(coder: aDecoder) 21 | self.backgroundColor = UIColor.green 22 | self.textColor = .black 23 | // fatalError("init(coder:) has not been implemented") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Example/Example/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Created by Oleksandr Khymych on 21.12.2017. 6 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import KOAlertController 11 | 12 | class ViewController: UIViewController { 13 | //–––––––––––––––––––––––––––––––––––––––– 14 | //MARK: - Outlets - 15 | //–––––––––––––––––––––––––––––––––––––––– 16 | @IBOutlet weak private var tableView: UITableView! 17 | //–––––––––––––––––––––––––––––––––––––––– 18 | //MARK: - Property - 19 | //–––––––––––––––––––––––––––––––––––––––– 20 | /// Content array 21 | fileprivate var contentArray:Array = ["Title and message", 22 | "Title, message and image", 23 | "Two buttons", 24 | "Three buttons", 25 | "With UITextField", 26 | "With more text in message", 27 | "Custom style alert", 28 | "With header view", 29 | "With custom UITextField"] 30 | // Text 31 | private let message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tincidunt nisi leo, sit amet volutpat justo placerat et. Morbi aliquam magna at odio mollis imperdiet. Nunc viverra elit velit posuere." 32 | 33 | /// More text 34 | private let moreTextMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nec consectetur lorem. Aenean ac erat quis turpis pretium dignissim a eget nulla. Duis at libero euismod, sodales lectus ac, volutpat dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis vestibulum condimentum ligula eu iaculis. Nullam hendrerit, risus pellentesque accumsan accumsan, enim eros ultrices leo, et tristique nulla massa a magna. In nibh velit, ultricies vitae erat sit amet, faucibus facilisis nisi. Suspendisse sapien sem, accumsan ac nulla vel, ultrices rutrum enim. Curabitur porttitor at tellus id feugiat. Fusce venenatis est non erat vulputate, vel cursus felis euismod. Quisque mattis est nec mauris rhoncus tristique. Donec sollicitudin lobortis leo eget fringilla. Aliquam tempor lacus et egestas volutpat. Donec id placerat neque, at eleifend est. Nulla facilisi. Donec aliquam dapibus nisl, ac consequat turpis lacinia et. Curabitur faucibus dolor vitae tellus euismod, eget rhoncus turpis tincidunt. Nulla urna libero, cursus sit amet malesuada quis, ultrices a libero. Sed egestas, urna eu finibus consequat, neque dui lobortis quam, sed dapibus lorem nibh ut magna. Aliquam condimentum tortor vel tortor imperdiet, eget molestie purus posuere. Nunc tristique turpis at mi posuere, et accumsan urna fermentum. Vivamus sed orci porta, luctus nisl volutpat, vehicula enim. Integer sollicitudin convallis arcu eget efficitur. Duis facilisis interdum dolor ut aliquet. Mauris semper sed ligula ac tristique. Duis id ligula vel erat tristique molestie eget nec nisi. Pellentesque nec consequat arcu, nec tempus libero. In sed nunc vulputate erat ornare dignissim vel at sapien. Nulla commodo eget magna et luctus. Donec ac porta est. Vivamus gravida leo vitae dui commodo, vel pellentesque tellus lobortis. Phasellus maximus dolor quis mattis ornare. Nulla et placerat risus. Nullam vel lacus bibendum, laoreet tellus ac, porttitor justo. Quisque bibendum lorem ut nunc finibus porta. Etiam sagittis dignissim ante, non finibus urna lobortis ac. Nullam eu dui quis mi efficitur vulputate id ut est. Donec dapibus turpis scelerisque ex eleifend, eu convallis erat rutrum. Nullam auctor, augue eget egestas mollis, nisl lectus bibendum risus, vel blandit ligula ex sit amet justo. Maecenas sem ante, sollicitudin ut neque ac, congue feugiat velit. Aenean id felis rhoncus, accumsan metus ac, faucibus ligula. Aliquam maximus eu urna eget fringilla." 35 | //–––––––––––––––––––––––––––––––––––––––– 36 | //MARK: - Show Alert - 37 | //–––––––––––––––––––––––––––––––––––––––– 38 | fileprivate func showAlert(_ index:Int){ 39 | switch index { 40 | case 0: 41 | self.alert_1(contentArray[index]) 42 | case 1: 43 | self.alert_2(contentArray[index]) 44 | case 2: 45 | self.alert_3(contentArray[index]) 46 | case 3: 47 | self.alert_4(contentArray[index]) 48 | case 4: 49 | self.alert_5(contentArray[index]) 50 | case 5: 51 | self.alert_6(contentArray[index]) 52 | case 6: 53 | self.alert_7(contentArray[index]) 54 | case 7: 55 | self.alert_8(contentArray[index]) 56 | case 8: 57 | self.alert_9(contentArray[index]) 58 | default: 59 | break 60 | } 61 | } 62 | //–––––––––––––––––––––––––––––––––––––––– 63 | //MARK: - Exemple creating alerts - 64 | //–––––––––––––––––––––––––––––––––––––––– 65 | /// Title and message 66 | /// 67 | /// - Parameter title: String 68 | private func alert_1(_ title:String){ 69 | let alertController = KOAlertController(title, self.message) 70 | alertController.addAction(KOAlertButton(.default, title:"Close")) { 71 | debugPrint("Action:Close") 72 | } 73 | self.present(alertController, animated: false) {} 74 | } 75 | /// Title, message and image 76 | /// 77 | /// - Parameter title: String 78 | private func alert_2(_ title:String){ 79 | let alertController = KOAlertController(title, self.message, UIImage(named:"alert_image")) 80 | alertController.addAction(KOAlertButton(.default, title:"Close")) {debugPrint("Action:Close")} 81 | self.present(alertController, animated: false) {} 82 | } 83 | /// Two buttons 84 | /// 85 | /// - Parameter title: String 86 | private func alert_3(_ title:String){ 87 | let alertController = KOAlertController(title, self.message, UIImage(named:"alert_image")) 88 | alertController.addAction(KOAlertButton(.default, title:"Ok")) {debugPrint("Action:Ok")} 89 | alertController.addAction(KOAlertButton(.cancel, title:"Cancel")) {debugPrint("Action:Cancel")} 90 | self.present(alertController, animated: false) {} 91 | } 92 | /// Three buttons 93 | /// 94 | /// - Parameter title: String 95 | private func alert_4(_ title:String){ 96 | let alertController = KOAlertController(title, self.message, UIImage(named:"alert_image")) 97 | alertController.addAction(KOAlertButton(.default, title:"1")) {debugPrint("Action:1")} 98 | alertController.addAction(KOAlertButton(.cancel, title:"2")) {debugPrint("Action:2")} 99 | alertController.addAction(KOAlertButton(.default, title:"3")) {debugPrint("Action:3")} 100 | self.present(alertController, animated: false) {} 101 | } 102 | /// With UITextField 103 | /// 104 | /// - Parameter title: String 105 | private func alert_5(_ title:String){ 106 | let alertController = KOAlertController(title, self.message) 107 | alertController.addTextField { (textField) in 108 | textField.placeholder = "Placeholder" 109 | textField.backgroundColor = .lightGray 110 | } 111 | alertController.addAction(KOAlertButton(.default, title:"Ok")) { 112 | debugPrint("Action:Ok") 113 | } 114 | alertController.addAction(KOAlertButton(.cancel, title:"Cancel")) { 115 | debugPrint("Action:Cancel") 116 | } 117 | self.present(alertController, animated: false) {} 118 | } 119 | // 120 | private func alert_6(_ title:String){ 121 | let alertController = KOAlertController(title, self.moreTextMessage) 122 | alertController.addAction(KOAlertButton(.default, title:"Close")) { 123 | debugPrint("Action:Close") 124 | } 125 | self.present(alertController, animated: false) {} 126 | } 127 | /// Custom style alert 128 | /// 129 | /// - Parameter title: String 130 | private func alert_7(_ title:String){ 131 | //Style alert 132 | let style = KOAlertStyle() 133 | style.backgroundColor = UIColor.black 134 | style.cornerRadius = 5 135 | style.messageColor = UIColor.lightGray 136 | style.titleColor = UIColor.white 137 | style.messageFont = UIFont.systemFont(ofSize: 17) 138 | style.titleFont = UIFont.systemFont(ofSize: 30) 139 | //style button 140 | let defButton = KOAlertButton(.default, title:"ok") 141 | defButton.backgroundColor = UIColor.white 142 | defButton.titleColor = UIColor.black 143 | let cancelButton = KOAlertButton(.cancel, title:"cancel") 144 | cancelButton.borderColor = UIColor.white 145 | cancelButton.titleColor = UIColor.white 146 | cancelButton.backgroundColor = UIColor.black 147 | // Alert controller 148 | let alertController = KOAlertController(title, self.message, UIImage(named:"alert_image")) 149 | // Add custom alert style 150 | alertController.style = style 151 | // Add action 152 | alertController.addAction(defButton) { 153 | debugPrint("Action:OK") 154 | } 155 | // Add action 156 | alertController.addAction(cancelButton) { 157 | debugPrint("Action:Cancel") 158 | } 159 | self.present(alertController, animated: false) {} 160 | } 161 | /// Alert with header 162 | /// 163 | /// - Parameter title: String 164 | private func alert_8(_ title:String){ 165 | let headerView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 44)) 166 | let button = UIButton(frame: CGRect(x: 0, y: 0, width: 190, height: 25)) 167 | button.setTitle("Header button", for: .normal) 168 | button.setTitleColor(UIColor.blue, for: .normal) 169 | button.setTitleColor(UIColor.blue.withAlphaComponent(0.25), for: .highlighted) 170 | button.contentHorizontalAlignment = .left 171 | headerView.addSubview(button) 172 | /// 173 | let alertController = KOAlertController(title, self.message) 174 | alertController.addHeaderView(headerView) 175 | alertController.addAction(KOAlertButton(.default, title:"Close")) { 176 | debugPrint("Action:Close") 177 | } 178 | self.present(alertController, animated: false) {} 179 | } 180 | /// Alert with custom UITextField 181 | /// 182 | /// - Parameter title: String 183 | private func alert_9(_ title:String){ 184 | /// 185 | let alertController = KOAlertController(title, self.message) 186 | //Need create textField with frame and set height 187 | alertController.alertTextField = CustomTextField(frame: CGRect(x: 0, y: 0, width: 0, height: 60)) 188 | alertController.addTextField { (textField) in 189 | 190 | } 191 | alertController.addAction(KOAlertButton(.default, title:"Close")) { 192 | debugPrint("Action:Close") 193 | } 194 | self.present(alertController, animated: false) {} 195 | } 196 | } 197 | //–––––––––––––––––––––––––––––––––––––––– 198 | //MARK: - UITableViewDataSource - 199 | //–––––––––––––––––––––––––––––––––––––––– 200 | extension ViewController: UITableViewDataSource{ 201 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ 202 | return self.contentArray.count 203 | } 204 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{ 205 | let cell = UITableViewCell(style: .default, reuseIdentifier: nil) 206 | cell.textLabel?.text = contentArray[indexPath.row] 207 | return cell 208 | } 209 | } 210 | //–––––––––––––––––––––––––––––––––––––––– 211 | //MARK: - UITableViewDelegate - 212 | //–––––––––––––––––––––––––––––––––––––––– 213 | extension ViewController: UITableViewDelegate{ 214 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 215 | self.showAlert(indexPath.row) 216 | tableView.deselectRow(at: indexPath, animated: true) 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | use_frameworks! 3 | 4 | target 'Example' do 5 | pod 'KOAlertController', '~> 1.0.2' 6 | end 7 | -------------------------------------------------------------------------------- /KOAlertController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'KOAlertController' 3 | s.version = '1.0.3' 4 | s.license = 'MIT' 5 | s.summary = 'KOAlertController is an custom alert controller library written in Swift' 6 | s.homepage = 'https://github.com/SethSky/KOAlertController' 7 | s.authors = { 'Oleksandr Khymych' => 'seth@khymych.com' } 8 | s.source = { :git => 'https://github.com/SethSky/KOAlertController.git', :tag => s.version } 9 | s.swift_version = '5.0' 10 | 11 | s.ios.deployment_target = '9.0' 12 | 13 | s.source_files = 'Source/*.swift' 14 | end 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2017 Oleksandr Khymych, Inc. http://khymych.com 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 13 | all 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 NON INFRINGEMENT. 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/KOAlertController.svg)](https://img.shields.io/cocoapods/v/KOAlertController.svg) 2 | [![Platform](https://img.shields.io/cocoapods/p/KOAlertController.svg?style=flat)](https://img.shields.io/cocoapods/p/KOAlertController.svg?style=flat) 3 | [![Build Status](https://travis-ci.org/SethSky/KOAlertController.svg?branch=master)](https://travis-ci.org/SethSky/KOAlertController) 4 | 5 | KOAlertController is an custom alert controller library written in Swift. 6 | Use this class to configure alerts and action sheets with the message that you want to display and the actions from which to choose 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | - [Features](#features) 18 | - [Requirements](#requirements) 19 | - [Installation](#installation) 20 | - [Usage](Documentation/Usage.md) 21 | - [Screenshots](Assets/) 22 | - [License](#license) 23 | 24 | ## Features 25 | 26 | - [x] Add actions with style button 27 | - [x] When configuring an alert with the alert style, you can also add text fields to the alert interface 28 | - [x] Has the ability to use custom text fields 29 | - [x] Custom style (Colors, Fonts, Padding) 30 | - [x] Add Image to the right area alert view 31 | - [x] Built-in scroll into the content area 32 | - [x] Supported landscape and portrait interface orientation 33 | 34 | ## Requirements 35 | 36 | - iOS 9.0+ 37 | - Xcode 8.3+ 38 | - Swift 3.1+ 39 | 40 | ## Installation 41 | 42 | ### CocoaPods 43 | 44 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 45 | 46 | ```bash 47 | $ gem install cocoapods 48 | ``` 49 | 50 | > CocoaPods 1.1+ is required to build KOAlertController 1.0.1+. 51 | 52 | To integrate KOAlertController into your Xcode project using CocoaPods, specify it in your `Podfile`: 53 | 54 | ```ruby 55 | source 'https://github.com/CocoaPods/Specs.git' 56 | platform :ios, '10.0' 57 | use_frameworks! 58 | 59 | target '' do 60 | pod 'KOAlertController', '~> 1.0.2' 61 | end 62 | ``` 63 | 64 | Then, run the following command: 65 | 66 | ```bash 67 | $ pod install 68 | ``` 69 | ## License 70 | 71 | KOAlertController is released under the MIT license. [See LICENSE](LICENSE) for details. 72 | -------------------------------------------------------------------------------- /Source/KOAlertButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KOAlertButton.swift 3 | // 4 | // Created by Oleksandr Khymych on 13.12.2017. 5 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// Aler button style 12 | open class KOAlertButton{ 13 | public var backgroundColor : UIColor! 14 | public var titleColor : UIColor! 15 | public var font : UIFont! 16 | public var borderColor : UIColor! 17 | public var borderWidth : CGFloat! 18 | public var cornerRadius : CGFloat! 19 | public var title : String! 20 | public init(_ type:KOTypeButton, title:String) { 21 | self.title = title.uppercased() 22 | switch type { 23 | case .cancel: 24 | self.backgroundColor = UIColor.white 25 | self.titleColor = UIColor.black 26 | self.font = UIFont.boldSystemFont(ofSize: 19) 27 | self.borderColor = UIColor.black 28 | self.borderWidth = 1 29 | self.cornerRadius = 2.0 30 | default: 31 | self.backgroundColor = UIColor.black 32 | self.titleColor = UIColor.white 33 | self.font = UIFont.boldSystemFont(ofSize: 19) 34 | self.borderColor = UIColor.black 35 | self.borderWidth = 0 36 | self.cornerRadius = 2.0 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/KOAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KOAlertController.swift 3 | // 4 | // Created by Oleksandr Khymych on 11.12.2017. 5 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// KOAlertController 12 | open class KOAlertController : UIViewController { 13 | // MARK: - Enum 14 | private enum ContentState { 15 | case image 16 | case textField 17 | case imageANDtextField 18 | } 19 | // MARK: - Public property 20 | //Style property 21 | public var style: KOAlertStyle = KOAlertStyle() 22 | // Max length for textField 23 | public var maxLengthTextField: Int = 128 24 | // Image 25 | public var image: UIImage? 26 | // UIEdgeInsets for root alert view 27 | public var insets: UIEdgeInsets = UIEdgeInsets(top: 20, left: 5, bottom: -2, right: 5) 28 | // UIEdgeInsets for button view 29 | public var buttonContainerInsets: UIEdgeInsets = UIEdgeInsets(top: 20, left: 16, bottom: 19, right: 16) 30 | // UIEdgeInsets for textField view 31 | public var textFieldContainerInsets: UIEdgeInsets = UIEdgeInsets(top: 20, left: 16, bottom: 0, right: 16) 32 | // UIEdgeInsets for textField view 33 | public var infoContainerInsets: UIEdgeInsets = UIEdgeInsets(top: 18, left: 16, bottom: 0, right: 16) 34 | // UIEdgeInsets for Image View 35 | public var imageInsets: UIEdgeInsets = UIEdgeInsets(top: 18, left: 4, bottom: 0, right: 16) 36 | // height button 37 | public var heightButton: CGFloat = 55 38 | // Image size 39 | public var imageSize: CGSize = CGSize(width: 80, height: 80) 40 | // Custom TextField 41 | public var alertTextField: AnyObject? 42 | // Duration animation 43 | public var animationDuration: TimeInterval = 0.3 44 | // MARK: - Private property 45 | // Message text 46 | private var _message: String? 47 | // position constraint 48 | private var _positionConstrant: NSLayoutConstraint? 49 | // Height scroll constraint 50 | private var _heightScrollConstraint: NSLayoutConstraint? 51 | // Content container 52 | private var _scrollView: UIScrollView? 53 | // Alert container 54 | private var _alertView: UIView? 55 | // Button container 56 | private var _buttonView: UIView? 57 | // Info container 58 | private var _infoView: UIView? 59 | // Header container optional 60 | private var _headerView: UIView? 61 | // textField container 62 | private var _textFieldView: UIView? 63 | //Image view 64 | private var _imageView: UIImageView? 65 | // OperationQueue 66 | private let _queue: OperationQueue = OperationQueue.main 67 | // Operations array 68 | private var _opsArray: Array = [] 69 | // Button Array 70 | private var _buttonArray: Array = [] 71 | // UITextField Array 72 | private var _textFieldArray: Array = [] 73 | // Actions array 74 | private var _actionArray: Array<(() -> Void)?> = [] 75 | // MARK: - Init 76 | /// Init alert with title 77 | /// 78 | /// - Parameter title: String optional 79 | public init(_ title: String?) { 80 | super.init(nibName: nil, bundle: nil) 81 | self.title = title 82 | modalPresentationStyle = .overCurrentContext 83 | } 84 | /// Init alert with title and message 85 | /// 86 | /// - Parameters: 87 | /// - title: String optional 88 | /// - message: String optional 89 | public init(_ title: String?, _ message: String?) { 90 | super.init(nibName: nil, bundle: nil) 91 | self.title = title 92 | _message = message 93 | modalPresentationStyle = .overCurrentContext 94 | } 95 | /// Init alert with title and message, icon image 96 | /// 97 | /// - Parameters: 98 | /// - title: String optional 99 | /// - message: String optional 100 | /// - image: UIImage optional 101 | public init(_ title: String?, _ message: String?, _ image: UIImage?) { 102 | super.init(nibName: nil, bundle: nil) 103 | self.title = title 104 | _message = message 105 | self.image = image 106 | modalPresentationStyle = .overCurrentContext 107 | } 108 | public required init?(coder aDecoder: NSCoder) { 109 | fatalError("init(coder:) has not been implemented") 110 | } 111 | // MARK: - View lifecycle 112 | override open func viewDidLoad() { 113 | super.viewDidLoad() 114 | settings() 115 | } 116 | override open func viewWillAppear(_ animated: Bool) { 117 | super.viewWillAppear(animated) 118 | for ops in _opsArray { 119 | ops.start() 120 | } 121 | _opsArray.removeAll() 122 | } 123 | override open func viewDidAppear(_ animated: Bool) { 124 | super.viewDidAppear(animated) 125 | UIView.animate(withDuration: animationDuration, animations: { 126 | self.view.backgroundColor = UIColor.black.withAlphaComponent(0.70) 127 | self._positionConstrant?.constant = self.insets.bottom 128 | self.view.layoutIfNeeded() 129 | }) { (compete) in 130 | self._textFieldArray.first?.becomeFirstResponder() 131 | } 132 | } 133 | override open var preferredStatusBarStyle: UIStatusBarStyle { 134 | return presentingViewController?.preferredStatusBarStyle ?? UIStatusBarStyle.default 135 | } 136 | override open func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation){ 137 | UIView.animate(withDuration:self.animationDuration) { 138 | self._heightScrollConstraint?.constant = self.calculateScrollViewHeight() 139 | self.view.layoutIfNeeded() 140 | } 141 | } 142 | // MARK: - Settings 143 | private func settings() { 144 | //Background view settings 145 | backgroundViewSettings() 146 | //Create alert root view 147 | createAlertView() 148 | //Create and add horizontal button view 149 | createButtonView() 150 | //Create container view 151 | createContainerView() 152 | //Register Keyboard Notifications 153 | registerForKeyboardNotifications() 154 | // Add function in blockOperation to blockOperation Array 155 | _opsArray.append(BlockOperation(block: { 156 | self.createInfoView() 157 | self.createTextContent() 158 | guard let buttonView = self._buttonView else { return } 159 | self.addButtonConstraints(self._buttonArray, mainView: buttonView, height: self.heightButton) 160 | })) 161 | } 162 | /// Background view settings 163 | private func backgroundViewSettings() { 164 | view.backgroundColor = UIColor.black.withAlphaComponent(0.0) 165 | } 166 | // MARK: - Create views 167 | /// Create UIView with background color and corner radius 168 | /// 169 | /// - Parameters: 170 | /// - backgroundColor: UIColor 171 | /// - cornerRadius: CGFloat 172 | /// - Returns: UIView 173 | private func createView(_ backgroundColor: UIColor,_ cornerRadius: CGFloat) -> UIView { 174 | let view = UIView() 175 | view.backgroundColor = backgroundColor 176 | view.layer.cornerRadius = cornerRadius 177 | view.translatesAutoresizingMaskIntoConstraints = false 178 | return view 179 | } 180 | /// Create alert root view 181 | private func createAlertView() { 182 | let alertView = createView(style.backgroundColor, style.cornerRadius) 183 | _alertView = alertView 184 | view.addSubview(alertView) 185 | let screenHeight = UIScreen.main.bounds.height 186 | //Add constraints 187 | switch style.position { 188 | case .center: 189 | _positionConstrant = view.centerYAnchor.constraint(equalTo: alertView.centerYAnchor, constant: -screenHeight) 190 | default: 191 | _positionConstrant = view.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -screenHeight) 192 | } 193 | guard let positionConstrant = _positionConstrant else { return } 194 | view.addConstraints([ 195 | view.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: insets.right), 196 | alertView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left), 197 | positionConstrant 198 | ]) 199 | } 200 | /// Create and add button view 201 | private func createButtonView() { 202 | _buttonView = createView(.clear, 0) 203 | guard let alertView = _alertView, let buttonView = _buttonView else { return } 204 | alertView.addSubview(buttonView) 205 | var bottomPadding: CGFloat = 0 206 | if #available(iOS 11.0, *) { 207 | let window = UIApplication.shared.keyWindow 208 | bottomPadding = window?.safeAreaInsets.bottom ?? 0 209 | } 210 | //Add constraints 211 | alertView.addConstraints([ 212 | alertView.trailingAnchor.constraint(equalTo: buttonView.trailingAnchor, constant: buttonContainerInsets.right), 213 | buttonView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: buttonContainerInsets.left), 214 | alertView.bottomAnchor.constraint(equalTo: buttonView.bottomAnchor, constant: buttonContainerInsets.bottom + bottomPadding) 215 | ]) 216 | } 217 | /// Create container view 218 | private func createContainerView() { 219 | _scrollView = UIScrollView() 220 | guard let scrollView = _scrollView, let alertView = _alertView, let buttonView = _buttonView else { return } 221 | scrollView.translatesAutoresizingMaskIntoConstraints = false 222 | alertView.addSubview(scrollView) 223 | _heightScrollConstraint = scrollView.heightAnchor.constraint(equalToConstant: 0) 224 | guard let heightScrollConstraint = _heightScrollConstraint else { return } 225 | //Add constraints 226 | alertView.addConstraints([ 227 | alertView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), 228 | scrollView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor), 229 | scrollView.topAnchor.constraint(equalTo: alertView.topAnchor), 230 | buttonView.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: buttonContainerInsets.top), 231 | heightScrollConstraint 232 | ]) 233 | } 234 | /// Create info view 235 | private func createInfoView() { 236 | // Add infoView to scrollView 237 | _infoView = createView(.clear, 0) 238 | guard let scrollView = _scrollView, let infoView = _infoView else { return } 239 | scrollView.addSubview(infoView) 240 | // Create textFieldView and add to scrollView if array not empty 241 | if _textFieldArray.count > 0 { 242 | _textFieldView = createView(.clear, 0) 243 | if let textFieldView = _textFieldView { 244 | scrollView.addSubview(textFieldView) 245 | } 246 | } 247 | // If image not nil, create imageView and add to scrollView 248 | if let _image = image { 249 | _imageView = UIImageView(image: _image) 250 | guard let imageView = _imageView else { return } 251 | imageView.translatesAutoresizingMaskIntoConstraints = false 252 | scrollView.addSubview(imageView) 253 | } 254 | // If image not nil and textFieldView not nil add constraints 255 | if let _ = image, let _ = _textFieldView { 256 | scrollView.addConstraints(createConstraintsForScrollView(.imageANDtextField)) 257 | return 258 | } 259 | // If image not nil and textFieldView nil add constraints 260 | if let _ = image, _textFieldView == nil { 261 | scrollView.addConstraints(createConstraintsForScrollView(.image)) 262 | return 263 | } 264 | // If image nil and textFieldView not nil add constraints 265 | if let _ = _textFieldView, image == nil { 266 | scrollView.addConstraints(createConstraintsForScrollView(.textField)) 267 | return 268 | } 269 | // If image nil and textFieldView nil add constraints 270 | if image == nil && _textFieldView == nil { 271 | addConstraintsTo(infoView, scrollView, insets: infoContainerInsets) 272 | scrollView.centerXAnchor.constraint(equalTo: infoView.centerXAnchor).isActive = true 273 | } 274 | } 275 | // MARK: - Create Labels 276 | /// Create label 277 | /// 278 | /// - Parameters: 279 | /// - text: String 280 | /// - textColor: UIColor 281 | /// - font: UIFont 282 | /// - Returns: UILabel 283 | private func createLabel(_ text: String, _ textColor: UIColor,_ font: UIFont ) -> UILabel { 284 | let label = UILabel() 285 | label.text = text 286 | label.textColor = textColor 287 | label.font = font 288 | label.numberOfLines = 0 289 | label.translatesAutoresizingMaskIntoConstraints = false 290 | return label 291 | } 292 | /// Create text content 293 | private func createTextContent() { 294 | var array: Array = [] 295 | // Add headerView 296 | if let headerView = _headerView { 297 | addHeaderView() 298 | array.append(headerView) 299 | } 300 | // Add title label 301 | if let _title = title { 302 | let label = createLabel(_title, style.titleColor, style.titleFont) 303 | guard let infoView = _infoView else { return } 304 | infoView.addSubview(label) 305 | array.append(label) 306 | } 307 | // Add textFeildView 308 | if let textFieldView = _textFieldView, _textFieldArray.count > 0 { 309 | for (index, view) in _textFieldArray.enumerated() { 310 | textFieldView.addSubview(view) 311 | var constrantsArray:Array = [] 312 | //right and left 313 | constrantsArray.append(textFieldView.trailingAnchor.constraint(equalTo: view.trailingAnchor)) 314 | constrantsArray.append(view.leadingAnchor.constraint(equalTo: textFieldView.leadingAnchor)) 315 | constrantsArray.append(view.heightAnchor.constraint(equalToConstant: view.bounds.height)) 316 | // Top and bottom constraints 317 | if _textFieldArray.count == 1 { 318 | constrantsArray.append(textFieldView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 319 | } 320 | if index == 0 { 321 | constrantsArray.append(view.topAnchor.constraint(equalTo: textFieldView.topAnchor)) 322 | } else { 323 | //Last item 324 | if index == _textFieldArray.count - 1 { 325 | constrantsArray.append(textFieldView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 326 | } 327 | let prevLabel = _textFieldArray[index - 1] 328 | constrantsArray.append(view.topAnchor.constraint(equalTo: prevLabel.bottomAnchor, constant: 2)) 329 | let firstItem = _textFieldArray[0] 330 | constrantsArray.append(view.widthAnchor.constraint(equalTo: firstItem.widthAnchor)) 331 | } 332 | textFieldView.addConstraints(constrantsArray) 333 | } 334 | } 335 | // Message Label 336 | if let _message = _message, let infoView = _infoView { 337 | let label = createLabel(_message, style.messageColor, style.messageFont) 338 | infoView.addSubview(label) 339 | array.append(label) 340 | } 341 | guard let infoView = _infoView, array.count > 0 else { return } 342 | addIndividualItemConstraints(array, mainView: infoView, insideInset: 2) 343 | _heightScrollConstraint?.constant = calculateScrollViewHeight() 344 | } 345 | /// Add header view 346 | private func addHeaderView() { 347 | guard let infoView = _infoView else { return } 348 | if let _height = _headerView?.frame.height { 349 | _headerView?.heightAnchor.constraint(equalToConstant: _height).isActive = true 350 | } 351 | _headerView?.translatesAutoresizingMaskIntoConstraints = false 352 | infoView.addSubview(_headerView!) 353 | } 354 | /// Calculate scrollView height 355 | /// 356 | /// - Returns: CGFloat 357 | private func calculateScrollViewHeight() -> CGFloat { 358 | guard let alertView = _alertView, let infoView = _infoView else { return 0 } 359 | alertView.layoutIfNeeded() 360 | var scrollHeight = infoView.bounds.height + infoView.frame.origin.y 361 | if _textFieldArray.count > 0 { 362 | guard let textFieldView = _textFieldView else { return 0 } 363 | scrollHeight += textFieldView.bounds.height + 20 364 | } 365 | var safeAreaInsets: UIEdgeInsets = UIEdgeInsets.zero 366 | if #available(iOS 11.0, *) { 367 | let window = UIApplication.shared.keyWindow 368 | safeAreaInsets = window?.safeAreaInsets ?? UIEdgeInsets.zero 369 | } 370 | let notScrollHeight = heightButton + buttonContainerInsets.bottom + buttonContainerInsets.top + insets.top + insets.bottom 371 | let maxHeight = UIScreen.main.bounds.height - (notScrollHeight + safeAreaInsets.bottom + safeAreaInsets.top) 372 | return scrollHeight > maxHeight ? maxHeight : scrollHeight 373 | } 374 | // MARK: - Add constraints 375 | /// Create constraints for scrollView with state 376 | /// 377 | /// - Parameter state: ContentState 378 | /// - Returns: Array 379 | private func createConstraintsForScrollView(_ state: ContentState) -> [NSLayoutConstraint] { 380 | guard let scrollView = _scrollView, let infoView = _infoView else { return [] } 381 | switch state { 382 | case .image: 383 | guard let imageView = _imageView else { return [] } 384 | return [imageView.heightAnchor.constraint(equalToConstant: imageSize.height), 385 | imageView.widthAnchor.constraint(equalToConstant: imageSize.width), 386 | scrollView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: infoContainerInsets.right), 387 | imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: infoContainerInsets.top), 388 | imageView.leadingAnchor.constraint(equalTo: infoView.trailingAnchor, constant: imageInsets.left), 389 | infoView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: infoContainerInsets.left), 390 | infoView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: infoContainerInsets.top), 391 | scrollView.bottomAnchor.constraint(equalTo: infoView.bottomAnchor), 392 | scrollView.bottomAnchor.constraint(greaterThanOrEqualTo: imageView.bottomAnchor), 393 | scrollView.centerXAnchor.constraint(equalTo: infoView.centerXAnchor, constant: (imageSize.width + imageInsets.left) / 2)] 394 | case .textField: 395 | guard let textFieldView = _textFieldView else { return [] } 396 | return [infoView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: infoContainerInsets.top), 397 | infoView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: infoContainerInsets.left), 398 | scrollView.trailingAnchor.constraint(equalTo: infoView.trailingAnchor, constant: infoContainerInsets.right), 399 | scrollView.trailingAnchor.constraint(equalTo: textFieldView.trailingAnchor, constant: textFieldContainerInsets.right), 400 | textFieldView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: textFieldContainerInsets.left), 401 | textFieldView.topAnchor.constraint(equalTo: infoView.bottomAnchor, constant: textFieldContainerInsets.top), 402 | scrollView.bottomAnchor.constraint(equalTo: textFieldView.bottomAnchor), 403 | scrollView.centerXAnchor.constraint(equalTo: textFieldView.centerXAnchor)] 404 | case .imageANDtextField: 405 | guard let textFieldView = _textFieldView, let imageView = _imageView else { return [] } 406 | return [ 407 | imageView.heightAnchor.constraint(equalToConstant: imageSize.height), 408 | imageView.widthAnchor.constraint(equalToConstant: imageSize.width), 409 | scrollView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: imageInsets.right), 410 | imageView.topAnchor.constraint(equalTo: infoView.topAnchor), 411 | imageView.leadingAnchor.constraint(equalTo: infoView.trailingAnchor, constant: imageInsets.left), 412 | textFieldView.topAnchor.constraint(greaterThanOrEqualTo: imageView.bottomAnchor, constant:textFieldContainerInsets.top), 413 | infoView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: infoContainerInsets.top), 414 | infoView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: infoContainerInsets.left), 415 | imageView.leadingAnchor.constraint(equalTo: infoView.trailingAnchor, constant: imageInsets.left), 416 | textFieldView.topAnchor.constraint(equalTo: infoView.bottomAnchor, constant: textFieldContainerInsets.top), 417 | scrollView.trailingAnchor.constraint(equalTo: textFieldView.trailingAnchor, constant: textFieldContainerInsets.right), 418 | textFieldView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: textFieldContainerInsets.left), 419 | scrollView.bottomAnchor.constraint(equalTo: textFieldView.bottomAnchor), 420 | scrollView.centerXAnchor.constraint(equalTo: infoView.centerXAnchor, constant: (imageSize.width + imageInsets.left) / 2)] 421 | } 422 | } 423 | /// Add constrans with insets 424 | /// 425 | /// - Parameters: 426 | /// - view: UIView 427 | /// - mainView: UIView 428 | private func addConstraintsTo(_ view: UIView,_ mainView: UIView, insets: UIEdgeInsets) { 429 | mainView.addConstraints([ 430 | mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant:insets.right), 431 | view.topAnchor.constraint(equalTo: mainView.topAnchor, constant:insets.top), 432 | view.leadingAnchor.constraint(equalTo: mainView.leadingAnchor, constant:insets.left), 433 | view.bottomAnchor.constraint(equalTo: mainView.bottomAnchor, constant:insets.bottom) 434 | ]) 435 | } 436 | /// Add individual item constraints 437 | /// 438 | /// - Parameters: 439 | /// - items: Array 440 | /// - mainView: UIView 441 | /// - insideInset: CGFloat 442 | private func addIndividualItemConstraints(_ items: [UIView], mainView: UIView, insideInset: CGFloat) { 443 | for (index, view) in items.enumerated() { 444 | var constrantsArray:Array = [] 445 | //right and left 446 | constrantsArray.append(mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor)) 447 | constrantsArray.append(view.leadingAnchor.constraint(equalTo: mainView.leadingAnchor)) 448 | // Top and bottom constraints 449 | if items.count == 1 { 450 | constrantsArray.append(mainView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 451 | } 452 | if index == 0 { 453 | constrantsArray.append(view.topAnchor.constraint(equalTo: mainView.topAnchor)) 454 | } else { 455 | if index == items.count - 1 { 456 | constrantsArray.append(mainView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 457 | } 458 | let prevLabel = items[index - 1] 459 | constrantsArray.append(view.topAnchor.constraint(equalTo: prevLabel.bottomAnchor, constant: insideInset)) 460 | let firstItem = items[0] 461 | constrantsArray.append(view.widthAnchor.constraint(equalTo: firstItem.widthAnchor)) 462 | } 463 | mainView.addConstraints(constrantsArray) 464 | } 465 | } 466 | /// Add constraints to items and root view(For Buttons) 467 | /// 468 | /// - Parameters: 469 | /// - buttons: Array 470 | /// - mainView: UIView 471 | /// - height: CGFloat 472 | private func addButtonConstraints(_ buttons: [UIButton], mainView: UIView, height: CGFloat) { 473 | if buttons.count > 2 { 474 | addVerticalConstraints(buttons, mainView: mainView, height: height) 475 | } else { 476 | addHorizontalConstraints(buttons, mainView: mainView, height: height) 477 | } 478 | } 479 | /// Add horizontal constraints to buttons 480 | /// 481 | /// - Parameters: 482 | /// - buttons: Array 483 | /// - mainView: UIView 484 | /// - height: CGFloat 485 | private func addHorizontalConstraints(_ buttons: [UIButton], mainView: UIView, height: CGFloat) { 486 | for (index, button) in buttons.enumerated() { 487 | var constrantsArray:Array = [] 488 | //Top and bottom constraints 489 | constrantsArray.append(button.topAnchor.constraint(equalTo: mainView.topAnchor)) 490 | constrantsArray.append(button.bottomAnchor.constraint(equalTo: mainView.bottomAnchor)) 491 | constrantsArray.append(button.heightAnchor.constraint(equalToConstant: height)) 492 | //right and left 493 | if buttons.count == 1 { 494 | constrantsArray.append(mainView.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: 0)) 495 | } 496 | if index == 0 { 497 | constrantsArray.append(button.leadingAnchor.constraint(equalTo:mainView.leadingAnchor, constant: 0)) 498 | } else { 499 | //Last button 500 | if index == buttons.count - 1 { 501 | constrantsArray.append(mainView.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: 0)) 502 | } 503 | let prevButton = buttons[index - 1] 504 | constrantsArray.append(button.leadingAnchor.constraint(equalTo: prevButton.trailingAnchor, constant: 11)) 505 | let firstItem = buttons[0] 506 | constrantsArray.append(button.widthAnchor.constraint(equalTo: firstItem.widthAnchor)) 507 | } 508 | mainView.addConstraints(constrantsArray) 509 | } 510 | } 511 | /// Add vertical constraints to buttons 512 | /// 513 | /// - Parameters: 514 | /// - buttons: Array 515 | /// - mainView: UIView 516 | /// - height: CGFloat 517 | private func addVerticalConstraints(_ buttons: [UIButton], mainView: UIView, height: CGFloat) { 518 | for (index, button) in buttons.enumerated() { 519 | var constrantsArray:Array = [] 520 | //right and left 521 | constrantsArray.append(mainView.trailingAnchor.constraint(equalTo: button.trailingAnchor)) 522 | constrantsArray.append(button.leadingAnchor.constraint(equalTo: mainView.leadingAnchor)) 523 | constrantsArray.append(button.heightAnchor.constraint(equalToConstant: height)) 524 | // Top and bottom constraints 525 | if buttons.count == 1 { 526 | constrantsArray.append(mainView.bottomAnchor.constraint(equalTo: button.bottomAnchor)) 527 | } 528 | if index == 0 { 529 | constrantsArray.append(button.topAnchor.constraint(equalTo: mainView.topAnchor)) 530 | } else { 531 | //Last Label 532 | if index == buttons.count - 1 { 533 | constrantsArray.append(mainView.bottomAnchor.constraint(greaterThanOrEqualTo: button.bottomAnchor)) 534 | } 535 | let prevButton = buttons[index - 1] 536 | constrantsArray.append(button.topAnchor.constraint(equalTo: prevButton.bottomAnchor, constant: 11)) 537 | let firstButton = buttons[0] 538 | constrantsArray.append(button.widthAnchor.constraint(equalTo: firstButton.widthAnchor)) 539 | } 540 | mainView.addConstraints(constrantsArray) 541 | } 542 | } 543 | // MARK: - Helper methods 544 | /// Hidden keyboard 545 | /// 546 | /// - Parameters: 547 | /// - touches: Set 548 | /// - event: UIEvent 549 | override open func touchesBegan(_ touches: Set, with event: UIEvent?) { 550 | super.touchesBegan(touches, with: event) 551 | self.view.endEditing(true) 552 | } 553 | // MARK: - Public methods 554 | /// Add HeaderView 555 | /// 556 | /// - Parameter view: UIView 557 | public func addHeaderView(_ view: UIView) { 558 | self._headerView = view 559 | } 560 | /// Add KOTextField 561 | /// 562 | /// - Parameter textField: KOTextField 563 | public func addTextField(_ textField: @escaping (UITextField) -> Void) { 564 | _opsArray.append(BlockOperation(block: { 565 | if let tempTextField = self.alertTextField as? UITextField { 566 | let textF = tempTextField.createCopy() 567 | textF.translatesAutoresizingMaskIntoConstraints = false 568 | textF.delegate = self 569 | self._textFieldArray.append(textF) 570 | textField(textF) 571 | } else { 572 | let textF = UITextField(frame: CGRect(x: 0, y: 0, width: 0, height: 50)) 573 | textF.translatesAutoresizingMaskIntoConstraints = false 574 | textF.delegate = self 575 | self._textFieldArray.append(textF) 576 | textField(textF) 577 | } 578 | })) 579 | } 580 | /// Add action 581 | /// 582 | /// - Parameters: 583 | /// - action: KOAlertButton 584 | /// - handler: Void 585 | public func addAction(_ action: KOAlertButton, handler: @escaping () -> Void) { 586 | _opsArray.append(BlockOperation(block: { 587 | guard let buttonView = self._buttonView else { return } 588 | let button = UIButton(frame: CGRect.zero) 589 | button.setTitle(action.title, for: .normal) 590 | button.setTitleColor(action.titleColor, for: .normal) 591 | button.setTitleColor(action.titleColor.withAlphaComponent(0.25), for: .highlighted) 592 | button.titleLabel?.font = action.font 593 | button.backgroundColor = action.backgroundColor 594 | button.layer.borderColor = action.borderColor.cgColor 595 | button.layer.borderWidth = action.borderWidth 596 | button.layer.cornerRadius = action.cornerRadius 597 | button.addTarget(self, action: #selector(self.action(_:)), for: .touchUpInside) 598 | button.translatesAutoresizingMaskIntoConstraints = false 599 | buttonView.addSubview(button) 600 | self._buttonArray.append(button) 601 | })) 602 | _actionArray.append(handler) 603 | } 604 | // MARK: - Private Methods 605 | /// Dismis self with index 606 | /// 607 | /// - Parameter index: Int 608 | private func dismis(_ index: Int) { 609 | UIView.animate(withDuration: 0.35, animations: { 610 | self.view.backgroundColor = UIColor.black.withAlphaComponent(0.0) 611 | self._positionConstrant?.constant = -UIScreen.main.bounds.height 612 | self.view.layoutIfNeeded() 613 | }) { (comp) in 614 | DispatchQueue.main.async { 615 | self.dismiss(animated: false) { 616 | self._actionArray[index]?() 617 | } 618 | } 619 | } 620 | } 621 | /// Handler action 622 | @objc private func action(_ sender: UIButton) { 623 | view.endEditing(true) 624 | for (index, button) in _buttonArray.enumerated() { 625 | if button == sender { 626 | dismis(index) 627 | } 628 | } 629 | } 630 | // MARK: - Notifications 631 | // Call this method somewhere in your view controller setup code. 632 | @objc private func registerForKeyboardNotifications() { 633 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown), name: UIResponder.keyboardWillShowNotification, object: nil) 634 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillBeHidden), name: UIResponder.keyboardWillHideNotification, object: nil) 635 | } 636 | // Called when the UIKeyboardDidShowNotification is sent. 637 | @objc private func keyboardWasShown(_ notification: Notification) { 638 | guard var info = notification.userInfo, let alertView = _alertView else { return } 639 | guard let _frame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } 640 | let kbSize = _frame.size 641 | UIView.animate(withDuration: info[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval) { 642 | var bottomInset = kbSize.height + self.insets.bottom 643 | switch self.style.position { 644 | case .center: 645 | let bY = alertView.frame.height + alertView.frame.origin.y 646 | let screenHeight = UIScreen.main.bounds.height 647 | if (screenHeight - bottomInset) > bY{ 648 | bottomInset = 0 649 | }else{ 650 | bottomInset = bY - (screenHeight - bottomInset) 651 | } 652 | self._positionConstrant?.constant = bottomInset 653 | default: 654 | self._positionConstrant?.constant = bottomInset 655 | } 656 | self.view.layoutIfNeeded() 657 | } 658 | } 659 | // Called when the UIKeyboardWillHideNotification is sent 660 | @objc private func keyboardWillBeHidden(_ notification: Notification) { 661 | var info = notification.userInfo! 662 | UIView.animate(withDuration: info[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval) { 663 | self._positionConstrant?.constant = self.insets.bottom 664 | self.view.layoutIfNeeded() 665 | } 666 | } 667 | } 668 | // MARK: - UITextFieldDelegate 669 | extension KOAlertController: UITextFieldDelegate { 670 | public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{ 671 | let oldLength = textField.text!.count 672 | let replacementLength = string.count 673 | let rangeLength = range.length 674 | let newLength = oldLength - rangeLength + replacementLength 675 | return newLength <= maxLengthTextField 676 | } 677 | } 678 | extension UITextField { 679 | func createCopy() -> UITextField { 680 | let archivedData = NSKeyedArchiver.archivedData(withRootObject: self) 681 | return NSKeyedUnarchiver.unarchiveObject(with: archivedData) as! UITextField 682 | } 683 | } 684 | -------------------------------------------------------------------------------- /Source/KOAlertStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KOAlertStyle.swift 3 | // 4 | // Created by Oleksandr Khymych on 13.12.2017. 5 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// Aler style object 12 | open class KOAlertStyle{ 13 | public enum PositionType { 14 | case center 15 | case `default` 16 | } 17 | public var position : PositionType = .default 18 | public var backgroundColor : UIColor! 19 | public var titleColor : UIColor! 20 | public var titleFont : UIFont! 21 | public var messageColor : UIColor! 22 | public var messageFont : UIFont! 23 | public var cornerRadius : CGFloat! 24 | public init() { 25 | self.backgroundColor = UIColor.white 26 | self.titleColor = UIColor.black 27 | self.titleFont = UIFont.boldSystemFont(ofSize: 27) 28 | self.messageFont = UIFont.systemFont(ofSize: 17) 29 | self.messageColor = UIColor.gray 30 | self.cornerRadius = 2 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/KOTypeButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KOTypeButton.swift 3 | // 4 | // Created by Oleksandr Khymych on 26.12.2017. 5 | // Copyright © 2017 Oleksandr Khymych. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | /// KOTypeButton 11 | /// 12 | /// - cancel: button cancel type 13 | /// - `default`: button default type 14 | public enum KOTypeButton { 15 | case cancel 16 | case `default` 17 | } 18 | --------------------------------------------------------------------------------