├── .gitignore ├── LICENSE ├── README.md ├── WebKitDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── WebKitDemo ├── AppDelegate.swift ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── SceneDelegate.swift ├── WebViewController.swift └── WebViewProgressView.swift /.gitignore: -------------------------------------------------------------------------------- 1 | tignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 2 | 3 | ## User settings 4 | xcuserdata/ 5 | 6 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 7 | *.xcscmblueprint 8 | *.xccheckout 9 | 10 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 11 | build/ 12 | DerivedData/ 13 | *.moved-aside 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | 23 | ## Obj-C/Swift specific 24 | *.hmap 25 | 26 | ## App packaging 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | # *.xcodeproj 42 | # 43 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 44 | # hence it is not needed unless you have added a package configuration file to your project 45 | # .swiftpm 46 | 47 | .build/ 48 | 49 | # CocoaPods 50 | # 51 | # We recommend against adding the Pods directory to your .gitignore. However 52 | # you should judge for yourself, the pros and cons are mentioned at: 53 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 54 | # 55 | # Pods/ 56 | # 57 | # Add this line if you want to avoid checking in source code from the Xcode workspace 58 | # *.xcworkspace 59 | 60 | # Carthage 61 | # 62 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 63 | # Carthage/Checkouts 64 | 65 | Carthage/Build/ 66 | 67 | # Accio dependency management 68 | Dependencies/ 69 | .accio/ 70 | 71 | # fastlane 72 | # 73 | # It is recommended to not store the screenshots in the git repo. 74 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 75 | # For more information about the recommended setup visit: 76 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 77 | 78 | fastlane/report.xml 79 | fastlane/Preview.html 80 | fastlane/screenshots/**/*.png 81 | fastlane/test_output 82 | 83 | # Code Injection 84 | # 85 | # After new code Injection tools there's a generated folder /iOSInjectionProject 86 | # https://github.com/johnno1962/injectionforxcode 87 | 88 | iOSInjectionProject/ 89 | .DS_Store 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kyusaku Mihara 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebKitDemo 2 | ========== 3 | 4 | WebKit(WKWebView) Demo for iOS 5 | 6 | 7 | WebViewProgressView.swift 8 | ------------------------- 9 | 10 | porting [NJKWebViewProgress](https://github.com/ninjinkun/NJKWebViewProgress) to Swift. 11 | -------------------------------------------------------------------------------- /WebKitDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 10CCBCCC2855E1D900CFDB30 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCBCCB2855E1D900CFDB30 /* AppDelegate.swift */; }; 11 | 10CCBCCE2855E1D900CFDB30 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCBCCD2855E1D900CFDB30 /* SceneDelegate.swift */; }; 12 | 10CCBCD32855E1D900CFDB30 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10CCBCD12855E1D900CFDB30 /* Main.storyboard */; }; 13 | 10CCBCD52855E1DA00CFDB30 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 10CCBCD42855E1DA00CFDB30 /* Assets.xcassets */; }; 14 | 10CCBCD82855E1DA00CFDB30 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10CCBCD62855E1DA00CFDB30 /* LaunchScreen.storyboard */; }; 15 | 10CCBCE12855E27200CFDB30 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCBCDF2855E27200CFDB30 /* WebViewController.swift */; }; 16 | 10CCBCE22855E27200CFDB30 /* WebViewProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CCBCE02855E27200CFDB30 /* WebViewProgressView.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 10CCBCC82855E1D900CFDB30 /* WebKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 10CCBCCB2855E1D900CFDB30 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 10CCBCCD2855E1D900CFDB30 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 23 | 10CCBCD22855E1D900CFDB30 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 10CCBCD42855E1DA00CFDB30 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 10CCBCD72855E1DA00CFDB30 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 10CCBCD92855E1DA00CFDB30 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 10CCBCDF2855E27200CFDB30 /* WebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = ""; }; 28 | 10CCBCE02855E27200CFDB30 /* WebViewProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewProgressView.swift; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 10CCBCC52855E1D900CFDB30 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 10CCBCBF2855E1D900CFDB30 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 10CCBCCA2855E1D900CFDB30 /* WebKitDemo */, 46 | 10CCBCC92855E1D900CFDB30 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 10CCBCC92855E1D900CFDB30 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 10CCBCC82855E1D900CFDB30 /* WebKitDemo.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 10CCBCCA2855E1D900CFDB30 /* WebKitDemo */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 10CCBCCB2855E1D900CFDB30 /* AppDelegate.swift */, 62 | 10CCBCCD2855E1D900CFDB30 /* SceneDelegate.swift */, 63 | 10CCBCDF2855E27200CFDB30 /* WebViewController.swift */, 64 | 10CCBCE02855E27200CFDB30 /* WebViewProgressView.swift */, 65 | 10CCBCD12855E1D900CFDB30 /* Main.storyboard */, 66 | 10CCBCD42855E1DA00CFDB30 /* Assets.xcassets */, 67 | 10CCBCD62855E1DA00CFDB30 /* LaunchScreen.storyboard */, 68 | 10CCBCD92855E1DA00CFDB30 /* Info.plist */, 69 | ); 70 | path = WebKitDemo; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 10CCBCC72855E1D900CFDB30 /* WebKitDemo */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 10CCBCDC2855E1DA00CFDB30 /* Build configuration list for PBXNativeTarget "WebKitDemo" */; 79 | buildPhases = ( 80 | 10CCBCC42855E1D900CFDB30 /* Sources */, 81 | 10CCBCC52855E1D900CFDB30 /* Frameworks */, 82 | 10CCBCC62855E1D900CFDB30 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = WebKitDemo; 89 | productName = WebKitDemo; 90 | productReference = 10CCBCC82855E1D900CFDB30 /* WebKitDemo.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 10CCBCC02855E1D900CFDB30 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | BuildIndependentTargetsInParallel = 1; 100 | LastSwiftUpdateCheck = 1400; 101 | LastUpgradeCheck = 1400; 102 | TargetAttributes = { 103 | 10CCBCC72855E1D900CFDB30 = { 104 | CreatedOnToolsVersion = 14.0; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 10CCBCC32855E1D900CFDB30 /* Build configuration list for PBXProject "WebKitDemo" */; 109 | compatibilityVersion = "Xcode 14.0"; 110 | developmentRegion = en; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | Base, 115 | ); 116 | mainGroup = 10CCBCBF2855E1D900CFDB30; 117 | productRefGroup = 10CCBCC92855E1D900CFDB30 /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | 10CCBCC72855E1D900CFDB30 /* WebKitDemo */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXResourcesBuildPhase section */ 127 | 10CCBCC62855E1D900CFDB30 /* Resources */ = { 128 | isa = PBXResourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | 10CCBCD82855E1DA00CFDB30 /* LaunchScreen.storyboard in Resources */, 132 | 10CCBCD52855E1DA00CFDB30 /* Assets.xcassets in Resources */, 133 | 10CCBCD32855E1D900CFDB30 /* Main.storyboard in Resources */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | 10CCBCC42855E1D900CFDB30 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | 10CCBCE22855E27200CFDB30 /* WebViewProgressView.swift in Sources */, 145 | 10CCBCCC2855E1D900CFDB30 /* AppDelegate.swift in Sources */, 146 | 10CCBCCE2855E1D900CFDB30 /* SceneDelegate.swift in Sources */, 147 | 10CCBCE12855E27200CFDB30 /* WebViewController.swift in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin PBXVariantGroup section */ 154 | 10CCBCD12855E1D900CFDB30 /* Main.storyboard */ = { 155 | isa = PBXVariantGroup; 156 | children = ( 157 | 10CCBCD22855E1D900CFDB30 /* Base */, 158 | ); 159 | name = Main.storyboard; 160 | sourceTree = ""; 161 | }; 162 | 10CCBCD62855E1DA00CFDB30 /* LaunchScreen.storyboard */ = { 163 | isa = PBXVariantGroup; 164 | children = ( 165 | 10CCBCD72855E1DA00CFDB30 /* Base */, 166 | ); 167 | name = LaunchScreen.storyboard; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXVariantGroup section */ 171 | 172 | /* Begin XCBuildConfiguration section */ 173 | 10CCBCDA2855E1DA00CFDB30 /* Debug */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_ANALYZER_NONNULL = YES; 178 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 179 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 180 | CLANG_ENABLE_MODULES = YES; 181 | CLANG_ENABLE_OBJC_ARC = YES; 182 | CLANG_ENABLE_OBJC_WEAK = YES; 183 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 184 | CLANG_WARN_BOOL_CONVERSION = YES; 185 | CLANG_WARN_COMMA = YES; 186 | CLANG_WARN_CONSTANT_CONVERSION = YES; 187 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 189 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 190 | CLANG_WARN_EMPTY_BODY = YES; 191 | CLANG_WARN_ENUM_CONVERSION = YES; 192 | CLANG_WARN_INFINITE_RECURSION = YES; 193 | CLANG_WARN_INT_CONVERSION = YES; 194 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 195 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 199 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 200 | CLANG_WARN_STRICT_PROTOTYPES = YES; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | COPY_PHASE_STRIP = NO; 206 | DEBUG_INFORMATION_FORMAT = dwarf; 207 | ENABLE_STRICT_OBJC_MSGSEND = YES; 208 | ENABLE_TESTABILITY = YES; 209 | GCC_C_LANGUAGE_STANDARD = gnu11; 210 | GCC_DYNAMIC_NO_PIC = NO; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_OPTIMIZATION_LEVEL = 0; 213 | GCC_PREPROCESSOR_DEFINITIONS = ( 214 | "DEBUG=1", 215 | "$(inherited)", 216 | ); 217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_UNDECLARED_SELECTOR = YES; 220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 221 | GCC_WARN_UNUSED_FUNCTION = YES; 222 | GCC_WARN_UNUSED_VARIABLE = YES; 223 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 224 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 225 | MTL_FAST_MATH = YES; 226 | ONLY_ACTIVE_ARCH = YES; 227 | SDKROOT = iphoneos; 228 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 229 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 230 | }; 231 | name = Debug; 232 | }; 233 | 10CCBCDB2855E1DA00CFDB30 /* Release */ = { 234 | isa = XCBuildConfiguration; 235 | buildSettings = { 236 | ALWAYS_SEARCH_USER_PATHS = NO; 237 | CLANG_ANALYZER_NONNULL = YES; 238 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 240 | CLANG_ENABLE_MODULES = YES; 241 | CLANG_ENABLE_OBJC_ARC = YES; 242 | CLANG_ENABLE_OBJC_WEAK = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 250 | CLANG_WARN_EMPTY_BODY = YES; 251 | CLANG_WARN_ENUM_CONVERSION = YES; 252 | CLANG_WARN_INFINITE_RECURSION = YES; 253 | CLANG_WARN_INT_CONVERSION = YES; 254 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 256 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 260 | CLANG_WARN_STRICT_PROTOTYPES = YES; 261 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | COPY_PHASE_STRIP = NO; 266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 267 | ENABLE_NS_ASSERTIONS = NO; 268 | ENABLE_STRICT_OBJC_MSGSEND = YES; 269 | GCC_C_LANGUAGE_STANDARD = gnu11; 270 | GCC_NO_COMMON_BLOCKS = YES; 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 278 | MTL_ENABLE_DEBUG_INFO = NO; 279 | MTL_FAST_MATH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_COMPILATION_MODE = wholemodule; 282 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 283 | VALIDATE_PRODUCT = YES; 284 | }; 285 | name = Release; 286 | }; 287 | 10CCBCDD2855E1DA00CFDB30 /* Debug */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 292 | CODE_SIGN_STYLE = Automatic; 293 | CURRENT_PROJECT_VERSION = 1; 294 | GENERATE_INFOPLIST_FILE = YES; 295 | INFOPLIST_FILE = WebKitDemo/Info.plist; 296 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 297 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 298 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 299 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 300 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 301 | LD_RUNPATH_SEARCH_PATHS = ( 302 | "$(inherited)", 303 | "@executable_path/Frameworks", 304 | ); 305 | MARKETING_VERSION = 1.0; 306 | PRODUCT_BUNDLE_IDENTIFIER = com.epohsoft.WebKitDemo; 307 | PRODUCT_NAME = "$(TARGET_NAME)"; 308 | SWIFT_EMIT_LOC_STRINGS = YES; 309 | SWIFT_VERSION = 5.0; 310 | TARGETED_DEVICE_FAMILY = "1,2"; 311 | }; 312 | name = Debug; 313 | }; 314 | 10CCBCDE2855E1DA00CFDB30 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 318 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 319 | CODE_SIGN_STYLE = Automatic; 320 | CURRENT_PROJECT_VERSION = 1; 321 | GENERATE_INFOPLIST_FILE = YES; 322 | INFOPLIST_FILE = WebKitDemo/Info.plist; 323 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 324 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 325 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 326 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 327 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 328 | LD_RUNPATH_SEARCH_PATHS = ( 329 | "$(inherited)", 330 | "@executable_path/Frameworks", 331 | ); 332 | MARKETING_VERSION = 1.0; 333 | PRODUCT_BUNDLE_IDENTIFIER = com.epohsoft.WebKitDemo; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | SWIFT_EMIT_LOC_STRINGS = YES; 336 | SWIFT_VERSION = 5.0; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Release; 340 | }; 341 | /* End XCBuildConfiguration section */ 342 | 343 | /* Begin XCConfigurationList section */ 344 | 10CCBCC32855E1D900CFDB30 /* Build configuration list for PBXProject "WebKitDemo" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | 10CCBCDA2855E1DA00CFDB30 /* Debug */, 348 | 10CCBCDB2855E1DA00CFDB30 /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | 10CCBCDC2855E1DA00CFDB30 /* Build configuration list for PBXNativeTarget "WebKitDemo" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | 10CCBCDD2855E1DA00CFDB30 /* Debug */, 357 | 10CCBCDE2855E1DA00CFDB30 /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | /* End XCConfigurationList section */ 363 | }; 364 | rootObject = 10CCBCC02855E1D900CFDB30 /* Project object */; 365 | } 366 | -------------------------------------------------------------------------------- /WebKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WebKitDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WebKitDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WebKitDemo 4 | // 5 | // Created by Kyusaku Mihara on 2022/06/12. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /WebKitDemo/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 | -------------------------------------------------------------------------------- /WebKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WebKitDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WebKitDemo/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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /WebKitDemo/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 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /WebKitDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /WebKitDemo/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // WebKitDemo 4 | // 5 | // Created by Kyusaku Mihara on 2022/06/12. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /WebKitDemo/WebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewController.swift 3 | // WebKitDemo 4 | // 5 | // Created by Kyusaku Mihara on 9/17/14. 6 | // Copyright (c) 2014 epohsoft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | class WebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate { 13 | weak var webView: WKWebView! 14 | weak var progressView: WebViewProgressView! 15 | @IBOutlet weak var backBarButton: UIBarButtonItem! 16 | @IBOutlet weak var forwardBarButton: UIBarButtonItem! 17 | @IBOutlet weak var stopBarButton: UIBarButtonItem! 18 | @IBOutlet weak var refreshBarButton: UIBarButtonItem! 19 | 20 | private var keyValueObservations: [NSKeyValueObservation] = [] 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | let webViewConfiguration = WKWebViewConfiguration() 26 | webViewConfiguration.preferences.isElementFullscreenEnabled = true 27 | 28 | let webView = WKWebView(frame: view.bounds, configuration: webViewConfiguration) 29 | webView.navigationDelegate = self 30 | webView.uiDelegate = self 31 | webView.isFindInteractionEnabled = true 32 | webView.allowsBackForwardNavigationGestures = false 33 | keyValueObservations.append(webView.observe(\.isLoading, options: [.new]) { [weak self] _, change in 34 | guard let isLoading = change.newValue else { return } 35 | self?.stopBarButton.isEnabled = isLoading 36 | self?.refreshBarButton.isEnabled = !isLoading 37 | }) 38 | keyValueObservations.append(webView.observe(\.title, options: [.new]) { [weak self] _, change in 39 | guard let title = change.newValue else { return } 40 | self?.navigationItem.title = title 41 | }) 42 | keyValueObservations.append(webView.observe(\.estimatedProgress, options: [.new]) { [weak self] _, change in 43 | guard let progress = change.newValue else { return } 44 | self?.progressView.setProgress(Float(progress), animated: true) 45 | }) 46 | keyValueObservations.append(webView.observe(\.canGoBack, options: [.new]) { [weak self] _, change in 47 | guard let canGoBack = change.newValue else { return } 48 | self?.backBarButton.isEnabled = canGoBack 49 | }) 50 | keyValueObservations.append(webView.observe(\.canGoForward, options: [.new]) { [weak self] _, change in 51 | guard let canGoForward = change.newValue else { return } 52 | self?.forwardBarButton.isEnabled = canGoForward 53 | }) 54 | keyValueObservations.append(webView.observe(\.fullscreenState, options: [.new]) { [weak self] object, _ in 55 | print("WebView fullscreen state did change:\(object.fullscreenState)") 56 | let isHidden: Bool 57 | switch object.fullscreenState { 58 | case .enteringFullscreen: 59 | isHidden = true 60 | case .exitingFullscreen: 61 | isHidden = false 62 | default: 63 | return 64 | } 65 | self?.navigationController?.setNavigationBarHidden(isHidden, animated: true) 66 | self?.navigationController?.setToolbarHidden(isHidden, animated: true) 67 | }) 68 | view.addSubview(webView) 69 | NSLayoutConstraint.activate([ 70 | webView.topAnchor.constraint(equalTo: view.topAnchor), 71 | webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 72 | webView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 73 | webView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 74 | ]) 75 | self.webView = webView 76 | 77 | if let navigationBar = navigationController?.navigationBar { 78 | let progressView = WebViewProgressView() 79 | progressView.translatesAutoresizingMaskIntoConstraints = false 80 | navigationBar.addSubview(progressView) 81 | NSLayoutConstraint.activate([ 82 | progressView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor), 83 | progressView.trailingAnchor.constraint(equalTo: navigationBar.trailingAnchor), 84 | progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor), 85 | progressView.heightAnchor.constraint(equalToConstant: 2) 86 | ]) 87 | self.progressView = progressView 88 | } 89 | 90 | if let state = UserDefaults.standard.value(forKey: "WebViewState") { 91 | webView.interactionState = state 92 | } else { 93 | webView.load(URLRequest(url: URL(string: "https://duckduckgo.com")!)) 94 | } 95 | 96 | backBarButton.isEnabled = webView.canGoBack 97 | forwardBarButton.isEnabled = webView.canGoForward 98 | } 99 | 100 | @IBAction func backBarButtonTapped(_ sender: AnyObject) { 101 | if webView.canGoBack { 102 | webView.goBack() 103 | } 104 | } 105 | 106 | @IBAction func forwardBarButtonTapped(_ sender: AnyObject) { 107 | if webView.canGoForward { 108 | webView.goForward() 109 | } 110 | } 111 | 112 | @IBAction func stopBarButtonTapped(_ sender: AnyObject) { 113 | if webView.isLoading { 114 | webView.stopLoading() 115 | } 116 | } 117 | 118 | @IBAction func refreshBarButtonTapped(_ sender: AnyObject) { 119 | _ = webView.reload() 120 | } 121 | 122 | @IBAction func searchBarButtonTapped(_ sender: Any) { 123 | webView.findInteraction?.presentFindNavigator(showingReplace: false) 124 | } 125 | 126 | @IBAction func saveStateBarButtonTapped(_ sender: Any) { 127 | UserDefaults.standard.setValue(webView.interactionState, forKey: "WebViewState") 128 | } 129 | 130 | // MARK: - WKNavigationDelegate methods 131 | 132 | func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation) { 133 | print("webView:\(webView) didStartProvisionalNavigation:\(navigation)") 134 | } 135 | 136 | func webView(_ webView: WKWebView, didCommit navigation: WKNavigation) { 137 | print("webView:\(webView) didCommitNavigation:\(navigation)") 138 | } 139 | 140 | func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (@escaping (WKNavigationActionPolicy) -> Void)) { 141 | print("webView:\(webView) decidePolicyForNavigationAction:\(navigationAction) decisionHandler:\(String(describing: decisionHandler))") 142 | 143 | switch navigationAction.navigationType { 144 | case .linkActivated: 145 | if navigationAction.targetFrame == nil { 146 | webView.load(navigationAction.request) 147 | } 148 | default: 149 | break 150 | } 151 | 152 | decisionHandler(.allow) 153 | } 154 | 155 | func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: (@escaping (WKNavigationResponsePolicy) -> Void)) { 156 | print("webView:\(webView) decidePolicyForNavigationResponse:\(navigationResponse) decisionHandler:\(String(describing: decisionHandler))") 157 | 158 | decisionHandler(.allow) 159 | } 160 | 161 | func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 162 | print("webView:\(webView) didReceiveAuthenticationChallenge:\(challenge) completionHandler:\(String(describing: completionHandler))") 163 | 164 | switch (challenge.protectionSpace.authenticationMethod) { 165 | case NSURLAuthenticationMethodHTTPBasic: 166 | let alertController = UIAlertController(title: "Authentication Required", message: webView.url?.host, preferredStyle: .alert) 167 | weak var usernameTextField: UITextField! 168 | alertController.addTextField { textField in 169 | textField.placeholder = "Username" 170 | usernameTextField = textField 171 | } 172 | weak var passwordTextField: UITextField! 173 | alertController.addTextField { textField in 174 | textField.placeholder = "Password" 175 | textField.isSecureTextEntry = true 176 | passwordTextField = textField 177 | } 178 | alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { action in 179 | completionHandler(.cancelAuthenticationChallenge, nil) 180 | })) 181 | alertController.addAction(UIAlertAction(title: "Log In", style: .default, handler: { action in 182 | guard let username = usernameTextField.text, let password = passwordTextField.text else { 183 | completionHandler(.rejectProtectionSpace, nil) 184 | return 185 | } 186 | let credential = URLCredential(user: username, password: password, persistence: URLCredential.Persistence.forSession) 187 | completionHandler(.useCredential, credential) 188 | })) 189 | present(alertController, animated: true, completion: nil) 190 | default: 191 | completionHandler(.rejectProtectionSpace, nil); 192 | } 193 | } 194 | 195 | func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation) { 196 | print("webView:\(webView) didReceiveServerRedirectForProvisionalNavigation:\(navigation)") 197 | } 198 | 199 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation) { 200 | print("webView:\(webView) didFinishNavigation:\(navigation)") 201 | } 202 | 203 | func webView(_ webView: WKWebView, didFail navigation: WKNavigation, withError error: Error) { 204 | print("webView:\(webView) didFailNavigation:\(navigation) withError:\(error)") 205 | } 206 | 207 | func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation, withError error: Error) { 208 | print("webView:\(webView) didFailProvisionalNavigation:\(navigation) withError:\(error)") 209 | } 210 | 211 | // MARK: WKUIDelegate methods 212 | 213 | func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (@escaping () -> Void)) { 214 | print("webView:\(webView) runJavaScriptAlertPanelWithMessage:\(message) initiatedByFrame:\(frame) completionHandler:\(String(describing: completionHandler))") 215 | 216 | let alertController = UIAlertController(title: frame.request.url?.host, message: message, preferredStyle: .alert) 217 | alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in 218 | completionHandler() 219 | })) 220 | present(alertController, animated: true, completion: nil) 221 | } 222 | 223 | func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (@escaping (Bool) -> Void)) { 224 | print("webView:\(webView) runJavaScriptConfirmPanelWithMessage:\(message) initiatedByFrame:\(frame) completionHandler:\(String(describing: completionHandler))") 225 | 226 | let alertController = UIAlertController(title: frame.request.url?.host, message: message, preferredStyle: .alert) 227 | alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { action in 228 | completionHandler(false) 229 | })) 230 | alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in 231 | completionHandler(true) 232 | })) 233 | present(alertController, animated: true, completion: nil) 234 | } 235 | 236 | func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { 237 | print("webView:\(webView) runJavaScriptTextInputPanelWithPrompt:\(prompt) defaultText:\(String(describing: defaultText)) initiatedByFrame:\(frame) completionHandler:\(String(describing: completionHandler))") 238 | 239 | let alertController = UIAlertController(title: frame.request.url?.host, message: prompt, preferredStyle: .alert) 240 | weak var alertTextField: UITextField! 241 | alertController.addTextField { textField in 242 | textField.text = defaultText 243 | alertTextField = textField 244 | } 245 | alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { action in 246 | completionHandler(nil) 247 | })) 248 | alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in 249 | completionHandler(alertTextField.text) 250 | })) 251 | present(alertController, animated: true, completion: nil) 252 | } 253 | 254 | } 255 | 256 | extension WKWebView.FullscreenState: CustomDebugStringConvertible { 257 | public var debugDescription: String { 258 | let text: String 259 | switch self { 260 | case .notInFullscreen: 261 | text = "notInFullscreen" 262 | case .enteringFullscreen: 263 | text = "enteringFullscreen" 264 | case .inFullscreen: 265 | text = "inFullscreen" 266 | case .exitingFullscreen: 267 | text = "exitingFullscreen" 268 | @unknown default: 269 | text = "unknown" 270 | } 271 | return "\(text)(\(rawValue))" 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /WebKitDemo/WebViewProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewProgressView.swift 3 | // WebKitDemo 4 | // 5 | // porting NJKWebViewProgress to Swift. 6 | // see also https://github.com/ninjinkun/NJKWebViewProgress 7 | // 8 | // Created by Kyusaku Mihara on 9/17/14. 9 | // Copyright (c) 2014 epohsoft. All rights reserved. 10 | // 11 | 12 | import UIKit 13 | 14 | class WebViewProgressView: UIView { 15 | 16 | var barAnimationDuration = 0.27 17 | var fadeAnimationDuration = 0.27 18 | var fadeOutDelay = 0.1 19 | var progress: Float { 20 | get { 21 | return _progress 22 | } 23 | set { 24 | setProgress(newValue, animated: false) 25 | } 26 | } 27 | 28 | private var progressView = UIView() 29 | private var _progress: Float = 0.0 30 | 31 | override init(frame: CGRect) { 32 | super.init(frame: frame) 33 | configureViews() 34 | } 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | super.init(coder: aDecoder) 38 | configureViews() 39 | } 40 | 41 | func setProgress(_ progress: Float, animated: Bool) { 42 | _progress = progress 43 | let isGrowing = progress > 0.0 44 | UIView.animate( 45 | withDuration: (isGrowing && animated) ? barAnimationDuration: 0.0, 46 | delay: 0, 47 | options: UIView.AnimationOptions(), 48 | animations: { 49 | self.progressView.frame.size.width = CGFloat(progress) * self.bounds.size.width 50 | }, 51 | completion: nil 52 | ) 53 | 54 | if progress >= 1.0 { 55 | UIView.animate( 56 | withDuration: animated ? barAnimationDuration : 0.0, 57 | delay: fadeOutDelay, 58 | options: UIView.AnimationOptions(), 59 | animations: { 60 | self.progressView.alpha = 0.0 61 | }, completion: { completed in 62 | self.progressView.frame.size.width = 0.0 63 | } 64 | ) 65 | } else { 66 | UIView.animate( 67 | withDuration: animated ? barAnimationDuration : 0.0, 68 | delay: fadeOutDelay, 69 | options: UIView.AnimationOptions(), 70 | animations: { 71 | self.progressView.alpha = 1.0 72 | }, 73 | completion: nil 74 | ) 75 | } 76 | } 77 | 78 | private func configureViews() { 79 | isUserInteractionEnabled = false; 80 | autoresizingMask = .flexibleWidth 81 | 82 | progressView.frame = bounds 83 | progressView.frame.size.width = 0 84 | progressView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 85 | progressView.backgroundColor = tintColor 86 | addSubview(progressView) 87 | 88 | barAnimationDuration = 0.27 89 | fadeAnimationDuration = 0.27 90 | fadeOutDelay = 0.1 91 | } 92 | } 93 | --------------------------------------------------------------------------------