├── GTMWebKitExample ├── zh-Hans.lproj │ ├── Main.strings │ └── LaunchScreen.strings ├── Assets.xcassets │ ├── Contents.json │ ├── Logo.imageset │ │ ├── logo.png │ │ ├── wk-2.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── wk-1.png │ │ ├── wk-2.png │ │ ├── wk-3.png │ │ ├── wk-4.png │ │ ├── wk-5.png │ │ ├── wk-6.png │ │ ├── wk-7.png │ │ ├── wk-8.png │ │ ├── wk.png │ │ └── Contents.json │ ├── cus_back.imageset │ │ ├── icon_cp.png │ │ ├── icon_cp-1.png │ │ └── Contents.json │ └── nav_back.imageset │ │ ├── nav_back@2x.png │ │ ├── nav_back@3x.png │ │ └── Contents.json ├── ScanViewController.swift ├── Info.plist ├── BarcodeScanable.swift ├── Swift Scan │ ├── LBXScanNetAnimation.swift │ ├── LBXPermissions.swift │ ├── LBXScanLineAnimation.swift │ ├── LBXScanViewStyle.swift │ ├── LBXScanViewController.swift │ ├── LBXScanView.swift │ └── LBXScanWrapper.swift ├── AppDelegate.swift ├── test.html ├── CustomWebViewController.swift ├── ViewController.swift ├── UIWebViewCookiesVC.swift ├── WKWebViewCookiesVC.swift └── Base.lproj │ └── LaunchScreen.storyboard ├── logo.png ├── GTMWebKit ├── GTMWebKit.bundle │ ├── 404@2x.png │ ├── 404@3x.png │ ├── back@2x.png │ ├── back@3x.png │ ├── forward@2x.png │ ├── forward@3x.png │ ├── nav_back@2x.png │ ├── nav_back@3x.png │ ├── nosingle@2x.png │ ├── nosingle@3x.png │ ├── en.lproj │ │ ├── Root.strings │ │ └── Localizable.strings │ ├── zh-Hans.lproj │ │ └── Localizable.strings │ └── Root.plist ├── GTMWebView+Bundle.swift ├── URLConvertible.swift ├── GTMAlertable.swift ├── WeakScriptMessageHandler.swift ├── GTMWebKit.swift ├── Info.plist ├── GTMWebViewCookies.swift ├── GTMWebView+Alert.swift ├── GTMWebControls.swift ├── GTMWebView+WKWebView.swift └── GTMWebViewController.swift ├── GTMWebKit.podspec ├── LICENSE ├── README.md └── GTMWebKit.xcodeproj └── project.pbxproj /GTMWebKitExample/zh-Hans.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /GTMWebKitExample/zh-Hans.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/logo.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/404@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/404@2x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/404@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/404@3x.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/back@2x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/back@3x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/forward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/forward@2x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/forward@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/forward@3x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/nav_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/nav_back@2x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/nav_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/nav_back@3x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/nosingle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/nosingle@2x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/nosingle@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/nosingle@3x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/en.lproj/Root.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKit/GTMWebKit.bundle/en.lproj/Root.strings -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/Logo.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/Logo.imageset/logo.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/Logo.imageset/wk-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/Logo.imageset/wk-2.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-1.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-2.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-3.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-4.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-5.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-6.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-7.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk-8.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/wk.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/cus_back.imageset/icon_cp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/cus_back.imageset/icon_cp.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/cus_back.imageset/icon_cp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/cus_back.imageset/icon_cp-1.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/nav_back.imageset/nav_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/nav_back.imageset/nav_back@2x.png -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/nav_back.imageset/nav_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GTMYang/GTMWebKit/HEAD/GTMWebKitExample/Assets.xcassets/nav_back.imageset/nav_back@3x.png -------------------------------------------------------------------------------- /GTMWebKit/GTMWebView+Bundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebView+Bundle.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/30. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension GTMWebViewController { 12 | 13 | var sourceBundle: Bundle { 14 | return GTM_bundle() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "wk-2.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "logo.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/cus_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_cp-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icon_cp.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/nav_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "nav_back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "nav_back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /GTMWebKit/URLConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol URLConvertible { 12 | func url() -> URL? 13 | } 14 | 15 | extension String: URLConvertible { 16 | public func url() -> URL? { 17 | return URL.init(string: self) 18 | } 19 | } 20 | 21 | extension URL: URLConvertible { 22 | public func url() -> URL? { 23 | return self 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | AXWebViewController.strings 3 | AXWebViewController 4 | 5 | Created by devedbox on 16/8/14. 6 | Copyright © 2016年 AiXing. All rights reserved. 7 | */ 8 | "back" = "返回"; 9 | "close" = " 关闭 "; 10 | "done" = "完成"; 11 | "loading" = "加载中..."; 12 | "load failed" = "加载失败"; 13 | "load failed:" = "网页加载失败:"; 14 | "web page" = "网页由"; 15 | "provided" = "提供"; 16 | "browsing the web" = "内容浏览"; 17 | 18 | "messages" = "来自网页的消息"; 19 | "cancel" = "取消"; 20 | "confirm" = "确定"; 21 | "input" = "输入文字消息"; 22 | "terminate" = "网页进程终止"; 23 | 24 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | AXWebViewController.strings 3 | AXWebViewController 4 | 5 | Created by devedbox on 16/8/14. 6 | Copyright © 2016年 AiXing. All rights reserved. 7 | */ 8 | "back" = "Back"; 9 | "close" = " Close "; 10 | "done" = "Done"; 11 | "loading" = "Loading..."; 12 | "load failed" = "Load failed"; 13 | "load failed:" = "Load failed: "; 14 | "web page" = "This page was generated by "; 15 | "provided" = ""; 16 | "browsing the web" = "Web browsing"; 17 | 18 | "messages" = "Messages from web page"; 19 | "cancel" = "Cancel"; 20 | "confirm" = "Confirm"; 21 | "input" = "Input text content"; 22 | "terminate" = "Web content process did terminate"; 23 | 24 | -------------------------------------------------------------------------------- /GTMWebKit.podspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pod::Spec.new do |s| 4 | 5 | s.name = "GTMWebKit" 6 | s.version = "1.0.1" 7 | s.summary = "swift 针对 WKWebKit 的封装" 8 | s.swift_version= "4.2" 9 | 10 | s.homepage = "https://github.com/GTMYang/GTMWebKit" 11 | 12 | s.license = { :type => "MIT", :file => "LICENSE" } 13 | s.author = { "GTMYang" => "17757128523@163.com" } 14 | 15 | 16 | s.source = { :git => "https://github.com/GTMYang/GTMWebKit.git", :tag => s.version } 17 | s.source_files = 'GTMWebKit/*.{h,swift}' 18 | s.resources = 'GTMWebKit/GTMWebKit.bundle' 19 | 20 | s.ios.deployment_target = '8.0' 21 | s.frameworks = 'UIKit','Foundation','WebKit' 22 | 23 | 24 | end 25 | -------------------------------------------------------------------------------- /GTMWebKit/GTMAlertable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMAlertable.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/26. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol GTMAlertable { 12 | func alert(_ message: String) 13 | } 14 | 15 | extension GTMAlertable where Self: UIViewController { 16 | public func alert(_ message: String) { 17 | let alert = UIAlertController(title: "", message: message, preferredStyle: .alert) 18 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in 19 | alert.dismiss(animated: true, completion: nil) 20 | })) 21 | 22 | self.present(alert, animated: true, completion: nil) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GTMWebKit/WeakScriptMessageHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeakScriptMessageDelegate.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import WebKit 11 | 12 | public class WeakScriptMessageHandler:NSObject, WKScriptMessageHandler { 13 | weak var realHandler: WKScriptMessageHandler? 14 | 15 | init(_ realHandler: WKScriptMessageHandler) { 16 | super.init() 17 | self.realHandler = realHandler 18 | } 19 | 20 | // MARK:- WKScriptMessageHandler 21 | public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { 22 | self.realHandler?.userContentController(userContentController, didReceive: message) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebKit.swift 3 | // GTMWebKit 4 | // 5 | // Created by 骆扬 on 2018/9/11. 6 | // Copyright © 2018年 yang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct GTMWebKitConfig { 12 | static var debug: Bool = false 13 | } 14 | 15 | func println(_ msg: String) { 16 | if GTMWebKitConfig.debug { 17 | print("GTMWebKit -----> \(msg)") 18 | } 19 | } 20 | 21 | func GTM_bundle() -> Bundle { 22 | let bundle = Bundle.init(for: GTMWebViewController.self) 23 | 24 | let resourcePath = bundle.path(forResource: "GTMWebKit", ofType: "bundle") 25 | if let path = resourcePath { 26 | let bundle2 = Bundle.init(path: path) 27 | if let bundle = bundle2 { 28 | return bundle 29 | } 30 | } 31 | 32 | return bundle 33 | } 34 | -------------------------------------------------------------------------------- /GTMWebKit/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 | FMWK 17 | CFBundleShortVersionString 18 | 0.5 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /GTMWebKitExample/ScanViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScanViewController.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/11/2. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ScanViewController: LBXScanViewController { 12 | 13 | weak var delegate: ScanViewControllerDelegate? 14 | 15 | // MARK: - handleCodeResult 16 | /** 17 | 处理扫码结果,如果是继承本控制器的,可以重写该方法,作出相应地处理 18 | */ 19 | open override func handleCodeResult(arrayResult:[LBXScanResult]) 20 | { 21 | let result:LBXScanResult = arrayResult[0] 22 | 23 | print("swiftScan -> scan success: barcode = \(result.strScanned!) type = [\(String(describing: result.strBarCodeType))]") 24 | self.delegate?.onScanSuccess(barcode: result.strScanned!) 25 | 26 | // self.dismiss(animated: true, completion: nil) 27 | let _ = self.navigationController?.popViewController(animated: true) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017年 GTMYang 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 | -------------------------------------------------------------------------------- /GTMWebKitExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "wk-8.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "wk-6.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "wk-7.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "wk-4.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "wk-5.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "wk-3.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "wk-2.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "wk-1.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "1024x1024", 53 | "idiom" : "ios-marketing", 54 | "filename" : "wk.png", 55 | "scale" : "1x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /GTMWebKit/GTMWebKit.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StringsTable 6 | Root 7 | PreferenceSpecifiers 8 | 9 | 10 | Type 11 | PSGroupSpecifier 12 | Title 13 | Group 14 | 15 | 16 | Type 17 | PSTextFieldSpecifier 18 | Title 19 | Name 20 | Key 21 | name_preference 22 | DefaultValue 23 | 24 | IsSecure 25 | 26 | KeyboardType 27 | Alphabet 28 | AutocapitalizationType 29 | None 30 | AutocorrectionType 31 | No 32 | 33 | 34 | Type 35 | PSToggleSwitchSpecifier 36 | Title 37 | Enabled 38 | Key 39 | enabled_preference 40 | DefaultValue 41 | 42 | 43 | 44 | Type 45 | PSSliderSpecifier 46 | Key 47 | slider_preference 48 | DefaultValue 49 | 0.5 50 | MinimumValue 51 | 0 52 | MaximumValue 53 | 1 54 | MinimumValueImage 55 | 56 | MaximumValueImage 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebViewCookies.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebViewCookies.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/11/16. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | public class GTMWebViewCookies: NSObject { 13 | 14 | /// 将 HTTPCookieStorage 中的 Cookies 同步到 WKWebsiteDataStore 中 15 | public static func shareNativeCookies(url: URLConvertible) { 16 | if let _url = url.url() { 17 | if #available(iOS 11.0, *) { 18 | let cookiesStorage = HTTPCookieStorage.shared 19 | let cookies = cookiesStorage.cookies(for: _url) 20 | let selfCookies = cookies!.filter({ (cookie) -> Bool in 21 | return cookie.domain == _url.host 22 | }) 23 | 24 | let dataStore = WKWebsiteDataStore.default() 25 | for cookie in selfCookies { 26 | dataStore.httpCookieStore.setCookie(cookie, completionHandler: nil) 27 | } 28 | 29 | } 30 | } 31 | } 32 | /// 将 WKWebsiteDataStore 中的 Cookies 同步到 HTTPCookieStorage 中 33 | public static func shareWebViewCookies(url: URLConvertible) { 34 | if let _url = url.url() { 35 | if #available(iOS 11.0, *) { 36 | let dataStore = WKWebsiteDataStore.default() 37 | dataStore.httpCookieStore.getAllCookies({ (cookies) in 38 | let selfCookies = cookies.filter({ (cookie) -> Bool in 39 | return cookie.domain == _url.host 40 | }) 41 | let cookiesStorage = HTTPCookieStorage.shared 42 | for cookie in selfCookies { 43 | cookiesStorage.setCookie(cookie) 44 | } 45 | }) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GTMWebKitExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | WebKit例子 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.1 21 | CFBundleVersion 22 | 1.0.1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | NSAllowsArbitraryLoadsInWebContent 30 | 31 | 32 | NSCameraUsageDescription 33 | 请点击“允许”以允许访问 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /GTMWebKitExample/BarcodeScanable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarcodeScanable.swift 3 | // Olliix 4 | // 5 | // Created by luoyang on 2017/9/6. 6 | // Copyright © 2017年 syncsoft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ScanViewControllerDelegate: class { 12 | func onScanSuccess(barcode: String) 13 | } 14 | 15 | protocol BarcodeScanable: ScanViewControllerDelegate { 16 | func startScanBarcode(viewTitle: String) 17 | } 18 | 19 | extension BarcodeScanable where Self: UIViewController { 20 | 21 | /// 扫描方法 22 | func startScanBarcode(viewTitle: String) { 23 | //设置扫码区域参数 24 | var style = LBXScanViewStyle() 25 | 26 | style.centerUpOffset = 44 27 | style.photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Inner 28 | style.photoframeLineW = 4 29 | style.photoframeAngleW = 28 30 | style.photoframeAngleH = 16 31 | style.isNeedShowRetangle = false 32 | 33 | style.anmiationStyle = LBXScanViewAnimationStyle.LineStill 34 | 35 | 36 | style.animationImage = createImageWithColor(color: UIColor.red) 37 | //非正方形 38 | //设置矩形宽高比 39 | style.whRatio = 4.3/2.18 40 | 41 | //离左边和右边距离 42 | style.xScanRetangleOffset = 30 43 | 44 | let scanViewController = ScanViewController() 45 | scanViewController.title = viewTitle 46 | 47 | scanViewController.delegate = self 48 | 49 | scanViewController.scanStyle = style 50 | 51 | self.navigationController?.pushViewController(scanViewController, animated: false) 52 | } 53 | 54 | private func createImageWithColor(color:UIColor)->UIImage { 55 | let rect=CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0) 56 | UIGraphicsBeginImageContext(rect.size) 57 | let context = UIGraphicsGetCurrentContext() 58 | context!.setFillColor(color.cgColor) 59 | context!.fill(rect) 60 | let theImage = UIGraphicsGetImageFromCurrentImageContext() 61 | UIGraphicsEndImageContext() 62 | return theImage! 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebView+Alert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebViewAlert.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | extension GTMWebViewController: WKUIDelegate { 13 | 14 | open func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { 15 | let alert = UIAlertController(title: "", message: message, preferredStyle: .alert) 16 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in 17 | completionHandler() 18 | })) 19 | 20 | self.present(alert, animated: false, completion: nil) 21 | } 22 | 23 | open func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { 24 | let alert = UIAlertController(title: "", message: message, preferredStyle: .alert) 25 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in 26 | completionHandler(true) 27 | })) 28 | alert.addAction(UIAlertAction(title: "Cancle", style: .cancel, handler: { (_) in 29 | completionHandler(false) 30 | })) 31 | self.present(alert, animated: false, completion: nil) 32 | } 33 | 34 | open func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { 35 | 36 | let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .alert) 37 | alert.addTextField { (textFiled) in 38 | textFiled.textColor = .red 39 | } 40 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in 41 | completionHandler(alert.textFields![0].text!) 42 | })) 43 | self.present(alert, animated: false, completion: nil) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanNetAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanNetAnimation.swift 3 | // swiftScan 4 | // 5 | // Created by lbxia on 15/12/9. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LBXScanNetAnimation: UIImageView { 12 | 13 | var isAnimationing = false 14 | var animationRect:CGRect = CGRect.zero 15 | 16 | 17 | 18 | static public func instance()->LBXScanNetAnimation 19 | { 20 | return LBXScanNetAnimation() 21 | } 22 | 23 | func startAnimatingWithRect(animationRect:CGRect, parentView:UIView, image:UIImage?) 24 | { 25 | self.image = image 26 | self.animationRect = animationRect 27 | parentView.addSubview(self) 28 | 29 | self.isHidden = false; 30 | 31 | isAnimationing = true; 32 | 33 | if (image != nil) 34 | { 35 | stepAnimation() 36 | } 37 | 38 | 39 | 40 | } 41 | 42 | @objc func stepAnimation() 43 | { 44 | if (!isAnimationing) { 45 | return; 46 | } 47 | 48 | var frame = animationRect; 49 | 50 | 51 | let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width; 52 | 53 | frame.origin.y -= hImg; 54 | frame.size.height = hImg; 55 | self.frame = frame; 56 | 57 | self.alpha = 0.0; 58 | 59 | UIView.animate(withDuration: 1.2, animations: { () -> Void in 60 | 61 | self.alpha = 1.0; 62 | 63 | var frame = self.animationRect; 64 | let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width; 65 | 66 | frame.origin.y += (frame.size.height - hImg); 67 | frame.size.height = hImg; 68 | 69 | self.frame = frame; 70 | 71 | }, completion:{ (value: Bool) -> Void in 72 | 73 | self.perform(#selector(LBXScanNetAnimation.stepAnimation), with: nil, afterDelay: 0.3) 74 | 75 | }) 76 | 77 | } 78 | 79 | func stopStepAnimating() 80 | { 81 | self.isHidden = true; 82 | isAnimationing = false; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXPermissions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXPermissions.swift 3 | // swiftScan 4 | // 5 | // Created by xialibing on 15/12/15. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | import Photos 12 | import AssetsLibrary 13 | 14 | 15 | 16 | class LBXPermissions: NSObject { 17 | 18 | //MARK: ----获取相册权限 19 | static func authorizePhotoWith(comletion:@escaping (Bool)->Void ) 20 | { 21 | let granted = PHPhotoLibrary.authorizationStatus() 22 | switch granted { 23 | case PHAuthorizationStatus.authorized: 24 | comletion(true) 25 | case PHAuthorizationStatus.denied,PHAuthorizationStatus.restricted: 26 | comletion(false) 27 | case PHAuthorizationStatus.notDetermined: 28 | PHPhotoLibrary.requestAuthorization({ (status) in 29 | comletion(status == PHAuthorizationStatus.authorized ? true:false) 30 | }) 31 | } 32 | 33 | } 34 | 35 | //MARK: ---相机权限 36 | static func authorizeCameraWith(comletion:@escaping (Bool)->Void ) 37 | { 38 | let granted = AVCaptureDevice.authorizationStatus(for: AVMediaType.video); 39 | 40 | switch granted { 41 | case AVAuthorizationStatus.authorized: 42 | comletion(true) 43 | break; 44 | case AVAuthorizationStatus.denied: 45 | comletion(false) 46 | break; 47 | case AVAuthorizationStatus.restricted: 48 | comletion(false) 49 | break; 50 | case AVAuthorizationStatus.notDetermined: 51 | 52 | AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted:Bool) in 53 | comletion(granted) 54 | }) 55 | } 56 | } 57 | 58 | //MARK:跳转到APP系统设置权限界面 59 | static func jumpToSystemPrivacySetting() 60 | { 61 | let appSetting = URL(string:UIApplication.openSettingsURLString) 62 | 63 | if appSetting != nil 64 | { 65 | if #available(iOS 10, *) { 66 | UIApplication.shared.open(appSetting!, options: [:], completionHandler: nil) 67 | } 68 | else{ 69 | UIApplication.shared.openURL(appSetting!) 70 | } 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /GTMWebKitExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | UINavigationBar.appearance().tintColor = UIColor.gray 20 | 21 | 22 | return true 23 | } 24 | 25 | func applicationWillResignActive(_ application: UIApplication) { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 28 | } 29 | 30 | func applicationDidEnterBackground(_ application: UIApplication) { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | func applicationWillEnterForeground(_ application: UIApplication) { 36 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | func applicationDidBecomeActive(_ application: UIApplication) { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | func applicationWillTerminate(_ application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanLineAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanLineAnimation.swift 3 | // swiftScan 4 | // 5 | // Created by lbxia on 15/12/9. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LBXScanLineAnimation: UIImageView { 12 | 13 | var isAnimationing = false 14 | var animationRect: CGRect = CGRect.zero 15 | 16 | func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) 17 | { 18 | self.image = image 19 | self.animationRect = animationRect 20 | parentView.addSubview(self) 21 | 22 | self.isHidden = false; 23 | 24 | isAnimationing = true; 25 | 26 | if image != nil 27 | { 28 | stepAnimation() 29 | } 30 | 31 | } 32 | 33 | @objc func stepAnimation() 34 | { 35 | if (!isAnimationing) { 36 | return; 37 | } 38 | 39 | var frame:CGRect = animationRect; 40 | 41 | let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width; 42 | 43 | frame.origin.y -= hImg; 44 | frame.size.height = hImg; 45 | self.frame = frame; 46 | self.alpha = 0.0; 47 | 48 | UIView.animate(withDuration: 1.4, animations: { () -> Void in 49 | 50 | self.alpha = 1.0; 51 | 52 | var frame = self.animationRect; 53 | let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width; 54 | 55 | frame.origin.y += (frame.size.height - hImg); 56 | frame.size.height = hImg; 57 | 58 | self.frame = frame; 59 | 60 | }, completion:{ (value: Bool) -> Void in 61 | 62 | self.perform(#selector(LBXScanLineAnimation.stepAnimation), with: nil, afterDelay: 0.3) 63 | }) 64 | 65 | } 66 | 67 | func stopStepAnimating() 68 | { 69 | self.isHidden = true; 70 | isAnimationing = false; 71 | } 72 | 73 | static public func instance()->LBXScanLineAnimation 74 | { 75 | return LBXScanLineAnimation() 76 | } 77 | 78 | deinit 79 | { 80 | // print("LBXScanLineAnimation deinit") 81 | stopStepAnimating() 82 | } 83 | 84 | } 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanViewStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanViewStyle.swift 3 | // swiftScan 4 | // 5 | // Created by xialibing on 15/12/8. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | ///扫码区域动画效果 12 | public enum LBXScanViewAnimationStyle 13 | { 14 | case LineMove //线条上下移动 15 | case NetGrid//网格 16 | case LineStill//线条停止在扫码区域中央 17 | case None //无动画 18 | } 19 | 20 | ///扫码区域4个角位置类型 21 | public enum LBXScanViewPhotoframeAngleStyle 22 | { 23 | case Inner//内嵌,一般不显示矩形框情况下 24 | case Outer//外嵌,包围在矩形框的4个角 25 | case On //在矩形框的4个角上,覆盖 26 | } 27 | 28 | 29 | public struct LBXScanViewStyle 30 | { 31 | 32 | // MARK: - -中心位置矩形框 33 | 34 | /// 是否需要绘制扫码矩形框,默认YES 35 | public var isNeedShowRetangle:Bool = true 36 | 37 | /** 38 | * 默认扫码区域为正方形,如果扫码区域不是正方形,设置宽高比 39 | */ 40 | public var whRatio:CGFloat = 1.0 41 | 42 | /** 43 | @brief 矩形框(视频显示透明区)域向上移动偏移量,0表示扫码透明区域在当前视图中心位置,如果负值表示扫码区域下移 44 | */ 45 | public var centerUpOffset:CGFloat = 44 46 | 47 | /** 48 | * 矩形框(视频显示透明区)域离界面左边及右边距离,默认60 49 | */ 50 | public var xScanRetangleOffset:CGFloat = 60 51 | 52 | /** 53 | @brief 矩形框线条颜色,默认白色 54 | */ 55 | public var colorRetangleLine = UIColor.white 56 | 57 | 58 | //MARK -矩形框(扫码区域)周围4个角 59 | 60 | /** 61 | @brief 扫码区域的4个角类型 62 | */ 63 | public var photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Outer 64 | 65 | //4个角的颜色 66 | public var colorAngle = UIColor(red: 0.0, green: 167.0/255.0, blue: 231.0/255.0, alpha: 1.0) 67 | 68 | //扫码区域4个角的宽度和高度 69 | public var photoframeAngleW:CGFloat = 24.0 70 | public var photoframeAngleH:CGFloat = 24.0 71 | /** 72 | @brief 扫码区域4个角的线条宽度,默认6,建议8到4之间 73 | */ 74 | public var photoframeLineW:CGFloat = 6 75 | 76 | //MARK: ----动画效果 77 | 78 | /** 79 | @brief 扫码动画效果:线条或网格 80 | */ 81 | public var anmiationStyle = LBXScanViewAnimationStyle.LineMove 82 | 83 | 84 | /** 85 | * 动画效果的图像,如线条或网格的图像 86 | */ 87 | public var animationImage:UIImage? 88 | 89 | 90 | // MARK: -非识别区域颜色,默认 RGBA (0,0,0,0.5),范围(0--1) 91 | public var color_NotRecoginitonArea:UIColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5); 92 | 93 | public init() 94 | { 95 | 96 | } 97 | 98 | 99 | } 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebControls.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebControls.swift 3 | // GTMWebKit 4 | // 5 | // Created by 骆扬 on 2018/11/8. 6 | // Copyright © 2018 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol GTMWebErrorViewReloadHandler { 12 | /// 自定义View 可以通过此 block 刷新页面 13 | var reloadHandler: ()->Void { get set } 14 | } 15 | 16 | public typealias GTMWebErrorView = UIView & GTMWebErrorViewReloadHandler 17 | 18 | class GTMWebNetErrorView: GTMWebErrorView { 19 | var reloadHandler: () -> Void = {} 20 | private var iconName: String? 21 | private var iconImageV: UIImageView! 22 | private var reloadButton: UIButton! 23 | 24 | convenience init(_ iconName: String) { 25 | let size = UIScreen.main.bounds.size 26 | self.init(frame: CGRect.init(x: (size.width - 320)/2, y: (size.height - 200)/2 - 64, width: 320, height: 200)) 27 | self.iconName = iconName 28 | setup() 29 | } 30 | 31 | private override init(frame: CGRect) { 32 | super.init(frame: frame) 33 | } 34 | required init?(coder aDecoder: NSCoder) { 35 | super.init(coder: aDecoder) 36 | } 37 | 38 | func setup() { 39 | // icon 40 | let icon = UIImageView() 41 | let iconImage = UIImage.init(named: iconName ?? "", in: GTM_bundle(), compatibleWith: nil) 42 | icon.image = iconImage 43 | icon.sizeToFit() 44 | self.iconImageV = icon 45 | self.addSubview(iconImageV) 46 | // button 47 | let button = UIButton(type: .custom) 48 | button.setTitle("重新加载", for: .normal) 49 | button.setTitleColor(.gray, for: .normal) 50 | button.sizeToFit() 51 | button.addTarget(self, action: #selector(onReload), for: .touchUpInside) 52 | button.layer.borderColor = UIColor.lightGray.cgColor 53 | button.layer.borderWidth = 1 54 | button.layer.cornerRadius = 2 55 | self.reloadButton = button 56 | self.addSubview(reloadButton) 57 | } 58 | 59 | // MARK: - Event 60 | @objc func onReload() { 61 | self.reloadHandler() 62 | } 63 | 64 | // MARK: - Layout 65 | override func layoutSubviews() { 66 | super.layoutSubviews() 67 | let frame = self.bounds 68 | var y: CGFloat = 0 69 | if var size = iconImageV.image?.size { 70 | iconImageV.frame = CGRect(x: (frame.size.width - size.width)/2, y: y, width: size.width, height: size.height) 71 | y = size.height + 30 72 | size = CGSize.init(width: 200, height: 40) 73 | reloadButton.frame = CGRect(x: (frame.size.width - size.width)/2, y: y, width: size.width, height: size.height) 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 | 6 |

7 | 8 | 9 | 10 | 11 |

12 | 13 |
14 | 15 | GTMWebKit 16 | =================== 17 | `GTMWebKit` swift 针对 WKWebView 的封装 18 | 19 | # Introduction 20 | 21 | - 使得在App内嵌网页变得非常简单 22 | - 实现了类似微信里面的网页导航控制功能 23 | - 注册JS使用的API方法变得简单 24 | - 支持Swift4 25 | - 支持与原生代码共享Cookies 26 | 27 | 28 | 29 | # Demo 30 | 直接下载代码,里面详细的使用例子 31 | 32 | # Installation 33 | 34 | ## Cocoapods 35 | 36 | Install Cocoapods if need be. 37 | 38 | ```bash 39 | $ gem install cocoapods 40 | ``` 41 | 42 | Add `GTMWebKit` in your `Podfile`. 43 | 44 | ```ruby 45 | use_frameworks! 46 | 47 | pod 'GTMWebKit' 48 | ``` 49 | 50 | Then, run the following command. 51 | 52 | ```bash 53 | $ pod install 54 | ``` 55 | 56 | 57 | ## Manual 58 | 59 | Copy `GTMWebKit` folder to your project. That's it. 60 | 61 | _**Note:** Make sure that all files in `GTMWebKit` included in Compile Sources in Build Phases._ 62 | 63 | # 版本 64 | 65 | ## Vesrion 1.0 66 | 67 | This version requires Xcode 9.0 and Swift 4.2. 68 | 69 | # 使用帮助 70 | 71 | Firstly, import `GTMWebKit`. 72 | 73 | ```swift 74 | import GTMWebKit 75 | ``` 76 | 77 | ## Push方式内嵌网页 78 | ```swift 79 | // Push 80 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .navbar) 81 | self.navigationController?.pushViewController(webVC, animated: true) 82 | ``` 83 | 84 | ## Present方式内嵌网页 85 | ```swift 86 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .toolbar) 87 | let navigationC = UINavigationController.init(rootViewController: webVC) 88 | navigationC.navigationBar.tintColor = UIColor.gray 89 | self.present(navigationC, animated: true, completion: nil) 90 | ``` 91 | 92 | ## 配置项 93 | 94 | ```swift 95 | public enum GTMWK_NavigationType { 96 | case navbar // web导航控制按钮放在导航栏 97 | case toolbar // web导航控制按钮放在底部工具栏 98 | case both // 同时使用两种导航按钮 99 | case none // 不使用web导航按钮 100 | } 101 | 102 | public var navigType: GTMWK_NavigationType! // 控制网页导航的方式(导航栏,工具栏) 103 | public var isShowCloseItem = true // 是否显示关闭按钮(navigType == .navbar 时使用) 104 | public var isShowToolbar = true // 是否显示工具栏(navigType == .toolbar 时使用) 105 | public var isForcedUIWebView = false // 强制使用 UIWebView 106 | public var isNeedShareCookies = false // 是否需要共享cookies 107 | public var backIconName: String? // 返回按钮图标,可自行设置 108 | public var view404: GTMWebErrorView! // 资源不存在的时候展示的UI,可自定义 109 | public var netErrorView: GTMWebErrorView! // 网络错误的时候展示的UI,可自定义 110 | 111 | ``` 112 | 113 | 114 | 115 | #参与开源 116 | 欢迎提交 issue 和 PR,大门永远向所有人敞开。 117 | 118 | #开源协议 119 | 本项目遵循 MIT 协议开源,具体请查看根目录下的 [LICENSE](https://raw.githubusercontent.com/GTMYang/GTMWebKit/master/LICENSE) 文件。 120 | 121 | 122 | -------------------------------------------------------------------------------- /GTMWebKitExample/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | iOS and Js 7 | 15 | 16 | 17 | 18 | 19 |
20 |

Test how to use swift call js


21 |

22 |

23 |
24 |
25 |
26 |

27 |

28 |

29 |
Click me here: 跳转到百度
30 |
31 | 32 |
33 |
34 | 35 |
36 | 37 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /GTMWebKitExample/CustomWebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomWebViewController.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/11/1. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GTMWebKit 11 | import WebKit 12 | 13 | class CustomWebViewController: GTMWebViewController { 14 | 15 | var lblJsMessage: UILabel! 16 | var btnCallJsMethod: UIButton! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.setup() 22 | self.registApiForJs() 23 | } 24 | 25 | func setup() { 26 | self.title = "Native code <-> JS" 27 | 28 | let screenSize = UIScreen.main.bounds 29 | // message label 30 | self.lblJsMessage = UILabel() 31 | lblJsMessage.frame = CGRect.init(x: 0, y: 64, width: screenSize.width, height: 100) 32 | lblJsMessage.textAlignment = .center 33 | lblJsMessage.backgroundColor = .orange 34 | lblJsMessage.isHidden = true 35 | self.view.addSubview(self.lblJsMessage) 36 | // button 37 | self.btnCallJsMethod = UIButton.init(type: .custom) 38 | btnCallJsMethod.frame = CGRect.init(x: 8, y: screenSize.height - 122, width: screenSize.width - 16 , height: 50) 39 | btnCallJsMethod.setTitle("Swift 调用 JS 方法改变网页中控件颜色", for: .normal) 40 | btnCallJsMethod.setTitleColor(.black, for: .normal) 41 | btnCallJsMethod.backgroundColor = .white 42 | btnCallJsMethod.layer.borderColor = UIColor.gray.cgColor 43 | btnCallJsMethod.layer.borderWidth = 1 44 | btnCallJsMethod.addTarget(self, action: #selector(onCallJsMethod), for: .touchUpInside) 45 | self.view.addSubview(self.btnCallJsMethod) 46 | } 47 | 48 | override func webDidLoad() { 49 | super.webDidLoad() 50 | self.wkWebView?.evaluateJavaScript("jsinit();", completionHandler: { (data, error) in 51 | if let _ = error { 52 | print("error: javascript 'jsinit()' method is undifind!") 53 | } 54 | }) 55 | } 56 | 57 | func registApiForJs() { 58 | 59 | // 简单测试方法 60 | self.registApi(method: "test") { [weak self] (body) in 61 | print("\nCustomWebViewController -----> recived js message: \(body ?? "")\n\n") 62 | let message = "\(body ?? "")" 63 | self?.showMessage(message: message) 64 | } 65 | // 扫描功能API 66 | self.registApi(method: "scanBarcode") { [weak self] (body) in 67 | self?.startScanBarcode(viewTitle: "条码扫描") 68 | } 69 | 70 | } 71 | 72 | // MARK: - Events 73 | @objc func onCallJsMethod() { 74 | let wkwebV = self.webView 75 | wkwebV?.evaluateJavaScript("changeColor();", completionHandler: nil) 76 | } 77 | 78 | // MARK: - Private 79 | fileprivate func showMessage(message: String) { 80 | self.lblJsMessage.text = message 81 | 82 | DispatchQueue.main.async { 83 | self.lblJsMessage.isHidden = false 84 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, execute: { 85 | self.lblJsMessage.isHidden = true 86 | }) 87 | } 88 | } 89 | 90 | } 91 | 92 | extension CustomWebViewController: ScanViewControllerDelegate, BarcodeScanable { 93 | 94 | // MARK: - ScanViewControllerDelegate 95 | func onScanSuccess(barcode: String) { 96 | let message = "条码内容为:\(barcode)" 97 | self.showMessage(message: message) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /GTMWebKitExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GTMWebKit 11 | 12 | class ViewController: UITableViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do any additional setup after loading the view, typically from a nib. 17 | self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "nav_back") 18 | } 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | // Dispose of any resources that can be recreated. 23 | } 24 | 25 | // MARK: - Table View 26 | 27 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 28 | if indexPath.row == 0 { 29 | // Push 30 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .navbar) 31 | self.navigationController?.pushViewController(webVC, animated: true) 32 | } else if indexPath.row == 1 { 33 | // Present 34 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .toolbar) 35 | let navigationC = UINavigationController.init(rootViewController: webVC) 36 | navigationC.navigationBar.tintColor = UIColor.gray 37 | self.present(navigationC, animated: true, completion: nil) 38 | } else if indexPath.row == 2 { 39 | // Github 40 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .both) 41 | self.navigationController?.pushViewController(webVC, animated: true) 42 | } else if indexPath.row == 3 { 43 | // Github 44 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .none) 45 | self.navigationController?.pushViewController(webVC, animated: true) 46 | } else if indexPath.row == 4 { 47 | // Github 48 | let webVC = GTMWebViewController.init(with: "https://github.com", navigType: .navbar) 49 | webVC.isShowCloseItem = false 50 | self.navigationController?.pushViewController(webVC, animated: true) 51 | } else if indexPath.row == 5 { 52 | // WKWebView Native <-> JS 53 | let url = Bundle.main.url(forResource: "test", withExtension: "html") 54 | let webVC = CustomWebViewController.init(with: url!, navigType: .navbar) 55 | self.navigationController?.pushViewController(webVC, animated: true) 56 | } else if indexPath.row == 6 { 57 | // WKWebView Cookies 58 | let webVC = WKWebViewCookiesVC.init(with: cookieTestUrl, navigType: .navbar) 59 | webVC.isShowCloseItem = false 60 | webVC.isShowToolbar = false 61 | webVC.isNeedShareCookies = true 62 | 63 | self.navigationController?.pushViewController(webVC, animated: true) 64 | } else if indexPath.row == 7 { 65 | // 404 66 | let webVC = GTMWebViewController.init(with: "https://www.baidu1.com", navigType: .navbar) 67 | self.navigationController?.pushViewController(webVC, animated: true) 68 | } else if indexPath.row == 8 { 69 | // back icon 70 | let webVC = GTMWebViewController.init(with: "https://www.baidu.com", navigType: .navbar) 71 | webVC.backIconName = "cus_back" 72 | self.navigationController?.pushViewController(webVC, animated: true) 73 | } 74 | } 75 | 76 | var cookieTestUrl = "http://192.168.85.168" //"https://www.baidu.com" // 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /GTMWebKitExample/UIWebViewCookiesVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIWebViewCookiesVC.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/11/16. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import GTMWebKit 12 | 13 | class UIWebViewCookiesVC: GTMWebViewController { 14 | 15 | var lblCookies: UILabel! 16 | var btnShowCookies: UIButton! 17 | var btnShowNativeCookies: UIButton! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | self.setup() 23 | } 24 | func setup() { 25 | self.title = "UIWebView Cookies" 26 | 27 | let screenSize = UIScreen.main.bounds 28 | // message label 29 | self.lblCookies = UILabel() 30 | lblCookies.frame = CGRect.init(x: 20, y: 84, width: screenSize.width - 40, height: 300) 31 | lblCookies.textAlignment = .left 32 | lblCookies.backgroundColor = .orange 33 | lblCookies.numberOfLines = 0 34 | lblCookies.isHidden = true 35 | self.view.addSubview(self.lblCookies) 36 | // button web cookies 37 | self.btnShowCookies = UIButton.init(type: .custom) 38 | btnShowCookies.frame = CGRect.init(x: 8, y: screenSize.height - 122, width: screenSize.width - 16 , height: 50) 39 | btnShowCookies.setTitle("WKWebsiteDataStore Cookies", for: .normal) 40 | btnShowCookies.setTitleColor(.white, for: .normal) 41 | btnShowCookies.backgroundColor = .gray 42 | btnShowCookies.layer.borderColor = UIColor.gray.cgColor 43 | btnShowCookies.layer.borderWidth = 1 44 | btnShowCookies.addTarget(self, action: #selector(onCallCookies), for: .touchUpInside) 45 | self.view.addSubview(self.btnShowCookies) 46 | // button native cookies 47 | self.btnShowNativeCookies = UIButton.init(type: .custom) 48 | btnShowNativeCookies.frame = CGRect.init(x: 8, y: screenSize.height - 180, width: screenSize.width - 16 , height: 50) 49 | btnShowNativeCookies.setTitle("HTTPCookieStorage Cookies", for: .normal) 50 | btnShowNativeCookies.setTitleColor(.white, for: .normal) 51 | btnShowNativeCookies.backgroundColor = .gray 52 | btnShowNativeCookies.layer.borderColor = UIColor.gray.cgColor 53 | btnShowNativeCookies.layer.borderWidth = 1 54 | btnShowNativeCookies.addTarget(self, action: #selector(onCallNativeCookies), for: .touchUpInside) 55 | self.view.addSubview(self.btnShowNativeCookies) 56 | } 57 | 58 | // MARK: - Events 59 | 60 | @objc func onCallCookies() { 61 | // WKWebView 获取Cookies 62 | if #available(iOS 11.0, *) { 63 | let dataStore = WKWebsiteDataStore.default() 64 | // let dataStore = self.wkWebView?.configuration.websiteDataStore 65 | dataStore.httpCookieStore.getAllCookies({ (cookies) in 66 | let selfCookies = cookies.filter({ (cookie) -> Bool in 67 | return cookie.domain == self.webView!.url!.host 68 | }) 69 | self.showCookies(selfCookies) 70 | }) 71 | } 72 | } 73 | 74 | @objc func onCallNativeCookies() { 75 | // UIWebView 获取Cookies 76 | let cookiesStorage = HTTPCookieStorage.shared 77 | let cookies = cookiesStorage.cookies(for: webView.url!) 78 | 79 | let selfCookies = cookies!.filter({ (cookie) -> Bool in 80 | return cookie.domain == self.webView!.url!.host 81 | }) 82 | 83 | self.showCookies(selfCookies) 84 | } 85 | 86 | 87 | 88 | // MARK: - Private 89 | fileprivate func showCookies(_ cookies: [HTTPCookie]) { 90 | let cookiesString = cookies.reduce("", { (re, cookie) -> String in 91 | return re + " \(cookie.name): \(cookie.value)\n" 92 | }) 93 | self.lblCookies.text = cookiesString 94 | 95 | DispatchQueue.main.async { 96 | self.lblCookies.isHidden = false 97 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 15, execute: { 98 | self.lblCookies.isHidden = true 99 | }) 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /GTMWebKitExample/WKWebViewCookiesVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebViewCookiesVC.swift 3 | // GTMWebKitExample 4 | // 5 | // Created by luoyang on 2017/11/16. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import GTMWebKit 12 | 13 | class WKWebViewCookiesVC: GTMWebViewController { 14 | 15 | var lblCookies: UILabel! 16 | var btnShowCookies: UIButton! 17 | var btnShowNativeCookies: UIButton! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | self.setup() 23 | } 24 | 25 | func setup() { 26 | self.title = "WKWebView Cookies" 27 | 28 | let screenSize = UIScreen.main.bounds 29 | // message label 30 | self.lblCookies = UILabel() 31 | lblCookies.frame = CGRect.init(x: 20, y: 84, width: screenSize.width - 40, height: 300) 32 | lblCookies.textAlignment = .left 33 | lblCookies.backgroundColor = .orange 34 | lblCookies.numberOfLines = 0 35 | lblCookies.isHidden = true 36 | self.view.addSubview(self.lblCookies) 37 | // button web cookies 38 | self.btnShowCookies = UIButton.init(type: .custom) 39 | btnShowCookies.frame = CGRect.init(x: 8, y: screenSize.height - 122, width: screenSize.width - 16 , height: 50) 40 | btnShowCookies.setTitle("WKWebsiteDataStore Cookies", for: .normal) 41 | btnShowCookies.setTitleColor(.white, for: .normal) 42 | btnShowCookies.backgroundColor = .gray 43 | btnShowCookies.layer.borderColor = UIColor.gray.cgColor 44 | btnShowCookies.layer.borderWidth = 1 45 | btnShowCookies.addTarget(self, action: #selector(onCallCookies), for: .touchUpInside) 46 | self.view.addSubview(self.btnShowCookies) 47 | // button native cookies 48 | self.btnShowNativeCookies = UIButton.init(type: .custom) 49 | btnShowNativeCookies.frame = CGRect.init(x: 8, y: screenSize.height - 180, width: screenSize.width - 16 , height: 50) 50 | btnShowNativeCookies.setTitle("HTTPCookieStorage Cookies", for: .normal) 51 | btnShowNativeCookies.setTitleColor(.white, for: .normal) 52 | btnShowNativeCookies.backgroundColor = .gray 53 | btnShowNativeCookies.layer.borderColor = UIColor.gray.cgColor 54 | btnShowNativeCookies.layer.borderWidth = 1 55 | btnShowNativeCookies.addTarget(self, action: #selector(onCallNativeCookies), for: .touchUpInside) 56 | self.view.addSubview(self.btnShowNativeCookies) 57 | } 58 | // 59 | // override func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { 60 | // 61 | // super.webView(webView, decidePolicyFor: navigationResponse, decisionHandler: decisionHandler) 62 | // 63 | // guard #available(iOS 11.0, *) else { 64 | // if let response = navigationResponse.response as? HTTPURLResponse { 65 | // let cookies = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String : String], for: response.url!) 66 | // self.showCookies(cookies) 67 | // } 68 | // return 69 | // } 70 | // } 71 | 72 | // MARK: - Events 73 | 74 | @objc func onCallCookies() { 75 | // WKWebView 获取Cookies 76 | if #available(iOS 11.0, *) { 77 | let dataStore = WKWebsiteDataStore.default() 78 | // let dataStore = self.wkWebView?.configuration.websiteDataStore 79 | dataStore.httpCookieStore.getAllCookies({ (cookies) in 80 | let selfCookies = cookies.filter({ (cookie) -> Bool in 81 | return cookie.domain == self.webView!.url!.host 82 | }) 83 | self.showCookies(selfCookies) 84 | }) 85 | } 86 | } 87 | 88 | @objc func onCallNativeCookies() { 89 | // UIWebView 获取Cookies 90 | let cookiesStorage = HTTPCookieStorage.shared 91 | let cookies = cookiesStorage.cookies(for: webView!.url!) 92 | 93 | let selfCookies = cookies!.filter({ (cookie) -> Bool in 94 | return cookie.domain == self.webView!.url!.host 95 | }) 96 | 97 | self.showCookies(selfCookies) 98 | } 99 | 100 | 101 | // MARK: - Private 102 | fileprivate func showCookies(_ cookies: [HTTPCookie]) { 103 | let cookiesString = cookies.reduce("", { (re, cookie) -> String in 104 | return re + " \(cookie.name): \(cookie.value)\n" 105 | }) 106 | self.lblCookies.text = cookiesString 107 | 108 | DispatchQueue.main.async { 109 | self.lblCookies.isHidden = false 110 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 15, execute: { 111 | self.lblCookies.isHidden = true 112 | }) 113 | } 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /GTMWebKitExample/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 | 33 | 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 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanViewController.swift 3 | // swiftScan 4 | // 5 | // Created by lbxia on 15/12/8. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | import AVFoundation 12 | 13 | public protocol LBXScanViewControllerDelegate { 14 | func scanFinished(scanResult: LBXScanResult, error: String?) 15 | } 16 | 17 | 18 | open class LBXScanViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 19 | 20 | //返回扫码结果,也可以通过继承本控制器,改写该handleCodeResult方法即可 21 | open var scanResultDelegate: LBXScanViewControllerDelegate? 22 | 23 | open var scanObj: LBXScanWrapper? 24 | 25 | open var scanStyle: LBXScanViewStyle? = LBXScanViewStyle() 26 | 27 | open var qRScanView: LBXScanView? 28 | 29 | 30 | //启动区域识别功能 31 | open var isOpenInterestRect = false 32 | 33 | //识别码的类型 34 | public var arrayCodeType:[AVMetadataObject.ObjectType]? 35 | 36 | //是否需要识别后的当前图像 37 | public var isNeedCodeImage = false 38 | 39 | //相机启动提示文字 40 | public var readyString:String! = "loading" 41 | 42 | override open func viewDidLoad() { 43 | super.viewDidLoad() 44 | 45 | // Do any additional setup after loading the view. 46 | 47 | // [self.view addSubview:_qRScanView]; 48 | self.view.backgroundColor = UIColor.black 49 | self.edgesForExtendedLayout = UIRectEdge(rawValue: 0) 50 | } 51 | 52 | open func setNeedCodeImage(needCodeImg:Bool) 53 | { 54 | isNeedCodeImage = needCodeImg; 55 | } 56 | //设置框内识别 57 | open func setOpenInterestRect(isOpen:Bool){ 58 | isOpenInterestRect = isOpen 59 | } 60 | 61 | override open func viewWillAppear(_ animated: Bool) { 62 | super.viewWillAppear(animated) 63 | } 64 | 65 | override open func viewDidAppear(_ animated: Bool) { 66 | 67 | super.viewDidAppear(animated) 68 | 69 | drawScanView() 70 | 71 | perform(#selector(LBXScanViewController.startScan), with: nil, afterDelay: 0.3) 72 | 73 | } 74 | 75 | @objc open func startScan() 76 | { 77 | 78 | if (scanObj == nil) 79 | { 80 | var cropRect = CGRect.zero 81 | if isOpenInterestRect 82 | { 83 | cropRect = LBXScanView.getScanRectWithPreView(preView: self.view, style:scanStyle! ) 84 | } 85 | 86 | //指定识别几种码 87 | if arrayCodeType == nil 88 | { 89 | arrayCodeType = [AVMetadataObject.ObjectType.qr,AVMetadataObject.ObjectType.ean13,AVMetadataObject.ObjectType.code128] 90 | } 91 | 92 | scanObj = LBXScanWrapper(videoPreView: self.view,objType:arrayCodeType!, isCaptureImg: isNeedCodeImage,cropRect:cropRect, success: { [weak self] (arrayResult) -> Void in 93 | 94 | if let strongSelf = self 95 | { 96 | //停止扫描动画 97 | strongSelf.qRScanView?.stopScanAnimation() 98 | 99 | strongSelf.handleCodeResult(arrayResult: arrayResult) 100 | } 101 | }) 102 | } 103 | 104 | //结束相机等待提示 105 | qRScanView?.deviceStopReadying() 106 | 107 | //开始扫描动画 108 | qRScanView?.startScanAnimation() 109 | 110 | //相机运行 111 | scanObj?.start() 112 | } 113 | 114 | open func drawScanView() 115 | { 116 | if qRScanView == nil 117 | { 118 | qRScanView = LBXScanView(frame: self.view.frame,vstyle:scanStyle! ) 119 | self.view.addSubview(qRScanView!) 120 | } 121 | qRScanView?.deviceStartReadying(readyStr: readyString) 122 | 123 | } 124 | 125 | 126 | /** 127 | 处理扫码结果,如果是继承本控制器的,可以重写该方法,作出相应地处理,或者设置delegate作出相应处理 128 | */ 129 | open func handleCodeResult(arrayResult:[LBXScanResult]) 130 | { 131 | if let delegate = scanResultDelegate { 132 | 133 | self.navigationController? .popViewController(animated: true) 134 | let result:LBXScanResult = arrayResult[0] 135 | 136 | delegate.scanFinished(scanResult: result, error: nil) 137 | 138 | }else{ 139 | 140 | for result:LBXScanResult in arrayResult 141 | { 142 | print("%@",result.strScanned ?? "") 143 | } 144 | 145 | let result:LBXScanResult = arrayResult[0] 146 | 147 | showMsg(title: result.strBarCodeType, message: result.strScanned) 148 | } 149 | } 150 | 151 | override open func viewWillDisappear(_ animated: Bool) { 152 | 153 | NSObject.cancelPreviousPerformRequests(withTarget: self) 154 | 155 | qRScanView?.stopScanAnimation() 156 | 157 | scanObj?.stop() 158 | } 159 | 160 | open func openPhotoAlbum() 161 | { 162 | LBXPermissions.authorizePhotoWith { [weak self] (granted) in 163 | 164 | let picker = UIImagePickerController() 165 | 166 | picker.sourceType = UIImagePickerController.SourceType.photoLibrary 167 | 168 | picker.delegate = self; 169 | 170 | picker.allowsEditing = true 171 | 172 | self?.present(picker, animated: true, completion: nil) 173 | } 174 | } 175 | 176 | //MARK: -----相册选择图片识别二维码 (条形码没有找到系统方法) 177 | public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 178 | { 179 | picker.dismiss(animated: true, completion: nil) 180 | 181 | var image: UIImage? = info[UIImagePickerController.InfoKey.editedImage.rawValue] as? UIImage 182 | 183 | if (image == nil ) { 184 | image = info[UIImagePickerController.InfoKey.originalImage.rawValue] as? UIImage 185 | } 186 | 187 | if(image != nil) 188 | { 189 | let arrayResult = LBXScanWrapper.recognizeQRImage(image: image!) 190 | if arrayResult.count > 0 191 | { 192 | handleCodeResult(arrayResult: arrayResult) 193 | return 194 | } 195 | } 196 | 197 | showMsg(title: nil, message: NSLocalizedString("Identify failed", comment: "Identify failed")) 198 | } 199 | 200 | func showMsg(title:String?,message:String?) 201 | { 202 | 203 | let alertController = UIAlertController(title: nil, message:message, preferredStyle: UIAlertController.Style.alert) 204 | let alertAction = UIAlertAction(title: NSLocalizedString("OK", comment: "OK"), style: UIAlertAction.Style.default) { (alertAction) in 205 | 206 | // if let strongSelf = self 207 | // { 208 | // strongSelf.startScan() 209 | // } 210 | } 211 | 212 | alertController.addAction(alertAction) 213 | present(alertController, animated: true, completion: nil) 214 | } 215 | deinit 216 | { 217 | // print("LBXScanViewController deinit") 218 | } 219 | 220 | } 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebView+WKWebView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebView+WKWebView.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/11/9. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | // MARK: - WKWebView 13 | extension GTMWebViewController: WKNavigationDelegate { 14 | 15 | public var wkWebView: WKWebView? { 16 | return self.webView 17 | } 18 | 19 | func setupWkWebView() { 20 | 21 | // init weakScriptHandler 22 | self.weakScriptHandler = WeakScriptMessageHandler(self) 23 | 24 | let configuration = WKWebViewConfiguration() // 配置 25 | configuration.processPool = GTMWebViewController.sharedProcessPool // WkWebView 实例间共享Cookies 26 | configuration.preferences.minimumFontSize = 1 27 | configuration.preferences.javaScriptEnabled = true 28 | configuration.allowsInlineMediaPlayback = true // 允许视频播放回退 29 | configuration.userContentController = WKUserContentController() // 交互对象 30 | configuration.userContentController.add(self.weakScriptHandler, name: "GTMWebKitAPI") 31 | let wkWebV = WKWebView(frame: self.view.bounds, configuration: configuration) // WKWebView 32 | wkWebV.uiDelegate = self 33 | wkWebV.navigationDelegate = self 34 | wkWebV.allowsBackForwardNavigationGestures = true 35 | self.view.addSubview(wkWebV) 36 | self.webView = wkWebV 37 | } 38 | 39 | // MARK: - KVO 40 | 41 | public func wkwebv_addObservers() { 42 | wkWebView?.addObserver(self, forKeyPath: "loading", options: .new, context: nil) 43 | wkWebView?.addObserver(self, forKeyPath: "title", options: .new, context: nil) 44 | wkWebView?.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) 45 | } 46 | 47 | public func wkwebv_removeObservers() { 48 | wkWebView?.removeObserver(self, forKeyPath: "loading") 49 | wkWebView?.removeObserver(self, forKeyPath: "title") 50 | wkWebView?.removeObserver(self, forKeyPath: "estimatedProgress") 51 | } 52 | 53 | public func wkwebv_observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 54 | if keyPath == "loading" { 55 | println("loading") 56 | } else if keyPath == "title" { 57 | if isUseWebTitle { 58 | self.title = self.webView.title 59 | } 60 | self.updateButtonItems() // 更新导航按钮状态 61 | } else if keyPath == "estimatedProgress" { 62 | self.progressView?.isHidden = false 63 | self.progressView?.setProgress(Float(wkWebView!.estimatedProgress), animated: true) 64 | } 65 | 66 | // 已经完成加载时,我们就可以做我们的事了 67 | if !wkWebView!.isLoading { 68 | self.progressView?.setProgress(0, animated: false) 69 | self.progressView?.isHidden = true 70 | } 71 | } 72 | 73 | // MARK: - WKNavigationDelegate 74 | public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { 75 | self.webWillLoad() 76 | } 77 | // MARK: Initiating the Navigation 78 | public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { 79 | 80 | } 81 | // MARK: Responding to Server Actions 82 | public func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) { 83 | 84 | println(navigation.description) 85 | } 86 | // Authentication Challenges 87 | public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 88 | 89 | var disposition: URLSession.AuthChallengeDisposition = URLSession.AuthChallengeDisposition.performDefaultHandling 90 | var credential: URLCredential? 91 | 92 | if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 93 | disposition = URLSession.AuthChallengeDisposition.useCredential 94 | credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) 95 | } 96 | 97 | completionHandler(disposition, credential) 98 | } 99 | // MARK: Tracking Load Progress 100 | public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { 101 | println("webView didFinish") 102 | // 共享 Cookies 103 | if isNeedShareCookies { 104 | if #available(iOS 11.0, *) { 105 | GTMWebViewCookies.shareWebViewCookies(url: webView.url!) 106 | } 107 | } 108 | self.webDidLoad() 109 | } 110 | public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { 111 | // 如果出现频繁刷新的情况,说明页面占用内存确实过大,需要前端作优化处理 112 | if self.isTreatMemeryCrushWithReload { 113 | webView.reload() // 解决内存消耗过度出现白屏的问题 114 | } 115 | } 116 | // MARK: Permitting Navigation 117 | public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 118 | 119 | // Disable all the '_blank' target in page's target 120 | if let frame = navigationAction.targetFrame { 121 | if frame.isMainFrame { 122 | webView.evaluateJavaScript("var a = document.getElementsByTagName('a');for(var i=0;i Void) { 164 | 165 | println("decidePolicyFor navigationResponse") 166 | decisionHandler(.allow) 167 | } 168 | // MARK: Reacting to Errors 169 | public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { 170 | let nserror = error as NSError 171 | if nserror.code == NSURLErrorCancelled { 172 | return 173 | } 174 | self.webDidLoadFail(error: nserror) 175 | } 176 | public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { 177 | let nserror = error as NSError 178 | if nserror.code == NSURLErrorCancelled { 179 | return 180 | } 181 | self.webDidLoadFail(error: nserror) 182 | } 183 | } 184 | 185 | extension GTMWebViewController: WKScriptMessageHandler { 186 | 187 | public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { 188 | if message.name == "GTMWebKitAPI" { 189 | if let body = message.body as? Dictionary { 190 | let method = body["method"] as! String 191 | println("\(body)") 192 | println("\(method)") 193 | 194 | if let handler = self.scriptHandlers[method] { 195 | handler(body["body"]) 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanView.swift 3 | // swiftScan 4 | // 5 | // Created by xialibing on 15/12/8. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class LBXScanView: UIView 12 | { 13 | //扫码区域各种参数 14 | var viewStyle:LBXScanViewStyle = LBXScanViewStyle() 15 | 16 | //扫码区域 17 | var scanRetangleRect:CGRect = CGRect.zero 18 | 19 | //线条扫码动画封装 20 | var scanLineAnimation:LBXScanLineAnimation? 21 | 22 | //网格扫码动画封装 23 | var scanNetAnimation:LBXScanNetAnimation? 24 | 25 | //线条在中间位置,不移动 26 | var scanLineStill:UIImageView? 27 | 28 | //启动相机时 菊花等待 29 | var activityView:UIActivityIndicatorView? 30 | 31 | //启动相机中的提示文字 32 | var labelReadying:UILabel? 33 | 34 | //记录动画状态 35 | var isAnimationing:Bool = false 36 | 37 | /** 38 | 初始化扫描界面 39 | - parameter frame: 界面大小,一般为视频显示区域 40 | - parameter vstyle: 界面效果参数 41 | 42 | - returns: instancetype 43 | */ 44 | public init(frame:CGRect, vstyle:LBXScanViewStyle ) 45 | { 46 | viewStyle = vstyle 47 | 48 | switch (viewStyle.anmiationStyle) 49 | { 50 | case LBXScanViewAnimationStyle.LineMove: 51 | scanLineAnimation = LBXScanLineAnimation.instance() 52 | break 53 | case LBXScanViewAnimationStyle.NetGrid: 54 | scanNetAnimation = LBXScanNetAnimation.instance() 55 | break 56 | case LBXScanViewAnimationStyle.LineStill: 57 | scanLineStill = UIImageView() 58 | scanLineStill?.image = viewStyle.animationImage 59 | break 60 | 61 | 62 | default: 63 | break 64 | } 65 | 66 | var frameTmp = frame; 67 | frameTmp.origin = CGPoint.zero 68 | 69 | super.init(frame: frameTmp) 70 | 71 | backgroundColor = UIColor.clear 72 | } 73 | 74 | override init(frame: CGRect) { 75 | 76 | var frameTmp = frame; 77 | frameTmp.origin = CGPoint.zero 78 | 79 | super.init(frame: frameTmp) 80 | 81 | backgroundColor = UIColor.clear 82 | } 83 | 84 | required public init?(coder aDecoder: NSCoder) 85 | { 86 | self.init() 87 | 88 | } 89 | 90 | deinit 91 | { 92 | if (scanLineAnimation != nil) 93 | { 94 | scanLineAnimation!.stopStepAnimating() 95 | } 96 | if (scanNetAnimation != nil) 97 | { 98 | scanNetAnimation!.stopStepAnimating() 99 | } 100 | 101 | 102 | // print("LBXScanView deinit") 103 | } 104 | 105 | 106 | /** 107 | * 开始扫描动画 108 | */ 109 | func startScanAnimation() 110 | { 111 | if isAnimationing 112 | { 113 | return 114 | } 115 | 116 | isAnimationing = true 117 | 118 | let cropRect:CGRect = getScanRectForAnimation() 119 | 120 | switch viewStyle.anmiationStyle 121 | { 122 | case LBXScanViewAnimationStyle.LineMove: 123 | 124 | // print(NSStringFromCGRect(cropRect)) 125 | 126 | scanLineAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage ) 127 | break 128 | case LBXScanViewAnimationStyle.NetGrid: 129 | 130 | scanNetAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage ) 131 | break 132 | case LBXScanViewAnimationStyle.LineStill: 133 | 134 | let stillRect = CGRect(x: cropRect.origin.x+20, 135 | y: cropRect.origin.y + cropRect.size.height/2, 136 | width: cropRect.size.width-40, 137 | height: 2); 138 | self.scanLineStill?.frame = stillRect 139 | 140 | self.addSubview(scanLineStill!) 141 | self.scanLineStill?.isHidden = false 142 | 143 | break 144 | 145 | default: break 146 | 147 | } 148 | } 149 | 150 | /** 151 | * 开始扫描动画 152 | */ 153 | func stopScanAnimation() 154 | { 155 | isAnimationing = false 156 | 157 | switch viewStyle.anmiationStyle 158 | { 159 | case LBXScanViewAnimationStyle.LineMove: 160 | 161 | scanLineAnimation?.stopStepAnimating() 162 | break 163 | case LBXScanViewAnimationStyle.NetGrid: 164 | 165 | scanNetAnimation?.stopStepAnimating() 166 | break 167 | case LBXScanViewAnimationStyle.LineStill: 168 | self.scanLineStill?.isHidden = true 169 | 170 | break 171 | 172 | default: break 173 | 174 | } 175 | } 176 | 177 | 178 | 179 | // Only override drawRect: if you perform custom drawing. 180 | // An empty implementation adversely affects performance during animation. 181 | override open func draw(_ rect: CGRect) 182 | { 183 | // Drawing code 184 | drawScanRect() 185 | } 186 | 187 | //MARK:----- 绘制扫码效果----- 188 | func drawScanRect() 189 | { 190 | let XRetangleLeft = viewStyle.xScanRetangleOffset 191 | var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2.0, height: self.frame.size.width - XRetangleLeft*2.0) 192 | if viewStyle.whRatio != 1.0 193 | { 194 | let w = sizeRetangle.width; 195 | var h:CGFloat = w / viewStyle.whRatio 196 | 197 | let hInt:Int = Int(h) 198 | h = CGFloat(hInt) 199 | 200 | sizeRetangle = CGSize(width: w, height: h) 201 | } 202 | 203 | //扫码区域Y轴最小坐标 204 | let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset 205 | let YMaxRetangle = YMinRetangle + sizeRetangle.height 206 | let XRetangleRight = self.frame.size.width - XRetangleLeft 207 | 208 | 209 | // print("frame:%@",NSStringFromCGRect(self.frame)) 210 | 211 | let context = UIGraphicsGetCurrentContext()! 212 | 213 | 214 | //非扫码区域半透明 215 | //设置非识别区域颜色 216 | context.setFillColor(viewStyle.color_NotRecoginitonArea.cgColor) 217 | //填充矩形 218 | //扫码区域上面填充 219 | var rect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: YMinRetangle) 220 | context.fill(rect) 221 | 222 | 223 | //扫码区域左边填充 224 | rect = CGRect(x: 0, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height) 225 | context.fill(rect) 226 | 227 | //扫码区域右边填充 228 | rect = CGRect(x: XRetangleRight, y: YMinRetangle, width: XRetangleLeft,height: sizeRetangle.height) 229 | context.fill(rect) 230 | 231 | //扫码区域下面填充 232 | rect = CGRect(x: 0, y: YMaxRetangle, width: self.frame.size.width,height: self.frame.size.height - YMaxRetangle) 233 | context.fill(rect) 234 | //执行绘画 235 | context.strokePath() 236 | 237 | 238 | if viewStyle.isNeedShowRetangle 239 | { 240 | //中间画矩形(正方形) 241 | context.setStrokeColor(viewStyle.colorRetangleLine.cgColor) 242 | context.setLineWidth(1); 243 | 244 | context.addRect(CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)) 245 | 246 | //CGContextMoveToPoint(context, XRetangleLeft, YMinRetangle); 247 | //CGContextAddLineToPoint(context, XRetangleLeft+sizeRetangle.width, YMinRetangle); 248 | 249 | context.strokePath() 250 | 251 | } 252 | scanRetangleRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) 253 | 254 | 255 | //画矩形框4格外围相框角 256 | 257 | //相框角的宽度和高度 258 | let wAngle = viewStyle.photoframeAngleW; 259 | let hAngle = viewStyle.photoframeAngleH; 260 | 261 | //4个角的 线的宽度 262 | let linewidthAngle = viewStyle.photoframeLineW;// 经验参数:6和4 263 | 264 | //画扫码矩形以及周边半透明黑色坐标参数 265 | var diffAngle = linewidthAngle/3; 266 | diffAngle = linewidthAngle / 2; //框外面4个角,与框有缝隙 267 | diffAngle = linewidthAngle/2; //框4个角 在线上加4个角效果 268 | diffAngle = 0;//与矩形框重合 269 | 270 | switch viewStyle.photoframeAngleStyle 271 | { 272 | case LBXScanViewPhotoframeAngleStyle.Outer: 273 | diffAngle = linewidthAngle/3//框外面4个角,与框紧密联系在一起 274 | 275 | case LBXScanViewPhotoframeAngleStyle.On: 276 | diffAngle = 0 277 | 278 | case LBXScanViewPhotoframeAngleStyle.Inner: 279 | diffAngle = -viewStyle.photoframeLineW/2 280 | } 281 | 282 | context.setStrokeColor(viewStyle.colorAngle.cgColor); 283 | context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0); 284 | 285 | // Draw them with a 2.0 stroke width so they are a bit more visible. 286 | context.setLineWidth(linewidthAngle); 287 | 288 | 289 | // 290 | let leftX = XRetangleLeft - diffAngle 291 | let topY = YMinRetangle - diffAngle 292 | let rightX = XRetangleRight + diffAngle 293 | let bottomY = YMaxRetangle + diffAngle 294 | 295 | //左上角水平线 296 | context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: topY)) 297 | context.addLine(to: CGPoint(x: leftX + wAngle, y: topY)) 298 | 299 | //左上角垂直线 300 | context.move(to: CGPoint(x: leftX, y: topY-linewidthAngle/2)) 301 | context.addLine(to: CGPoint(x: leftX, y: topY+hAngle)) 302 | 303 | //左下角水平线 304 | context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: bottomY)) 305 | context.addLine(to: CGPoint(x: leftX + wAngle, y: bottomY)) 306 | 307 | //左下角垂直线 308 | context.move(to: CGPoint(x: leftX, y: bottomY+linewidthAngle/2)) 309 | context.addLine(to: CGPoint(x: leftX, y: bottomY - hAngle)) 310 | 311 | //右上角水平线 312 | context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: topY)) 313 | context.addLine(to: CGPoint(x: rightX - wAngle, y: topY)) 314 | 315 | //右上角垂直线 316 | context.move(to: CGPoint(x: rightX, y: topY-linewidthAngle/2)) 317 | context.addLine(to: CGPoint(x: rightX, y: topY + hAngle)) 318 | 319 | // 右下角水平线 320 | context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: bottomY)) 321 | context.addLine(to: CGPoint(x: rightX - wAngle, y: bottomY)) 322 | 323 | //右下角垂直线 324 | context.move(to: CGPoint(x: rightX, y: bottomY+linewidthAngle/2)) 325 | context.addLine(to: CGPoint(x: rightX, y: bottomY - hAngle)) 326 | 327 | context.strokePath() 328 | } 329 | 330 | func getScanRectForAnimation() -> CGRect 331 | { 332 | let XRetangleLeft = viewStyle.xScanRetangleOffset 333 | var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2) 334 | 335 | if viewStyle.whRatio != 1 336 | { 337 | let w = sizeRetangle.width 338 | var h = w / viewStyle.whRatio 339 | 340 | 341 | let hInt:Int = Int(h) 342 | h = CGFloat(hInt) 343 | 344 | sizeRetangle = CGSize(width: w, height: h) 345 | } 346 | 347 | //扫码区域Y轴最小坐标 348 | let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset 349 | //扫码区域坐标 350 | let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) 351 | 352 | return cropRect; 353 | } 354 | 355 | //根据矩形区域,获取识别区域 356 | static func getScanRectWithPreView(preView:UIView, style:LBXScanViewStyle) -> CGRect 357 | { 358 | let XRetangleLeft = style.xScanRetangleOffset; 359 | var sizeRetangle = CGSize(width: preView.frame.size.width - XRetangleLeft*2, height: preView.frame.size.width - XRetangleLeft*2) 360 | 361 | if style.whRatio != 1 362 | { 363 | let w = sizeRetangle.width 364 | var h = w / style.whRatio 365 | 366 | let hInt:Int = Int(h) 367 | h = CGFloat(hInt) 368 | 369 | sizeRetangle = CGSize(width: w, height: h) 370 | } 371 | 372 | //扫码区域Y轴最小坐标 373 | let YMinRetangle = preView.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset 374 | //扫码区域坐标 375 | let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) 376 | 377 | 378 | //计算兴趣区域 379 | var rectOfInterest:CGRect 380 | 381 | //ref:http://www.cocoachina.com/ios/20141225/10763.html 382 | let size = preView.bounds.size; 383 | let p1 = size.height/size.width; 384 | 385 | let p2:CGFloat = 1920.0/1080.0 //使用了1080p的图像输出 386 | if p1 < p2 { 387 | let fixHeight = size.width * 1920.0 / 1080.0; 388 | let fixPadding = (fixHeight - size.height)/2; 389 | rectOfInterest = CGRect(x: (cropRect.origin.y + fixPadding)/fixHeight, 390 | y: cropRect.origin.x/size.width, 391 | width: cropRect.size.height/fixHeight, 392 | height: cropRect.size.width/size.width) 393 | 394 | 395 | } else { 396 | let fixWidth = size.height * 1080.0 / 1920.0; 397 | let fixPadding = (fixWidth - size.width)/2; 398 | rectOfInterest = CGRect(x: cropRect.origin.y/size.height, 399 | y: (cropRect.origin.x + fixPadding)/fixWidth, 400 | width: cropRect.size.height/size.height, 401 | height: cropRect.size.width/fixWidth) 402 | } 403 | return rectOfInterest 404 | } 405 | 406 | func getRetangeSize()->CGSize 407 | { 408 | let XRetangleLeft = viewStyle.xScanRetangleOffset 409 | 410 | var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2) 411 | 412 | let w = sizeRetangle.width; 413 | var h = w / viewStyle.whRatio; 414 | 415 | 416 | let hInt:Int = Int(h) 417 | h = CGFloat(hInt) 418 | 419 | sizeRetangle = CGSize(width: w, height: h) 420 | 421 | return sizeRetangle 422 | } 423 | 424 | func deviceStartReadying(readyStr:String) 425 | { 426 | let XRetangleLeft = viewStyle.xScanRetangleOffset 427 | 428 | let sizeRetangle = getRetangeSize() 429 | 430 | //扫码区域Y轴最小坐标 431 | let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset 432 | 433 | //设备启动状态提示 434 | if (activityView == nil) 435 | { 436 | self.activityView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30)) 437 | 438 | activityView?.center = CGPoint(x: XRetangleLeft + sizeRetangle.width/2 - 50, y: YMinRetangle + sizeRetangle.height/2) 439 | activityView?.style = UIActivityIndicatorView.Style.whiteLarge 440 | 441 | addSubview(activityView!) 442 | 443 | 444 | let labelReadyRect = CGRect(x: activityView!.frame.origin.x + activityView!.frame.size.width + 10, y: activityView!.frame.origin.y, width: 100, height: 30); 445 | //print("%@",NSStringFromCGRect(labelReadyRect)) 446 | self.labelReadying = UILabel(frame: labelReadyRect) 447 | labelReadying?.text = readyStr 448 | labelReadying?.backgroundColor = UIColor.clear 449 | labelReadying?.textColor = UIColor.white 450 | labelReadying?.font = UIFont.systemFont(ofSize: 18.0) 451 | addSubview(labelReadying!) 452 | } 453 | 454 | addSubview(labelReadying!) 455 | activityView?.startAnimating() 456 | 457 | } 458 | 459 | func deviceStopReadying() 460 | { 461 | if activityView != nil 462 | { 463 | activityView?.stopAnimating() 464 | activityView?.removeFromSuperview() 465 | labelReadying?.removeFromSuperview() 466 | 467 | activityView = nil 468 | labelReadying = nil 469 | 470 | } 471 | } 472 | 473 | } 474 | -------------------------------------------------------------------------------- /GTMWebKit/GTMWebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GTMWebViewController.swift 3 | // GTMWebKit 4 | // 5 | // Created by luoyang on 2017/10/25. 6 | // Copyright © 2017年 yang. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | public enum GTMWK_NavigationType { 13 | case navbar // web导航控制按钮放在导航栏 14 | case toolbar // web导航控制按钮放在底部工具栏 15 | case both // 同时使用两种导航按钮 16 | case none // 不使用web导航按钮 17 | } 18 | 19 | open class GTMWebViewController: UIViewController, GTMAlertable { 20 | 21 | // MARK: - 通用属性 22 | public var webView: WKWebView! 23 | public var isShowCloseItem = true // 是否显示关闭按钮(navigType == .navbar 时使用) 24 | public var isShowToolbar = true // 是否显示工具栏(navigType == .toolbar 时使用) 25 | public var isNeedShareCookies = false // 是否需要共享cookies 26 | public var isUseWebTitle = true // 是否使用网页的title 27 | public var backIconName: String? // 返回按钮图标,可自行设置 28 | public var view404: GTMWebErrorView! // 资源不存在的时候展示的UI,可自定义 29 | public var netErrorView: GTMWebErrorView! // 网络错误的时候展示的UI,可自定义 30 | 31 | 32 | // Private props 33 | private var webUrl: URL? 34 | /// 网页加载进度指示器 35 | var progressView: UIProgressView? 36 | 37 | private var navigType: GTMWK_NavigationType // 控制网页导航的方式(导航栏,工具栏) 38 | // MARK: Navigation Items 39 | var navbarItemBack: UIBarButtonItem? 40 | var navbarItemClose: UIBarButtonItem? 41 | // MARK: ToolBar Items 42 | var toolbarItemBack: UIBarButtonItem? 43 | var toolbarItemForward: UIBarButtonItem? 44 | var toolbarItemRefresh: UIBarButtonItem? 45 | var toolbarItemAction: UIBarButtonItem? 46 | 47 | 48 | // MARK: - WKWebView 属性 49 | // 是否使用reload的方式处理内存占用过大造成的白屏问题 50 | // 当打开的时候如果某个页面出现频繁刷新的情况,建议优化网页 51 | public var isTreatMemeryCrushWithReload = false 52 | /// 弱代理(处理内存泄漏的问题) 53 | public var weakScriptHandler: WeakScriptMessageHandler! 54 | /// 提供给JS的API容器 55 | var scriptHandlers: [String: (_ body: Any?) -> Void] = [:] 56 | // Cookies处理属性 57 | public static let sharedProcessPool = WKProcessPool() 58 | 59 | 60 | // MARK: - Life Cycle 61 | 62 | var urlCovertible: URLConvertible? 63 | public init(with url: URLConvertible, navigType type: GTMWK_NavigationType) { 64 | self.navigType = type 65 | super.init(nibName: nil, bundle: nil) 66 | self.urlCovertible = url 67 | self.webUrl = url.url() 68 | self.view404 = GTMWebNetErrorView("404") 69 | self.netErrorView = GTMWebNetErrorView("nosingle") 70 | } 71 | 72 | /// 如果当前不在根页面,回到并重新加载根页面。否则什么都不做 73 | public func reloadToRootPage() -> Bool { 74 | guard let url = self.urlCovertible?.url() else { 75 | return false 76 | } 77 | if self.webView.url!.absoluteString == url.absoluteString { 78 | return false 79 | } 80 | self.loadWithUrl(url: url) 81 | return true 82 | } 83 | /// reload当前页面 84 | public func reload() { 85 | self.webView.reload() 86 | } 87 | 88 | public required init?(coder aDecoder: NSCoder) { 89 | fatalError("init(coder:) has not been implemented") 90 | } 91 | 92 | var originToolBarState: Bool? 93 | var originNavBarState: Bool? 94 | override open func viewDidLoad() { 95 | super.viewDidLoad() 96 | 97 | self.setup() 98 | self.loadWebPage() // 加载网页 99 | 100 | originToolBarState = self.navigationController?.isToolbarHidden 101 | originNavBarState = self.navigationController?.isNavigationBarHidden 102 | } 103 | 104 | override open func viewWillAppear(_ animated: Bool) { 105 | super.viewWillAppear(animated) 106 | 107 | switch navigType { 108 | case .navbar: 109 | self.navigationController?.setToolbarHidden(true, animated: animated) 110 | case .toolbar: 111 | self.navigationController?.setNavigationBarHidden(false, animated: animated) 112 | self.navigationController?.setToolbarHidden(false, animated: animated) 113 | case .both: 114 | self.navigationController?.setToolbarHidden(false, animated: animated) 115 | self.navigationController?.setNavigationBarHidden(false, animated: animated) 116 | case .none: 117 | self.navigationController?.setToolbarHidden(true, animated: animated) 118 | self.navigationController?.setNavigationBarHidden(true, animated: animated) 119 | } 120 | // 121 | self.updateButtonItems() // 更新导航按钮状态 122 | } 123 | 124 | override open func viewWillDisappear(_ animated: Bool) { 125 | super.viewWillDisappear(animated) 126 | guard originNavBarState != nil else { 127 | return 128 | } 129 | self.navigationController?.setToolbarHidden(originToolBarState!, animated: animated) 130 | self.navigationController?.setNavigationBarHidden(originNavBarState!, animated: animated) 131 | } 132 | 133 | private func setup() { 134 | // 导航栏不透明 135 | self.navigationController?.navigationBar.isTranslucent = false 136 | 137 | /// init sub views 138 | 139 | // web view 140 | self.setupWkWebView() 141 | 142 | self.addObservers() // KVO 143 | 144 | // progress view 145 | self.progressView = UIProgressView(progressViewStyle: .default) 146 | self.progressView?.frame = self.view.bounds 147 | self.progressView?.trackTintColor = UIColor.white 148 | self.progressView?.tintColor = UIColor.gray 149 | self.view.addSubview(self.progressView!) 150 | 151 | // init button items 152 | self.initButtonItems() 153 | 154 | // 共享 Cookies 155 | if isNeedShareCookies { 156 | if #available(iOS 11.0, *) { 157 | GTMWebViewCookies.shareNativeCookies(url: self.webUrl!) 158 | } 159 | } 160 | 161 | } 162 | 163 | deinit { 164 | self.removeObservers() 165 | println("GTMWebViewController deinit") 166 | } 167 | 168 | // MARK: - Public 169 | /// 注册API 170 | public func registApi(method methodName: String, with handler: @escaping (_ body: Any?) -> Void) { 171 | // 添加到容器 172 | self.scriptHandlers[methodName] = handler 173 | } 174 | /// 注入JS 175 | public func injectUserScript(script: WKUserScript) { 176 | self.webView.configuration.userContentController.addUserScript(script) 177 | } 178 | 179 | // MARK: - Private 180 | func loadWebPage() { 181 | guard let url = self.webUrl else { 182 | println("没有为GTMWebViewController提供有效的网页URL") 183 | return 184 | } 185 | 186 | self.loadWithUrl(url: url) 187 | } 188 | 189 | func loadWithUrl(url: URL) { 190 | webView.load(URLRequest(url: url)) 191 | } 192 | 193 | // MARK: - 钩子函数 194 | // web加载完成(相当于抽象函数) 195 | open func webWillLoad() { } 196 | open func webDidLoad() { 197 | self.view404.removeFromSuperview() 198 | self.netErrorView.removeFromSuperview() 199 | } 200 | // 错误处理 201 | open func webDidLoadFail(error: NSError) { 202 | self.view404.removeFromSuperview() 203 | self.netErrorView.removeFromSuperview() 204 | 205 | if error.code == NSURLErrorCannotFindHost || 206 | error.code == NSURLErrorCannotConnectToHost || 207 | error.code == NSURLErrorResourceUnavailable { 208 | view404.reloadHandler = { [weak self] in 209 | self?.loadWebPage() 210 | } 211 | self.view.addSubview(view404) 212 | } else { 213 | netErrorView.reloadHandler = { [weak self] in 214 | self?.loadWebPage() 215 | } 216 | self.view.addSubview(netErrorView) 217 | } 218 | } 219 | 220 | // MARK: - KVO 221 | func addObservers() { 222 | self.wkwebv_addObservers() 223 | } 224 | func removeObservers() { 225 | self.wkwebv_removeObservers() 226 | } 227 | open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 228 | self.wkwebv_observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 229 | } 230 | 231 | } 232 | 233 | extension GTMWebViewController { 234 | // MARK: - Web Navigation Items 235 | 236 | fileprivate func initButtonItems() { 237 | 238 | switch navigType { 239 | case .navbar: 240 | self.initNavigationBarItems() 241 | case .toolbar: 242 | self.initBottomBarItems() 243 | case .both: 244 | self.initNavigationBarItems() 245 | self.initBottomBarItems() 246 | case .none: 247 | break 248 | } 249 | // done 250 | if let navigationC = self.navigationController { 251 | if navigationC.isBeingPresented { 252 | // done item 253 | let title = NSLocalizedString("done", bundle: self.sourceBundle, comment: "") 254 | let doneButton = UIButton.init(type: .custom) 255 | doneButton.frame = CGRect(x: 0, y: 0, width: 40, height: 44) 256 | doneButton.setTitle(title, for: .normal) 257 | doneButton.titleLabel?.font = UIFont.systemFont(ofSize: 15) 258 | doneButton.setTitleColor(self.navigationController?.navigationBar.tintColor, for: .normal) 259 | doneButton.addTarget(self, action: #selector(onNavigationDone), for: .touchUpInside) 260 | let doneButtonItem = UIBarButtonItem.init(customView: doneButton) 261 | self.navigationItem.rightBarButtonItem = doneButtonItem 262 | } 263 | } 264 | // 更新状态 265 | self.updateButtonItems() 266 | } 267 | 268 | // MARK: - Navigation Events 269 | 270 | @objc func onNavigationDone() { 271 | self.dismiss(animated: true, completion: nil) 272 | } 273 | 274 | @objc func onNavigationBack() { 275 | if webView.canGoBack { 276 | self.onWebpageBack() 277 | } else { 278 | self.navigationController?.popViewController(animated: true) 279 | } 280 | } 281 | @objc func onNavigationClose() { 282 | self.navigationController?.popViewController(animated: true) 283 | } 284 | 285 | 286 | @objc func onToolbarBack() { 287 | self.onWebpageBack() 288 | } 289 | @objc func onToolbarForward() { 290 | webView.goForward() 291 | } 292 | @objc func onToolbarRefresh() { 293 | webView.reload() 294 | } 295 | @objc func onToolbarAction() { 296 | if let url = webView.url { 297 | let activityVC = UIActivityViewController.init(activityItems: [url], applicationActivities: nil) 298 | self.present(activityVC, animated: true, completion: nil) 299 | } 300 | } 301 | 302 | func onWebpageBack() { 303 | webView.goBack() 304 | self.updateButtonItems() 305 | } 306 | 307 | // MARK: Navigation Items 308 | 309 | private func initNavigationBarItems() { 310 | let bundle = sourceBundle 311 | var iconBack = UIImage.init(named: "nav_back", in: bundle, compatibleWith: nil) 312 | if let image = self.navigationController?.navigationBar.backIndicatorImage { 313 | iconBack = image 314 | } 315 | let buttonBack = UIButton.init(type: .custom) 316 | buttonBack.setImage(iconBack, for: .normal) 317 | buttonBack.sizeToFit() 318 | // buttonBack.frame = CGRect(x: 0, y: 0, width: 20, height: 44) 319 | // if #available(iOS 11.0, *) { 320 | // buttonBack.imageEdgeInsets = UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4) 321 | // } else { 322 | // buttonBack.imageEdgeInsets = UIEdgeInsets(top: 12, left: -8, bottom: 12, right: 8) 323 | // } 324 | 325 | if let backIcon = backIconName { 326 | iconBack = UIImage(named: backIcon) 327 | buttonBack.setImage(iconBack, for: .normal) 328 | } 329 | // back item 330 | buttonBack.addTarget(self, action: #selector(onNavigationBack), for: .touchUpInside) 331 | self.navbarItemBack = UIBarButtonItem.init(customView: buttonBack) 332 | // self.navigationItem.setLeftBarButtonItems([self.navbarItemBack!], animated: false) 333 | 334 | // close item 335 | let title = NSLocalizedString("close", bundle: bundle, comment: "") 336 | let closeButton = UIButton.init(type: .custom) 337 | closeButton.setTitle(title, for: .normal) 338 | closeButton.titleLabel?.font = UIFont.systemFont(ofSize: 15) 339 | closeButton.setTitleColor(self.navigationController?.navigationBar.tintColor, for: .normal) 340 | closeButton.frame = CGRect(x: 0, y: 0, width: 40, height: 44) 341 | if #available(iOS 11.0, *) { 342 | closeButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 10) 343 | } else { 344 | closeButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: -5, bottom: 0, right: 5) 345 | } 346 | 347 | closeButton.addTarget(self, action: #selector(onNavigationClose), for: .touchUpInside) 348 | self.navbarItemClose = UIBarButtonItem.init(customView: closeButton) 349 | } 350 | private func initBottomBarItems() { 351 | let bundle = sourceBundle 352 | var iconBack = UIImage.init(named: "back", in: bundle, compatibleWith: nil) 353 | if let image = self.navigationController?.navigationBar.backIndicatorImage { 354 | iconBack = image 355 | } 356 | let buttonBack = UIButton.init(type: .system) 357 | buttonBack.setImage(iconBack, for: .normal) 358 | buttonBack.sizeToFit() 359 | buttonBack.frame = CGRect(x: 0, y: 0, width: 20, height: 44) 360 | if #available(iOS 11.0, *) { 361 | buttonBack.imageEdgeInsets = UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4) 362 | } else { 363 | buttonBack.imageEdgeInsets = UIEdgeInsets(top: 12, left: -8, bottom: 12, right: 8) 364 | } 365 | // back item 366 | buttonBack.addTarget(self, action: #selector(onToolbarBack), for: .touchUpInside) 367 | self.toolbarItemBack = UIBarButtonItem.init(customView: buttonBack) 368 | // forward item 369 | let iconForward = UIImage.init(named: "forward", in: bundle, compatibleWith: nil) 370 | let buttonForward = UIButton.init(type: .custom) 371 | buttonForward.setImage(iconForward, for: .normal) 372 | buttonForward.frame = CGRect(x: 0, y: 0, width: 20, height: 44) 373 | if #available(iOS 11.0, *) { 374 | buttonForward.imageEdgeInsets = UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4) 375 | } else { 376 | buttonForward.imageEdgeInsets = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0) 377 | } 378 | buttonForward.addTarget(self, action: #selector(onToolbarForward), for: .touchUpInside) 379 | self.toolbarItemForward = UIBarButtonItem.init(customView: buttonForward) 380 | // refresh item 381 | self.toolbarItemRefresh = UIBarButtonItem.init(barButtonSystemItem: .refresh, target: self, action: #selector(onToolbarRefresh)) 382 | // action item 383 | self.toolbarItemAction = UIBarButtonItem.init(barButtonSystemItem: .action, target: self, action: #selector(onToolbarAction)) 384 | } 385 | 386 | func updateButtonItems() { 387 | switch navigType { 388 | case .navbar: 389 | self.updateNavbarButtonItems() 390 | case .toolbar: 391 | self.updateToolbarButtonItems() 392 | case .both: 393 | self.updateNavbarButtonItems() 394 | self.updateToolbarButtonItems() 395 | default: 396 | break 397 | } 398 | } 399 | private func updateNavbarButtonItems() { 400 | self.navigationItem.setLeftBarButtonItems(nil, animated: false) 401 | if webView.canGoBack { 402 | self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false 403 | if let navigC = self.navigationController { 404 | var items: [UIBarButtonItem] = self.navigationItem.leftBarButtonItems ?? [] 405 | if navigC.viewControllers.count > 1 { 406 | if self.isShowCloseItem { 407 | items.insert(self.navbarItemBack!, at: 0) 408 | items.append(self.navbarItemClose!) 409 | self.navigationItem.setLeftBarButtonItems(items, animated: false) 410 | } else { 411 | for (i, item) in items.enumerated() { 412 | if item == self.navbarItemClose! { 413 | items.remove(at: i) 414 | } 415 | } 416 | self.navigationItem.setLeftBarButtonItems(items, animated: false) 417 | } 418 | } else { 419 | if self.isShowCloseItem { 420 | items.insert(self.navbarItemBack!, at: 0) 421 | items.append(self.navbarItemClose!) 422 | self.navigationItem.setLeftBarButtonItems(items, animated: false) 423 | } else { 424 | for (i, item) in items.enumerated() { 425 | if item == self.navbarItemClose! { 426 | items.remove(at: i) 427 | } 428 | } 429 | self.navigationItem.setLeftBarButtonItems(items, animated: false) 430 | } 431 | } 432 | } 433 | } else { 434 | var items: [UIBarButtonItem] = self.navigationItem.leftBarButtonItems ?? [] 435 | items.insert(self.navbarItemBack!, at: 0) 436 | self.navigationItem.setLeftBarButtonItems(items, animated: false) 437 | // self.navigationItem.setLeftBarButtonItems(nil, animated: false) 438 | self.navigationController?.interactivePopGestureRecognizer?.delegate = nil 439 | self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true 440 | } 441 | } 442 | private func updateToolbarButtonItems() { 443 | self.toolbarItemBack?.isEnabled = webView.canGoBack 444 | self.toolbarItemForward?.isEnabled = webView.canGoForward 445 | self.toolbarItemAction?.isEnabled = !webView.isLoading 446 | 447 | let space = UIBarButtonItem.init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 448 | let items = [self.toolbarItemBack!, space, self.toolbarItemForward!, space, self.toolbarItemRefresh!, space, self.toolbarItemAction!] 449 | self.navigationController?.toolbar.barStyle = (self.navigationController?.navigationBar.barStyle)! 450 | self.navigationController?.toolbar.tintColor = self.navigationController?.navigationBar.tintColor 451 | self.navigationController?.toolbar.barTintColor = self.navigationController?.navigationBar.barTintColor 452 | self.toolbarItems = items 453 | } 454 | } 455 | 456 | -------------------------------------------------------------------------------- /GTMWebKitExample/Swift Scan/LBXScanWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LBXScanWrapper.swift 3 | // swiftScan 4 | // 5 | // Created by lbxia on 15/12/10. 6 | // Copyright © 2015年 xialibing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | public struct LBXScanResult { 13 | 14 | //码内容 15 | public var strScanned:String? = "" 16 | //扫描图像 17 | public var imgScanned:UIImage? 18 | //码的类型 19 | public var strBarCodeType:String? = "" 20 | 21 | //码在图像中的位置 22 | public var arrayCorner:[AnyObject]? 23 | 24 | public init(str:String?,img:UIImage?,barCodeType:String?,corner:[AnyObject]?) 25 | { 26 | self.strScanned = str 27 | self.imgScanned = img 28 | self.strBarCodeType = barCodeType 29 | self.arrayCorner = corner 30 | } 31 | } 32 | 33 | 34 | 35 | open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { 36 | 37 | let device = AVCaptureDevice.default(for: AVMediaType.video) 38 | 39 | var input:AVCaptureDeviceInput? 40 | var output:AVCaptureMetadataOutput 41 | 42 | let session = AVCaptureSession() 43 | var previewLayer:AVCaptureVideoPreviewLayer? 44 | var stillImageOutput:AVCaptureStillImageOutput? 45 | 46 | //存储返回结果 47 | var arrayResult:[LBXScanResult] = []; 48 | 49 | //扫码结果返回block 50 | var successBlock:([LBXScanResult]) -> Void 51 | 52 | //是否需要拍照 53 | var isNeedCaptureImage:Bool 54 | 55 | //当前扫码结果是否处理 56 | var isNeedScanResult:Bool = true 57 | 58 | /** 59 | 初始化设备 60 | - parameter videoPreView: 视频显示UIView 61 | - parameter objType: 识别码的类型,缺省值 QR二维码 62 | - parameter isCaptureImg: 识别后是否采集当前照片 63 | - parameter cropRect: 识别区域 64 | - parameter success: 返回识别信息 65 | - returns: 66 | */ 67 | init( videoPreView:UIView,objType:[AVMetadataObject.ObjectType] = [AVMetadataObject.ObjectType.qr],isCaptureImg:Bool,cropRect:CGRect=CGRect.zero,success:@escaping ( ([LBXScanResult]) -> Void) ) 68 | { 69 | do{ 70 | input = try AVCaptureDeviceInput(device: device!) 71 | } 72 | catch let error as NSError { 73 | print("AVCaptureDeviceInput(): \(error)") 74 | } 75 | 76 | successBlock = success 77 | 78 | // Output 79 | output = AVCaptureMetadataOutput() 80 | 81 | isNeedCaptureImage = isCaptureImg 82 | 83 | stillImageOutput = AVCaptureStillImageOutput(); 84 | 85 | super.init() 86 | 87 | if device == nil 88 | { 89 | return 90 | } 91 | 92 | if session.canAddInput(input!) 93 | { 94 | session.addInput(input!) 95 | } 96 | if session.canAddOutput(output) 97 | { 98 | session.addOutput(output) 99 | } 100 | if session.canAddOutput(stillImageOutput!) 101 | { 102 | session.addOutput(stillImageOutput!) 103 | } 104 | 105 | let outputSettings:Dictionary = [AVVideoCodecJPEG:AVVideoCodecKey] 106 | stillImageOutput?.outputSettings = outputSettings 107 | 108 | session.sessionPreset = AVCaptureSession.Preset.high 109 | 110 | //参数设置 111 | output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) 112 | 113 | output.metadataObjectTypes = objType 114 | 115 | // output.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code] 116 | 117 | if !cropRect.equalTo(CGRect.zero) 118 | { 119 | //启动相机后,直接修改该参数无效 120 | output.rectOfInterest = cropRect 121 | } 122 | 123 | previewLayer = AVCaptureVideoPreviewLayer(session: session) 124 | previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill 125 | 126 | var frame:CGRect = videoPreView.frame 127 | frame.origin = CGPoint.zero 128 | previewLayer?.frame = frame 129 | 130 | videoPreView.layer .insertSublayer(previewLayer!, at: 0) 131 | 132 | if ( device!.isFocusPointOfInterestSupported && device!.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) ) 133 | { 134 | do 135 | { 136 | try input?.device.lockForConfiguration() 137 | 138 | input?.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus 139 | 140 | input?.device.unlockForConfiguration() 141 | } 142 | catch let error as NSError { 143 | print("device.lockForConfiguration(): \(error)") 144 | 145 | } 146 | } 147 | 148 | } 149 | 150 | public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { 151 | captureOutput(output, didOutputMetadataObjects: metadataObjects, from: connection) 152 | } 153 | 154 | func start() 155 | { 156 | if !session.isRunning 157 | { 158 | isNeedScanResult = true 159 | session.startRunning() 160 | } 161 | } 162 | func stop() 163 | { 164 | if session.isRunning 165 | { 166 | isNeedScanResult = false 167 | session.stopRunning() 168 | } 169 | } 170 | 171 | open func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { 172 | 173 | if !isNeedScanResult 174 | { 175 | //上一帧处理中 176 | return 177 | } 178 | 179 | isNeedScanResult = false 180 | 181 | arrayResult.removeAll() 182 | 183 | //识别扫码类型 184 | for current:Any in metadataObjects 185 | { 186 | if (current as AnyObject).isKind(of: AVMetadataMachineReadableCodeObject.self) 187 | { 188 | let code = current as! AVMetadataMachineReadableCodeObject 189 | 190 | //码类型 191 | let codeType = code.type 192 | // print("code type:%@",codeType) 193 | //码内容 194 | let codeContent = code.stringValue 195 | // print("code string:%@",codeContent) 196 | 197 | //4个字典,分别 左上角-右上角-右下角-左下角的 坐标百分百,可以使用这个比例抠出码的图像 198 | // let arrayRatio = code.corners 199 | 200 | arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType.rawValue, corner: code.corners as [AnyObject]?)) 201 | } 202 | } 203 | 204 | if arrayResult.count > 0 205 | { 206 | if isNeedCaptureImage 207 | { 208 | captureImage() 209 | } 210 | else 211 | { 212 | stop() 213 | successBlock(arrayResult) 214 | } 215 | 216 | } 217 | else 218 | { 219 | isNeedScanResult = true 220 | } 221 | 222 | } 223 | 224 | //MARK: ----拍照 225 | open func captureImage() 226 | { 227 | let stillImageConnection:AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video, connections: (stillImageOutput?.connections)! as [AnyObject]) 228 | 229 | 230 | stillImageOutput?.captureStillImageAsynchronously(from: stillImageConnection!, completionHandler: { (imageDataSampleBuffer, error) -> Void in 231 | 232 | self.stop() 233 | if imageDataSampleBuffer != nil 234 | { 235 | let imageData: Data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)! 236 | let scanImg:UIImage? = UIImage(data: imageData) 237 | 238 | 239 | for idx in 0...self.arrayResult.count-1 240 | { 241 | self.arrayResult[idx].imgScanned = scanImg 242 | } 243 | } 244 | 245 | self.successBlock(self.arrayResult) 246 | 247 | }) 248 | } 249 | 250 | open func connectionWithMediaType(mediaType:AVMediaType,connections:[AnyObject]) -> AVCaptureConnection? 251 | { 252 | for connection:AnyObject in connections 253 | { 254 | let connectionTmp:AVCaptureConnection = connection as! AVCaptureConnection 255 | 256 | for port:Any in connectionTmp.inputPorts 257 | { 258 | if (port as AnyObject).isKind(of: AVCaptureInput.Port.self) 259 | { 260 | let portTmp:AVCaptureInput.Port = port as! AVCaptureInput.Port 261 | if portTmp.mediaType == mediaType 262 | { 263 | return connectionTmp 264 | } 265 | } 266 | } 267 | } 268 | return nil 269 | } 270 | 271 | 272 | //MARK:切换识别区域 273 | open func changeScanRect(cropRect:CGRect) 274 | { 275 | //待测试,不知道是否有效 276 | stop() 277 | output.rectOfInterest = cropRect 278 | start() 279 | } 280 | 281 | //MARK: 切换识别码的类型 282 | open func changeScanType(objType:[AVMetadataObject.ObjectType]) 283 | { 284 | //待测试中途修改是否有效 285 | output.metadataObjectTypes = objType 286 | } 287 | 288 | open func isGetFlash()->Bool 289 | { 290 | if (device != nil && device!.hasFlash && device!.hasTorch) 291 | { 292 | return true 293 | } 294 | return false 295 | } 296 | 297 | /** 298 | 打开或关闭闪关灯 299 | - parameter torch: true:打开闪关灯 false:关闭闪光灯 300 | */ 301 | open func setTorch(torch:Bool) 302 | { 303 | if isGetFlash() 304 | { 305 | do 306 | { 307 | try input?.device.lockForConfiguration() 308 | 309 | input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off 310 | 311 | input?.device.unlockForConfiguration() 312 | } 313 | catch let error as NSError { 314 | print("device.lockForConfiguration(): \(error)") 315 | 316 | } 317 | } 318 | 319 | } 320 | 321 | 322 | /** 323 | ------闪光灯打开或关闭 324 | */ 325 | open func changeTorch() 326 | { 327 | if isGetFlash() 328 | { 329 | do 330 | { 331 | try input?.device.lockForConfiguration() 332 | 333 | var torch = false 334 | 335 | if input?.device.torchMode == AVCaptureDevice.TorchMode.on 336 | { 337 | torch = false 338 | } 339 | else if input?.device.torchMode == AVCaptureDevice.TorchMode.off 340 | { 341 | torch = true 342 | } 343 | 344 | input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off 345 | 346 | input?.device.unlockForConfiguration() 347 | } 348 | catch let error as NSError { 349 | print("device.lockForConfiguration(): \(error)") 350 | 351 | } 352 | } 353 | } 354 | 355 | //MARK: ------获取系统默认支持的码的类型 356 | static func defaultMetaDataObjectTypes() ->[AVMetadataObject.ObjectType] 357 | { 358 | var types = 359 | [AVMetadataObject.ObjectType.qr, 360 | AVMetadataObject.ObjectType.upce, 361 | AVMetadataObject.ObjectType.code39, 362 | AVMetadataObject.ObjectType.code39Mod43, 363 | AVMetadataObject.ObjectType.ean13, 364 | AVMetadataObject.ObjectType.ean8, 365 | AVMetadataObject.ObjectType.code93, 366 | AVMetadataObject.ObjectType.code128, 367 | AVMetadataObject.ObjectType.pdf417, 368 | AVMetadataObject.ObjectType.aztec, 369 | 370 | ]; 371 | //if #available(iOS 8.0, *) 372 | 373 | types.append(AVMetadataObject.ObjectType.interleaved2of5) 374 | types.append(AVMetadataObject.ObjectType.itf14) 375 | types.append(AVMetadataObject.ObjectType.dataMatrix) 376 | 377 | types.append(AVMetadataObject.ObjectType.interleaved2of5) 378 | types.append(AVMetadataObject.ObjectType.itf14) 379 | types.append(AVMetadataObject.ObjectType.dataMatrix) 380 | 381 | return types as [AVMetadataObject.ObjectType]; 382 | } 383 | 384 | 385 | static func isSysIos8Later()->Bool 386 | { 387 | // return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_0 388 | 389 | if #available(iOS 8, *) { 390 | return true; 391 | } 392 | return false 393 | } 394 | 395 | /** 396 | 识别二维码码图像 397 | 398 | - parameter image: 二维码图像 399 | 400 | - returns: 返回识别结果 401 | */ 402 | static public func recognizeQRImage(image:UIImage) ->[LBXScanResult] 403 | { 404 | var returnResult:[LBXScanResult]=[] 405 | 406 | if LBXScanWrapper.isSysIos8Later() 407 | { 408 | //if #available(iOS 8.0, *) 409 | 410 | let detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])! 411 | 412 | let img = CIImage(cgImage: (image.cgImage)!) 413 | 414 | let features:[CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]) 415 | 416 | if( features != nil && (features?.count)! > 0) 417 | { 418 | let feature = features![0] 419 | 420 | if feature.isKind(of: CIQRCodeFeature.self) 421 | { 422 | let featureTmp:CIQRCodeFeature = feature as! CIQRCodeFeature 423 | 424 | let scanResult = featureTmp.messageString 425 | 426 | 427 | let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue,corner: nil) 428 | 429 | returnResult.append(result) 430 | } 431 | } 432 | 433 | } 434 | 435 | return returnResult 436 | } 437 | 438 | 439 | //MARK: -- - 生成二维码,背景色及二维码颜色设置 440 | static public func createCode( codeType:String, codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? 441 | { 442 | //if #available(iOS 8.0, *) 443 | 444 | let stringData = codeString.data(using: String.Encoding.utf8) 445 | 446 | 447 | //系统自带能生成的码 448 | // CIAztecCodeGenerator 449 | // CICode128BarcodeGenerator 450 | // CIPDF417BarcodeGenerator 451 | // CIQRCodeGenerator 452 | let qrFilter = CIFilter(name: codeType) 453 | 454 | 455 | qrFilter?.setValue(stringData, forKey: "inputMessage") 456 | 457 | qrFilter?.setValue("H", forKey: "inputCorrectionLevel") 458 | 459 | 460 | //上色 461 | let colorFilter = CIFilter(name: "CIFalseColor", parameters: ["inputImage":qrFilter!.outputImage!,"inputColor0":CIColor(cgColor: qrColor.cgColor),"inputColor1":CIColor(cgColor: bkColor.cgColor)]) 462 | 463 | 464 | let qrImage = colorFilter!.outputImage!; 465 | 466 | //绘制 467 | let cgImage = CIContext().createCGImage(qrImage, from: qrImage.extent)! 468 | 469 | 470 | UIGraphicsBeginImageContext(size); 471 | let context = UIGraphicsGetCurrentContext()!; 472 | context.interpolationQuality = CGInterpolationQuality.none; 473 | context.scaleBy(x: 1.0, y: -1.0); 474 | context.draw(cgImage, in: context.boundingBoxOfClipPath) 475 | let codeImage = UIGraphicsGetImageFromCurrentImageContext(); 476 | UIGraphicsEndImageContext(); 477 | 478 | return codeImage 479 | 480 | } 481 | 482 | static public func createCode128( codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? 483 | { 484 | let stringData = codeString.data(using: String.Encoding.utf8) 485 | 486 | 487 | //系统自带能生成的码 488 | // CIAztecCodeGenerator 二维码 489 | // CICode128BarcodeGenerator 条形码 490 | // CIPDF417BarcodeGenerator 491 | // CIQRCodeGenerator 二维码 492 | let qrFilter = CIFilter(name: "CICode128BarcodeGenerator") 493 | qrFilter?.setDefaults() 494 | qrFilter?.setValue(stringData, forKey: "inputMessage") 495 | 496 | 497 | 498 | let outputImage:CIImage? = qrFilter?.outputImage 499 | let context = CIContext() 500 | let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent) 501 | 502 | let image = UIImage(cgImage: cgImage!, scale: 1.0, orientation: UIImage.Orientation.up) 503 | 504 | 505 | // Resize without interpolating 506 | let scaleRate:CGFloat = 20.0 507 | let resized = resizeImage(image: image, quality: CGInterpolationQuality.none, rate: scaleRate) 508 | 509 | return resized; 510 | } 511 | 512 | 513 | //MARK:根据扫描结果,获取图像中得二维码区域图像(如果相机拍摄角度故意很倾斜,获取的图像效果很差) 514 | static func getConcreteCodeImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->UIImage? 515 | { 516 | let rect:CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult) 517 | 518 | if rect.isEmpty 519 | { 520 | return nil 521 | } 522 | 523 | let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) 524 | 525 | if img != nil 526 | { 527 | let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right) 528 | return imgRotation 529 | } 530 | return nil 531 | } 532 | //根据二维码的区域截取二维码区域图像 533 | static public func getConcreteCodeImage(srcCodeImage:UIImage,rect:CGRect)->UIImage? 534 | { 535 | if rect.isEmpty 536 | { 537 | return nil 538 | } 539 | 540 | let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) 541 | 542 | if img != nil 543 | { 544 | let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right) 545 | return imgRotation 546 | } 547 | return nil 548 | } 549 | 550 | //获取二维码的图像区域 551 | static public func getConcreteCodeRectFromImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->CGRect 552 | { 553 | if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4 ) 554 | { 555 | return CGRect.zero 556 | } 557 | 558 | let corner:[[String:Float]] = codeResult.arrayCorner as! [[String:Float]] 559 | 560 | let dicTopLeft = corner[0] 561 | let dicTopRight = corner[1] 562 | let dicBottomRight = corner[2] 563 | let dicBottomLeft = corner[3] 564 | 565 | let xLeftTopRatio:Float = dicTopLeft["X"]! 566 | let yLeftTopRatio:Float = dicTopLeft["Y"]! 567 | 568 | let xRightTopRatio:Float = dicTopRight["X"]! 569 | let yRightTopRatio:Float = dicTopRight["Y"]! 570 | 571 | let xBottomRightRatio:Float = dicBottomRight["X"]! 572 | let yBottomRightRatio:Float = dicBottomRight["Y"]! 573 | 574 | let xLeftBottomRatio:Float = dicBottomLeft["X"]! 575 | let yLeftBottomRatio:Float = dicBottomLeft["Y"]! 576 | 577 | //由于截图只能矩形,所以截图不规则四边形的最大外围 578 | let xMinLeft = CGFloat( min(xLeftTopRatio, xLeftBottomRatio) ) 579 | let xMaxRight = CGFloat( max(xRightTopRatio, xBottomRightRatio) ) 580 | 581 | let yMinTop = CGFloat( min(yLeftTopRatio, yRightTopRatio) ) 582 | let yMaxBottom = CGFloat ( max(yLeftBottomRatio, yBottomRightRatio) ) 583 | 584 | let imgW = srcCodeImage.size.width 585 | let imgH = srcCodeImage.size.height 586 | 587 | //宽高反过来计算 588 | let rect = CGRect(x: xMinLeft * imgH, y: yMinTop*imgW, width: (xMaxRight-xMinLeft)*imgH, height: (yMaxBottom-yMinTop)*imgW) 589 | return rect 590 | } 591 | 592 | //MARK: ----图像处理 593 | 594 | /** 595 | @brief 图像中间加logo图片 596 | @param srcImg 原图像 597 | @param LogoImage logo图像 598 | @param logoSize logo图像尺寸 599 | @return 加Logo的图像 600 | */ 601 | static public func addImageLogo(srcImg:UIImage,logoImg:UIImage,logoSize:CGSize )->UIImage 602 | { 603 | UIGraphicsBeginImageContext(srcImg.size); 604 | srcImg.draw(in: CGRect(x: 0, y: 0, width: srcImg.size.width, height: srcImg.size.height)) 605 | let rect = CGRect(x: srcImg.size.width/2 - logoSize.width/2, y: srcImg.size.height/2-logoSize.height/2, width:logoSize.width, height: logoSize.height); 606 | logoImg.draw(in: rect) 607 | let resultingImage = UIGraphicsGetImageFromCurrentImageContext(); 608 | UIGraphicsEndImageContext(); 609 | return resultingImage!; 610 | } 611 | 612 | //图像缩放 613 | static func resizeImage(image:UIImage,quality:CGInterpolationQuality,rate:CGFloat)->UIImage? 614 | { 615 | var resized:UIImage?; 616 | let width = image.size.width * rate; 617 | let height = image.size.height * rate; 618 | 619 | UIGraphicsBeginImageContext(CGSize(width: width, height: height)); 620 | let context = UIGraphicsGetCurrentContext(); 621 | context!.interpolationQuality = quality; 622 | image.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) 623 | 624 | resized = UIGraphicsGetImageFromCurrentImageContext(); 625 | UIGraphicsEndImageContext(); 626 | 627 | return resized; 628 | } 629 | 630 | 631 | //图像裁剪 632 | static func imageByCroppingWithStyle(srcImg:UIImage,rect:CGRect)->UIImage? 633 | { 634 | let imageRef = srcImg.cgImage 635 | let imagePartRef = imageRef!.cropping(to: rect) 636 | let cropImage = UIImage(cgImage: imagePartRef!) 637 | 638 | return cropImage 639 | } 640 | //图像旋转 641 | static func imageRotation(image:UIImage,orientation:UIImage.Orientation)->UIImage 642 | { 643 | var rotate:Double = 0.0; 644 | var rect:CGRect; 645 | var translateX:CGFloat = 0.0; 646 | var translateY:CGFloat = 0.0; 647 | var scaleX:CGFloat = 1.0; 648 | var scaleY:CGFloat = 1.0; 649 | 650 | switch (orientation) { 651 | case UIImage.Orientation.left: 652 | rotate = .pi/2; 653 | rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); 654 | translateX = 0; 655 | translateY = -rect.size.width; 656 | scaleY = rect.size.width/rect.size.height; 657 | scaleX = rect.size.height/rect.size.width; 658 | break; 659 | case UIImage.Orientation.right: 660 | rotate = 3 * .pi/2; 661 | rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); 662 | translateX = -rect.size.height; 663 | translateY = 0; 664 | scaleY = rect.size.width/rect.size.height; 665 | scaleX = rect.size.height/rect.size.width; 666 | break; 667 | case UIImage.Orientation.down: 668 | rotate = .pi; 669 | rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height); 670 | translateX = -rect.size.width; 671 | translateY = -rect.size.height; 672 | break; 673 | default: 674 | rotate = 0.0; 675 | rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height); 676 | translateX = 0; 677 | translateY = 0; 678 | break; 679 | } 680 | 681 | UIGraphicsBeginImageContext(rect.size); 682 | let context = UIGraphicsGetCurrentContext()!; 683 | //做CTM变换 684 | context.translateBy(x: 0.0, y: rect.size.height); 685 | context.scaleBy(x: 1.0, y: -1.0); 686 | context.rotate(by: CGFloat(rotate)); 687 | context.translateBy(x: translateX, y: translateY); 688 | 689 | context.scaleBy(x: scaleX, y: scaleY); 690 | //绘制图片 691 | context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) 692 | let newPic = UIGraphicsGetImageFromCurrentImageContext(); 693 | 694 | return newPic!; 695 | } 696 | 697 | deinit 698 | { 699 | // print("LBXScanWrapper deinit") 700 | } 701 | 702 | 703 | 704 | } 705 | -------------------------------------------------------------------------------- /GTMWebKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2619000321477FFC00B2C31C /* GTMWebKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2619000221477FFC00B2C31C /* GTMWebKit.swift */; }; 11 | 2657A3AB1FA993C200A717A3 /* GTMWebKit.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 2657A3A41FA993C100A717A3 /* GTMWebKit.bundle */; }; 12 | 2657A3AC1FA993C200A717A3 /* GTMAlertable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3A51FA993C100A717A3 /* GTMAlertable.swift */; }; 13 | 2657A3AD1FA993C200A717A3 /* GTMWebView+Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3A61FA993C200A717A3 /* GTMWebView+Alert.swift */; }; 14 | 2657A3AE1FA993C200A717A3 /* GTMWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3A71FA993C200A717A3 /* GTMWebViewController.swift */; }; 15 | 2657A3AF1FA993C200A717A3 /* GTMWebView+Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3A81FA993C200A717A3 /* GTMWebView+Bundle.swift */; }; 16 | 2657A3B01FA993C200A717A3 /* URLConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3A91FA993C200A717A3 /* URLConvertible.swift */; }; 17 | 2657A3B11FA993C200A717A3 /* WeakScriptMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3AA1FA993C200A717A3 /* WeakScriptMessageHandler.swift */; }; 18 | 2657A3B91FA993EA00A717A3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3B81FA993EA00A717A3 /* AppDelegate.swift */; }; 19 | 2657A3BB1FA993EA00A717A3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3BA1FA993EA00A717A3 /* ViewController.swift */; }; 20 | 2657A3BE1FA993EA00A717A3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2657A3BC1FA993EA00A717A3 /* Main.storyboard */; }; 21 | 2657A3C01FA993EA00A717A3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2657A3BF1FA993EA00A717A3 /* Assets.xcassets */; }; 22 | 2657A3C31FA993EA00A717A3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2657A3C11FA993EA00A717A3 /* LaunchScreen.storyboard */; }; 23 | 2657A3C91FA994BE00A717A3 /* test.html in Resources */ = {isa = PBXBuildFile; fileRef = 2657A3C81FA994BE00A717A3 /* test.html */; }; 24 | 2657A3CA1FA994FD00A717A3 /* GTMWebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2657A3991FA9939300A717A3 /* GTMWebKit.framework */; }; 25 | 2657A3CB1FA994FD00A717A3 /* GTMWebKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2657A3991FA9939300A717A3 /* GTMWebKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 26 | 2657A3D41FA9BC7600A717A3 /* CustomWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2657A3D31FA9BC7600A717A3 /* CustomWebViewController.swift */; }; 27 | 26712C6B1FAABF3900AC3255 /* LBXPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C641FAABF3800AC3255 /* LBXPermissions.swift */; }; 28 | 26712C6C1FAABF3900AC3255 /* LBXScanLineAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C651FAABF3800AC3255 /* LBXScanLineAnimation.swift */; }; 29 | 26712C6D1FAABF3900AC3255 /* LBXScanNetAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C661FAABF3800AC3255 /* LBXScanNetAnimation.swift */; }; 30 | 26712C6E1FAABF3900AC3255 /* LBXScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C671FAABF3800AC3255 /* LBXScanView.swift */; }; 31 | 26712C6F1FAABF3900AC3255 /* LBXScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C681FAABF3800AC3255 /* LBXScanViewController.swift */; }; 32 | 26712C701FAABF3900AC3255 /* LBXScanViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C691FAABF3800AC3255 /* LBXScanViewStyle.swift */; }; 33 | 26712C711FAABF3900AC3255 /* LBXScanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C6A1FAABF3800AC3255 /* LBXScanWrapper.swift */; }; 34 | 26712C731FAABFC100AC3255 /* BarcodeScanable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C721FAABFC100AC3255 /* BarcodeScanable.swift */; }; 35 | 26712C751FAAC03200AC3255 /* ScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26712C741FAAC03200AC3255 /* ScanViewController.swift */; }; 36 | 26712C7B1FAAE8D600AC3255 /* GTMWebKit.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 26712C781FAAE8D500AC3255 /* GTMWebKit.podspec */; }; 37 | 26712C7C1FAAE8D600AC3255 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 26712C791FAAE8D500AC3255 /* LICENSE */; }; 38 | 26712C7D1FAAE8D600AC3255 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 26712C7A1FAAE8D500AC3255 /* README.md */; }; 39 | 26712C811FAAF30000AC3255 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 26712C801FAAF30000AC3255 /* logo.png */; }; 40 | 26B891F61FB3E5AF00EC7251 /* GTMWebView+WKWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B891F51FB3E5AF00EC7251 /* GTMWebView+WKWebView.swift */; }; 41 | 26C629162193FCD200AC5F42 /* GTMWebControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C629152193FCD200AC5F42 /* GTMWebControls.swift */; }; 42 | 26DDD6E51FBD588A0029EA3F /* UIWebViewCookiesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DDD6E41FBD588A0029EA3F /* UIWebViewCookiesVC.swift */; }; 43 | 26DDD6E71FBD589B0029EA3F /* WKWebViewCookiesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DDD6E61FBD589B0029EA3F /* WKWebViewCookiesVC.swift */; }; 44 | 26DDD6EB1FBD8CE50029EA3F /* GTMWebViewCookies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DDD6EA1FBD8CE50029EA3F /* GTMWebViewCookies.swift */; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXContainerItemProxy section */ 48 | 2657A3CC1FA994FD00A717A3 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 2657A3901FA9939300A717A3 /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 2657A3981FA9939300A717A3; 53 | remoteInfo = GTMWebKit; 54 | }; 55 | /* End PBXContainerItemProxy section */ 56 | 57 | /* Begin PBXCopyFilesBuildPhase section */ 58 | 2657A3CE1FA994FD00A717A3 /* Embed Frameworks */ = { 59 | isa = PBXCopyFilesBuildPhase; 60 | buildActionMask = 2147483647; 61 | dstPath = ""; 62 | dstSubfolderSpec = 10; 63 | files = ( 64 | 2657A3CB1FA994FD00A717A3 /* GTMWebKit.framework in Embed Frameworks */, 65 | ); 66 | name = "Embed Frameworks"; 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXCopyFilesBuildPhase section */ 70 | 71 | /* Begin PBXFileReference section */ 72 | 2619000221477FFC00B2C31C /* GTMWebKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTMWebKit.swift; sourceTree = ""; }; 73 | 2657A3991FA9939300A717A3 /* GTMWebKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GTMWebKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 2657A39D1FA9939300A717A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 2657A3A41FA993C100A717A3 /* GTMWebKit.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GTMWebKit.bundle; sourceTree = ""; }; 76 | 2657A3A51FA993C100A717A3 /* GTMAlertable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GTMAlertable.swift; sourceTree = ""; }; 77 | 2657A3A61FA993C200A717A3 /* GTMWebView+Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GTMWebView+Alert.swift"; sourceTree = ""; }; 78 | 2657A3A71FA993C200A717A3 /* GTMWebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GTMWebViewController.swift; sourceTree = ""; }; 79 | 2657A3A81FA993C200A717A3 /* GTMWebView+Bundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GTMWebView+Bundle.swift"; sourceTree = ""; }; 80 | 2657A3A91FA993C200A717A3 /* URLConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLConvertible.swift; sourceTree = ""; }; 81 | 2657A3AA1FA993C200A717A3 /* WeakScriptMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakScriptMessageHandler.swift; sourceTree = ""; }; 82 | 2657A3B61FA993EA00A717A3 /* GTMWebKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GTMWebKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | 2657A3B81FA993EA00A717A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84 | 2657A3BA1FA993EA00A717A3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 85 | 2657A3BD1FA993EA00A717A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 86 | 2657A3BF1FA993EA00A717A3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 87 | 2657A3C21FA993EA00A717A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 88 | 2657A3C41FA993EA00A717A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | 2657A3C81FA994BE00A717A3 /* test.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = test.html; sourceTree = ""; }; 90 | 2657A3CF1FA9952900A717A3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; 91 | 2657A3D01FA9952900A717A3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = ""; }; 92 | 2657A3D31FA9BC7600A717A3 /* CustomWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWebViewController.swift; sourceTree = ""; }; 93 | 26712C641FAABF3800AC3255 /* LBXPermissions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXPermissions.swift; sourceTree = ""; }; 94 | 26712C651FAABF3800AC3255 /* LBXScanLineAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanLineAnimation.swift; sourceTree = ""; }; 95 | 26712C661FAABF3800AC3255 /* LBXScanNetAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanNetAnimation.swift; sourceTree = ""; }; 96 | 26712C671FAABF3800AC3255 /* LBXScanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanView.swift; sourceTree = ""; }; 97 | 26712C681FAABF3800AC3255 /* LBXScanViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanViewController.swift; sourceTree = ""; }; 98 | 26712C691FAABF3800AC3255 /* LBXScanViewStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanViewStyle.swift; sourceTree = ""; }; 99 | 26712C6A1FAABF3800AC3255 /* LBXScanWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LBXScanWrapper.swift; sourceTree = ""; }; 100 | 26712C721FAABFC100AC3255 /* BarcodeScanable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarcodeScanable.swift; sourceTree = ""; }; 101 | 26712C741FAAC03200AC3255 /* ScanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanViewController.swift; sourceTree = ""; }; 102 | 26712C781FAAE8D500AC3255 /* GTMWebKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GTMWebKit.podspec; sourceTree = ""; }; 103 | 26712C791FAAE8D500AC3255 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 104 | 26712C7A1FAAE8D500AC3255 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 105 | 26712C801FAAF30000AC3255 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; }; 106 | 26B891F51FB3E5AF00EC7251 /* GTMWebView+WKWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GTMWebView+WKWebView.swift"; sourceTree = ""; }; 107 | 26C629152193FCD200AC5F42 /* GTMWebControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTMWebControls.swift; sourceTree = ""; }; 108 | 26DDD6E41FBD588A0029EA3F /* UIWebViewCookiesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIWebViewCookiesVC.swift; sourceTree = ""; }; 109 | 26DDD6E61FBD589B0029EA3F /* WKWebViewCookiesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKWebViewCookiesVC.swift; sourceTree = ""; }; 110 | 26DDD6EA1FBD8CE50029EA3F /* GTMWebViewCookies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GTMWebViewCookies.swift; sourceTree = ""; }; 111 | /* End PBXFileReference section */ 112 | 113 | /* Begin PBXFrameworksBuildPhase section */ 114 | 2657A3951FA9939300A717A3 /* Frameworks */ = { 115 | isa = PBXFrameworksBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | 2657A3B31FA993EA00A717A3 /* Frameworks */ = { 122 | isa = PBXFrameworksBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | 2657A3CA1FA994FD00A717A3 /* GTMWebKit.framework in Frameworks */, 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXFrameworksBuildPhase section */ 130 | 131 | /* Begin PBXGroup section */ 132 | 262656921FBC32E500681044 /* Cookies */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 26DDD6E41FBD588A0029EA3F /* UIWebViewCookiesVC.swift */, 136 | 26DDD6E61FBD589B0029EA3F /* WKWebViewCookiesVC.swift */, 137 | ); 138 | name = Cookies; 139 | sourceTree = ""; 140 | }; 141 | 2657A38F1FA9939300A717A3 = { 142 | isa = PBXGroup; 143 | children = ( 144 | 26712C801FAAF30000AC3255 /* logo.png */, 145 | 26712C781FAAE8D500AC3255 /* GTMWebKit.podspec */, 146 | 26712C791FAAE8D500AC3255 /* LICENSE */, 147 | 26712C7A1FAAE8D500AC3255 /* README.md */, 148 | 2657A39B1FA9939300A717A3 /* GTMWebKit */, 149 | 2657A3B71FA993EA00A717A3 /* GTMWebKitExample */, 150 | 2657A39A1FA9939300A717A3 /* Products */, 151 | ); 152 | sourceTree = ""; 153 | }; 154 | 2657A39A1FA9939300A717A3 /* Products */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 2657A3991FA9939300A717A3 /* GTMWebKit.framework */, 158 | 2657A3B61FA993EA00A717A3 /* GTMWebKitExample.app */, 159 | ); 160 | name = Products; 161 | sourceTree = ""; 162 | }; 163 | 2657A39B1FA9939300A717A3 /* GTMWebKit */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 2619000221477FFC00B2C31C /* GTMWebKit.swift */, 167 | 26C629152193FCD200AC5F42 /* GTMWebControls.swift */, 168 | 2657A3A51FA993C100A717A3 /* GTMAlertable.swift */, 169 | 2657A3A61FA993C200A717A3 /* GTMWebView+Alert.swift */, 170 | 2657A3A81FA993C200A717A3 /* GTMWebView+Bundle.swift */, 171 | 26B891F51FB3E5AF00EC7251 /* GTMWebView+WKWebView.swift */, 172 | 26DDD6EA1FBD8CE50029EA3F /* GTMWebViewCookies.swift */, 173 | 2657A3A71FA993C200A717A3 /* GTMWebViewController.swift */, 174 | 2657A3A91FA993C200A717A3 /* URLConvertible.swift */, 175 | 2657A3AA1FA993C200A717A3 /* WeakScriptMessageHandler.swift */, 176 | 2657A3D11FA9958D00A717A3 /* Supporting Files */, 177 | ); 178 | path = GTMWebKit; 179 | sourceTree = ""; 180 | }; 181 | 2657A3B71FA993EA00A717A3 /* GTMWebKitExample */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 262656921FBC32E500681044 /* Cookies */, 185 | 26712C771FAAC06000AC3255 /* Native And JS */, 186 | 26712C761FAAC04100AC3255 /* Barcode Scan */, 187 | 2657A3B81FA993EA00A717A3 /* AppDelegate.swift */, 188 | 2657A3BA1FA993EA00A717A3 /* ViewController.swift */, 189 | 2657A3D21FA995CA00A717A3 /* Supporting Files */, 190 | ); 191 | path = GTMWebKitExample; 192 | sourceTree = ""; 193 | }; 194 | 2657A3D11FA9958D00A717A3 /* Supporting Files */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 2657A39D1FA9939300A717A3 /* Info.plist */, 198 | 2657A3A41FA993C100A717A3 /* GTMWebKit.bundle */, 199 | ); 200 | name = "Supporting Files"; 201 | sourceTree = ""; 202 | }; 203 | 2657A3D21FA995CA00A717A3 /* Supporting Files */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 2657A3BC1FA993EA00A717A3 /* Main.storyboard */, 207 | 2657A3C11FA993EA00A717A3 /* LaunchScreen.storyboard */, 208 | 2657A3BF1FA993EA00A717A3 /* Assets.xcassets */, 209 | 2657A3C81FA994BE00A717A3 /* test.html */, 210 | 2657A3C41FA993EA00A717A3 /* Info.plist */, 211 | ); 212 | name = "Supporting Files"; 213 | sourceTree = ""; 214 | }; 215 | 26712C631FAABF3800AC3255 /* Swift Scan */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | 26712C641FAABF3800AC3255 /* LBXPermissions.swift */, 219 | 26712C651FAABF3800AC3255 /* LBXScanLineAnimation.swift */, 220 | 26712C661FAABF3800AC3255 /* LBXScanNetAnimation.swift */, 221 | 26712C671FAABF3800AC3255 /* LBXScanView.swift */, 222 | 26712C681FAABF3800AC3255 /* LBXScanViewController.swift */, 223 | 26712C691FAABF3800AC3255 /* LBXScanViewStyle.swift */, 224 | 26712C6A1FAABF3800AC3255 /* LBXScanWrapper.swift */, 225 | ); 226 | path = "Swift Scan"; 227 | sourceTree = ""; 228 | }; 229 | 26712C761FAAC04100AC3255 /* Barcode Scan */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | 26712C741FAAC03200AC3255 /* ScanViewController.swift */, 233 | 26712C721FAABFC100AC3255 /* BarcodeScanable.swift */, 234 | 26712C631FAABF3800AC3255 /* Swift Scan */, 235 | ); 236 | name = "Barcode Scan"; 237 | sourceTree = ""; 238 | }; 239 | 26712C771FAAC06000AC3255 /* Native And JS */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 2657A3D31FA9BC7600A717A3 /* CustomWebViewController.swift */, 243 | ); 244 | name = "Native And JS"; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXGroup section */ 248 | 249 | /* Begin PBXHeadersBuildPhase section */ 250 | 2657A3961FA9939300A717A3 /* Headers */ = { 251 | isa = PBXHeadersBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXHeadersBuildPhase section */ 258 | 259 | /* Begin PBXNativeTarget section */ 260 | 2657A3981FA9939300A717A3 /* GTMWebKit */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = 2657A3A11FA9939300A717A3 /* Build configuration list for PBXNativeTarget "GTMWebKit" */; 263 | buildPhases = ( 264 | 2657A3941FA9939300A717A3 /* Sources */, 265 | 2657A3951FA9939300A717A3 /* Frameworks */, 266 | 2657A3961FA9939300A717A3 /* Headers */, 267 | 2657A3971FA9939300A717A3 /* Resources */, 268 | ); 269 | buildRules = ( 270 | ); 271 | dependencies = ( 272 | ); 273 | name = GTMWebKit; 274 | productName = GTMWebKit; 275 | productReference = 2657A3991FA9939300A717A3 /* GTMWebKit.framework */; 276 | productType = "com.apple.product-type.framework"; 277 | }; 278 | 2657A3B51FA993EA00A717A3 /* GTMWebKitExample */ = { 279 | isa = PBXNativeTarget; 280 | buildConfigurationList = 2657A3C51FA993EA00A717A3 /* Build configuration list for PBXNativeTarget "GTMWebKitExample" */; 281 | buildPhases = ( 282 | 2657A3B21FA993EA00A717A3 /* Sources */, 283 | 2657A3B31FA993EA00A717A3 /* Frameworks */, 284 | 2657A3B41FA993EA00A717A3 /* Resources */, 285 | 2657A3CE1FA994FD00A717A3 /* Embed Frameworks */, 286 | ); 287 | buildRules = ( 288 | ); 289 | dependencies = ( 290 | 2657A3CD1FA994FD00A717A3 /* PBXTargetDependency */, 291 | ); 292 | name = GTMWebKitExample; 293 | productName = GTMWebKitExample; 294 | productReference = 2657A3B61FA993EA00A717A3 /* GTMWebKitExample.app */; 295 | productType = "com.apple.product-type.application"; 296 | }; 297 | /* End PBXNativeTarget section */ 298 | 299 | /* Begin PBXProject section */ 300 | 2657A3901FA9939300A717A3 /* Project object */ = { 301 | isa = PBXProject; 302 | attributes = { 303 | LastSwiftUpdateCheck = 0900; 304 | LastUpgradeCheck = 0900; 305 | ORGANIZATIONNAME = yang; 306 | TargetAttributes = { 307 | 2657A3981FA9939300A717A3 = { 308 | CreatedOnToolsVersion = 9.0.1; 309 | LastSwiftMigration = 0900; 310 | ProvisioningStyle = Automatic; 311 | }; 312 | 2657A3B51FA993EA00A717A3 = { 313 | CreatedOnToolsVersion = 9.0.1; 314 | ProvisioningStyle = Automatic; 315 | }; 316 | }; 317 | }; 318 | buildConfigurationList = 2657A3931FA9939300A717A3 /* Build configuration list for PBXProject "GTMWebKit" */; 319 | compatibilityVersion = "Xcode 8.0"; 320 | developmentRegion = en; 321 | hasScannedForEncodings = 0; 322 | knownRegions = ( 323 | en, 324 | Base, 325 | ); 326 | mainGroup = 2657A38F1FA9939300A717A3; 327 | productRefGroup = 2657A39A1FA9939300A717A3 /* Products */; 328 | projectDirPath = ""; 329 | projectRoot = ""; 330 | targets = ( 331 | 2657A3981FA9939300A717A3 /* GTMWebKit */, 332 | 2657A3B51FA993EA00A717A3 /* GTMWebKitExample */, 333 | ); 334 | }; 335 | /* End PBXProject section */ 336 | 337 | /* Begin PBXResourcesBuildPhase section */ 338 | 2657A3971FA9939300A717A3 /* Resources */ = { 339 | isa = PBXResourcesBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | 2657A3AB1FA993C200A717A3 /* GTMWebKit.bundle in Resources */, 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | 2657A3B41FA993EA00A717A3 /* Resources */ = { 347 | isa = PBXResourcesBuildPhase; 348 | buildActionMask = 2147483647; 349 | files = ( 350 | 26712C7C1FAAE8D600AC3255 /* LICENSE in Resources */, 351 | 2657A3C91FA994BE00A717A3 /* test.html in Resources */, 352 | 26712C7B1FAAE8D600AC3255 /* GTMWebKit.podspec in Resources */, 353 | 26712C7D1FAAE8D600AC3255 /* README.md in Resources */, 354 | 26712C811FAAF30000AC3255 /* logo.png in Resources */, 355 | 2657A3C31FA993EA00A717A3 /* LaunchScreen.storyboard in Resources */, 356 | 2657A3C01FA993EA00A717A3 /* Assets.xcassets in Resources */, 357 | 2657A3BE1FA993EA00A717A3 /* Main.storyboard in Resources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | /* End PBXResourcesBuildPhase section */ 362 | 363 | /* Begin PBXSourcesBuildPhase section */ 364 | 2657A3941FA9939300A717A3 /* Sources */ = { 365 | isa = PBXSourcesBuildPhase; 366 | buildActionMask = 2147483647; 367 | files = ( 368 | 26B891F61FB3E5AF00EC7251 /* GTMWebView+WKWebView.swift in Sources */, 369 | 2657A3AF1FA993C200A717A3 /* GTMWebView+Bundle.swift in Sources */, 370 | 2657A3B11FA993C200A717A3 /* WeakScriptMessageHandler.swift in Sources */, 371 | 2657A3AC1FA993C200A717A3 /* GTMAlertable.swift in Sources */, 372 | 2657A3AE1FA993C200A717A3 /* GTMWebViewController.swift in Sources */, 373 | 26C629162193FCD200AC5F42 /* GTMWebControls.swift in Sources */, 374 | 2619000321477FFC00B2C31C /* GTMWebKit.swift in Sources */, 375 | 26DDD6EB1FBD8CE50029EA3F /* GTMWebViewCookies.swift in Sources */, 376 | 2657A3AD1FA993C200A717A3 /* GTMWebView+Alert.swift in Sources */, 377 | 2657A3B01FA993C200A717A3 /* URLConvertible.swift in Sources */, 378 | ); 379 | runOnlyForDeploymentPostprocessing = 0; 380 | }; 381 | 2657A3B21FA993EA00A717A3 /* Sources */ = { 382 | isa = PBXSourcesBuildPhase; 383 | buildActionMask = 2147483647; 384 | files = ( 385 | 26712C6F1FAABF3900AC3255 /* LBXScanViewController.swift in Sources */, 386 | 26712C711FAABF3900AC3255 /* LBXScanWrapper.swift in Sources */, 387 | 26712C731FAABFC100AC3255 /* BarcodeScanable.swift in Sources */, 388 | 26712C6C1FAABF3900AC3255 /* LBXScanLineAnimation.swift in Sources */, 389 | 26712C6E1FAABF3900AC3255 /* LBXScanView.swift in Sources */, 390 | 2657A3BB1FA993EA00A717A3 /* ViewController.swift in Sources */, 391 | 26712C6B1FAABF3900AC3255 /* LBXPermissions.swift in Sources */, 392 | 26712C701FAABF3900AC3255 /* LBXScanViewStyle.swift in Sources */, 393 | 2657A3B91FA993EA00A717A3 /* AppDelegate.swift in Sources */, 394 | 26712C6D1FAABF3900AC3255 /* LBXScanNetAnimation.swift in Sources */, 395 | 2657A3D41FA9BC7600A717A3 /* CustomWebViewController.swift in Sources */, 396 | 26712C751FAAC03200AC3255 /* ScanViewController.swift in Sources */, 397 | 26DDD6E51FBD588A0029EA3F /* UIWebViewCookiesVC.swift in Sources */, 398 | 26DDD6E71FBD589B0029EA3F /* WKWebViewCookiesVC.swift in Sources */, 399 | ); 400 | runOnlyForDeploymentPostprocessing = 0; 401 | }; 402 | /* End PBXSourcesBuildPhase section */ 403 | 404 | /* Begin PBXTargetDependency section */ 405 | 2657A3CD1FA994FD00A717A3 /* PBXTargetDependency */ = { 406 | isa = PBXTargetDependency; 407 | target = 2657A3981FA9939300A717A3 /* GTMWebKit */; 408 | targetProxy = 2657A3CC1FA994FD00A717A3 /* PBXContainerItemProxy */; 409 | }; 410 | /* End PBXTargetDependency section */ 411 | 412 | /* Begin PBXVariantGroup section */ 413 | 2657A3BC1FA993EA00A717A3 /* Main.storyboard */ = { 414 | isa = PBXVariantGroup; 415 | children = ( 416 | 2657A3BD1FA993EA00A717A3 /* Base */, 417 | 2657A3CF1FA9952900A717A3 /* zh-Hans */, 418 | ); 419 | name = Main.storyboard; 420 | sourceTree = ""; 421 | }; 422 | 2657A3C11FA993EA00A717A3 /* LaunchScreen.storyboard */ = { 423 | isa = PBXVariantGroup; 424 | children = ( 425 | 2657A3C21FA993EA00A717A3 /* Base */, 426 | 2657A3D01FA9952900A717A3 /* zh-Hans */, 427 | ); 428 | name = LaunchScreen.storyboard; 429 | sourceTree = ""; 430 | }; 431 | /* End PBXVariantGroup section */ 432 | 433 | /* Begin XCBuildConfiguration section */ 434 | 2657A39F1FA9939300A717A3 /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 439 | CLANG_ANALYZER_NONNULL = YES; 440 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 442 | CLANG_CXX_LIBRARY = "libc++"; 443 | CLANG_ENABLE_MODULES = YES; 444 | CLANG_ENABLE_OBJC_ARC = YES; 445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 446 | CLANG_WARN_BOOL_CONVERSION = YES; 447 | CLANG_WARN_COMMA = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 450 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INFINITE_RECURSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 456 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 458 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 459 | CLANG_WARN_STRICT_PROTOTYPES = YES; 460 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 461 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | CODE_SIGN_IDENTITY = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | CURRENT_PROJECT_VERSION = 1; 467 | DEBUG_INFORMATION_FORMAT = dwarf; 468 | ENABLE_STRICT_OBJC_MSGSEND = YES; 469 | ENABLE_TESTABILITY = YES; 470 | GCC_C_LANGUAGE_STANDARD = gnu11; 471 | GCC_DYNAMIC_NO_PIC = NO; 472 | GCC_NO_COMMON_BLOCKS = YES; 473 | GCC_OPTIMIZATION_LEVEL = 0; 474 | GCC_PREPROCESSOR_DEFINITIONS = ( 475 | "DEBUG=1", 476 | "$(inherited)", 477 | ); 478 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 479 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 480 | GCC_WARN_UNDECLARED_SELECTOR = YES; 481 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 482 | GCC_WARN_UNUSED_FUNCTION = YES; 483 | GCC_WARN_UNUSED_VARIABLE = YES; 484 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 485 | MTL_ENABLE_DEBUG_INFO = YES; 486 | ONLY_ACTIVE_ARCH = YES; 487 | SDKROOT = iphoneos; 488 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 489 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 490 | SWIFT_VERSION = 4.2; 491 | VERSIONING_SYSTEM = "apple-generic"; 492 | VERSION_INFO_PREFIX = ""; 493 | }; 494 | name = Debug; 495 | }; 496 | 2657A3A01FA9939300A717A3 /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | ALWAYS_SEARCH_USER_PATHS = NO; 500 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 501 | CLANG_ANALYZER_NONNULL = YES; 502 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 503 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 504 | CLANG_CXX_LIBRARY = "libc++"; 505 | CLANG_ENABLE_MODULES = YES; 506 | CLANG_ENABLE_OBJC_ARC = YES; 507 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 508 | CLANG_WARN_BOOL_CONVERSION = YES; 509 | CLANG_WARN_COMMA = YES; 510 | CLANG_WARN_CONSTANT_CONVERSION = YES; 511 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 512 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 513 | CLANG_WARN_EMPTY_BODY = YES; 514 | CLANG_WARN_ENUM_CONVERSION = YES; 515 | CLANG_WARN_INFINITE_RECURSION = YES; 516 | CLANG_WARN_INT_CONVERSION = YES; 517 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 518 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 519 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 520 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 521 | CLANG_WARN_STRICT_PROTOTYPES = YES; 522 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 523 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 524 | CLANG_WARN_UNREACHABLE_CODE = YES; 525 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 526 | CODE_SIGN_IDENTITY = "iPhone Developer"; 527 | COPY_PHASE_STRIP = NO; 528 | CURRENT_PROJECT_VERSION = 1; 529 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 530 | ENABLE_NS_ASSERTIONS = NO; 531 | ENABLE_STRICT_OBJC_MSGSEND = YES; 532 | GCC_C_LANGUAGE_STANDARD = gnu11; 533 | GCC_NO_COMMON_BLOCKS = YES; 534 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 535 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 536 | GCC_WARN_UNDECLARED_SELECTOR = YES; 537 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 538 | GCC_WARN_UNUSED_FUNCTION = YES; 539 | GCC_WARN_UNUSED_VARIABLE = YES; 540 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 541 | MTL_ENABLE_DEBUG_INFO = NO; 542 | SDKROOT = iphoneos; 543 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 544 | SWIFT_VERSION = 4.2; 545 | VALIDATE_PRODUCT = YES; 546 | VERSIONING_SYSTEM = "apple-generic"; 547 | VERSION_INFO_PREFIX = ""; 548 | }; 549 | name = Release; 550 | }; 551 | 2657A3A21FA9939300A717A3 /* Debug */ = { 552 | isa = XCBuildConfiguration; 553 | buildSettings = { 554 | CLANG_ENABLE_MODULES = YES; 555 | CODE_SIGN_IDENTITY = ""; 556 | CODE_SIGN_STYLE = Automatic; 557 | DEFINES_MODULE = YES; 558 | DEVELOPMENT_TEAM = 2DUPQ924JY; 559 | DYLIB_COMPATIBILITY_VERSION = 1; 560 | DYLIB_CURRENT_VERSION = 1; 561 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 562 | INFOPLIST_FILE = GTMWebKit/Info.plist; 563 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 564 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 565 | PRODUCT_BUNDLE_IDENTIFIER = com.yang.GTMWebKit; 566 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 567 | SKIP_INSTALL = YES; 568 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 569 | SWIFT_VERSION = 4.2; 570 | TARGETED_DEVICE_FAMILY = "1,2"; 571 | }; 572 | name = Debug; 573 | }; 574 | 2657A3A31FA9939300A717A3 /* Release */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | CLANG_ENABLE_MODULES = YES; 578 | CODE_SIGN_IDENTITY = ""; 579 | CODE_SIGN_STYLE = Automatic; 580 | DEFINES_MODULE = YES; 581 | DEVELOPMENT_TEAM = 2DUPQ924JY; 582 | DYLIB_COMPATIBILITY_VERSION = 1; 583 | DYLIB_CURRENT_VERSION = 1; 584 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 585 | INFOPLIST_FILE = GTMWebKit/Info.plist; 586 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 587 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 588 | PRODUCT_BUNDLE_IDENTIFIER = com.yang.GTMWebKit; 589 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 590 | SKIP_INSTALL = YES; 591 | SWIFT_VERSION = 4.2; 592 | TARGETED_DEVICE_FAMILY = "1,2"; 593 | }; 594 | name = Release; 595 | }; 596 | 2657A3C61FA993EA00A717A3 /* Debug */ = { 597 | isa = XCBuildConfiguration; 598 | buildSettings = { 599 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 600 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 601 | CODE_SIGN_STYLE = Automatic; 602 | DEVELOPMENT_TEAM = 2DUPQ924JY; 603 | INFOPLIST_FILE = GTMWebKitExample/Info.plist; 604 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 605 | PRODUCT_BUNDLE_IDENTIFIER = com.yang.GTMWebKitExample; 606 | PRODUCT_NAME = "$(TARGET_NAME)"; 607 | SWIFT_VERSION = 4.2; 608 | TARGETED_DEVICE_FAMILY = 1; 609 | }; 610 | name = Debug; 611 | }; 612 | 2657A3C71FA993EA00A717A3 /* Release */ = { 613 | isa = XCBuildConfiguration; 614 | buildSettings = { 615 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 616 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 617 | CODE_SIGN_STYLE = Automatic; 618 | DEVELOPMENT_TEAM = 2DUPQ924JY; 619 | INFOPLIST_FILE = GTMWebKitExample/Info.plist; 620 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 621 | PRODUCT_BUNDLE_IDENTIFIER = com.yang.GTMWebKitExample; 622 | PRODUCT_NAME = "$(TARGET_NAME)"; 623 | SWIFT_VERSION = 4.2; 624 | TARGETED_DEVICE_FAMILY = 1; 625 | }; 626 | name = Release; 627 | }; 628 | /* End XCBuildConfiguration section */ 629 | 630 | /* Begin XCConfigurationList section */ 631 | 2657A3931FA9939300A717A3 /* Build configuration list for PBXProject "GTMWebKit" */ = { 632 | isa = XCConfigurationList; 633 | buildConfigurations = ( 634 | 2657A39F1FA9939300A717A3 /* Debug */, 635 | 2657A3A01FA9939300A717A3 /* Release */, 636 | ); 637 | defaultConfigurationIsVisible = 0; 638 | defaultConfigurationName = Release; 639 | }; 640 | 2657A3A11FA9939300A717A3 /* Build configuration list for PBXNativeTarget "GTMWebKit" */ = { 641 | isa = XCConfigurationList; 642 | buildConfigurations = ( 643 | 2657A3A21FA9939300A717A3 /* Debug */, 644 | 2657A3A31FA9939300A717A3 /* Release */, 645 | ); 646 | defaultConfigurationIsVisible = 0; 647 | defaultConfigurationName = Release; 648 | }; 649 | 2657A3C51FA993EA00A717A3 /* Build configuration list for PBXNativeTarget "GTMWebKitExample" */ = { 650 | isa = XCConfigurationList; 651 | buildConfigurations = ( 652 | 2657A3C61FA993EA00A717A3 /* Debug */, 653 | 2657A3C71FA993EA00A717A3 /* Release */, 654 | ); 655 | defaultConfigurationIsVisible = 0; 656 | defaultConfigurationName = Release; 657 | }; 658 | /* End XCConfigurationList section */ 659 | }; 660 | rootObject = 2657A3901FA9939300A717A3 /* Project object */; 661 | } 662 | --------------------------------------------------------------------------------