├── .gitattributes
├── snapshot
├── 001.png
├── 002.png
└── 003.png
├── WebViewLongPress
├── Vendor
│ ├── FSActionSheet
│ │ ├── FSActionSheetResources
│ │ │ ├── FSActionSheet_cancel@2x.png
│ │ │ └── FSActionSheet_cancel@3x.png
│ │ ├── FSActionSheetCell.h
│ │ ├── FSActionSheetItem.m
│ │ ├── FSActionSheetConfig.m
│ │ ├── FSActionSheet.h
│ │ ├── FSActionSheetItem.h
│ │ ├── FSActionSheetConfig.h
│ │ ├── FSActionSheetCell.m
│ │ └── FSActionSheet.m
│ └── RNCachingURLProtocol
│ │ ├── NSString+Sha1.h
│ │ ├── NSString+Sha1.m
│ │ ├── RNCachingURLProtocol.h
│ │ ├── Reachability.h
│ │ ├── Reachability.m
│ │ └── RNCachingURLProtocol.m
├── SwizzeMethod.h
├── MainViewController.h
├── AppDelegate.h
├── main.m
├── CVWebViewController.h
├── WKWebViewController.h
├── CVWebViewController+ImageHelper.h
├── MainViewController.m
├── SwizzeMethod.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── CVWebViewController.m
├── AppDelegate.m
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── WKWebViewController.m
└── CVWebViewController+ImageHelper.m
├── WebViewLongPress.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── .gitignore
├── WebViewLongPressTests
├── Info.plist
└── WebViewLongPressTests.m
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.strings text diff
2 | *.pbxproj merge=union
--------------------------------------------------------------------------------
/snapshot/001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoxiucai/WebViewLongPress/HEAD/snapshot/001.png
--------------------------------------------------------------------------------
/snapshot/002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoxiucai/WebViewLongPress/HEAD/snapshot/002.png
--------------------------------------------------------------------------------
/snapshot/003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoxiucai/WebViewLongPress/HEAD/snapshot/003.png
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetResources/FSActionSheet_cancel@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoxiucai/WebViewLongPress/HEAD/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetResources/FSActionSheet_cancel@2x.png
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetResources/FSActionSheet_cancel@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guoxiucai/WebViewLongPress/HEAD/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetResources/FSActionSheet_cancel@3x.png
--------------------------------------------------------------------------------
/WebViewLongPress.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WebViewLongPress/SwizzeMethod.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwizzeMethod.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | void SwizzlingMethod(Class c, SEL origSEL, SEL newSEL);
12 |
--------------------------------------------------------------------------------
/WebViewLongPress/MainViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 2017/2/15.
6 | // Copyright © 2017年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MainViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/WebViewLongPress/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/WebViewLongPress/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/WebViewLongPress/CVWebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // CVWebViewController.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface CVWebViewController : UIViewController
13 |
14 | @property (nonatomic, strong) NSURL *url;
15 |
16 | @property (nonatomic, strong) UIWebView *webView;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/NSString+Sha1.h:
--------------------------------------------------------------------------------
1 |
2 | #import
3 | #import
4 |
5 | /**
6 | * This extension contains several a helper
7 | * for creating a sha1 hash from instances of NSString
8 | */
9 | @interface NSString (Sha1)
10 |
11 | /**
12 | * Creates a SHA1 (hash) representation of NSString.
13 | *
14 | * @return NSString
15 | */
16 | - (NSString *)sha1;
17 |
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/WebViewLongPress/WKWebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WKWebViewController.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 2017/2/15.
6 | // Copyright © 2017年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface WKWebViewController : UIViewController
13 |
14 | @property (nonatomic, strong) NSURL *url;
15 |
16 | @property (nonatomic, strong) WKWebView *webView;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetCell.h
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/7/16.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "FSActionSheetConfig.h"
11 | @class FSActionSheetItem;
12 |
13 | @interface FSActionSheetCell : UITableViewCell
14 |
15 | @property (nonatomic, assign) FSContentAlignment contentAlignment;
16 | @property (nonatomic, strong) FSActionSheetItem *item;
17 | @property (nonatomic, assign) BOOL hideTopLine; ///< 是否隐藏顶部线条
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetItem.m
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 16/5/11.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import "FSActionSheetItem.h"
10 |
11 | @implementation FSActionSheetItem
12 |
13 | + (instancetype)itemWithType:(FSActionSheetType)type image:(UIImage *)image title:(NSString *)title tintColor:(UIColor *)tintColor {
14 |
15 | FSActionSheetItem *item = [[FSActionSheetItem alloc] init];
16 | item.type = type;
17 | item.image = image;
18 | item.title = title;
19 | item.tintColor = tintColor;
20 |
21 | return item;
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Objective-C ###
2 | # Xcode
3 | #
4 | build/
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | *.xccheckout
15 | *.moved-aside
16 | DerivedData
17 | *.hmap
18 | *.ipa
19 | *.xcuserstate
20 |
21 | ### OSX ###
22 | .DS_Store
23 | .AppleDouble
24 | .LSOverride
25 |
26 | # Icon must end with two \r
27 | Icon
28 |
29 |
30 | # Thumbnails
31 | ._*
32 |
33 | # Files that might appear on external disk
34 | .Spotlight-V100
35 | .Trashes
36 |
37 | # Directories potentially created on remote AFP share
38 | .AppleDB
39 | .AppleDesktop
40 | Network Trash Folder
41 | Temporary Items
42 | .apdisk
43 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/NSString+Sha1.m:
--------------------------------------------------------------------------------
1 |
2 | #import "NSString+Sha1.h"
3 |
4 | @implementation NSString (Sha1)
5 |
6 | - (NSString *)sha1
7 | {
8 | // see http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/
9 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
10 | uint8_t digest[CC_SHA1_DIGEST_LENGTH];
11 |
12 | CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
13 |
14 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
15 |
16 | for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
17 | [output appendFormat:@"%02x", digest[i]];
18 | }
19 |
20 | return output;
21 | }
22 |
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/WebViewLongPress/CVWebViewController+ImageHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // CVWebViewController+ImageHelper.h
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import "CVWebViewController.h"
10 | #import "FSActionSheet.h"
11 |
12 | @interface CVWebViewController (ImageHelper)
13 |
14 | /**
15 | * get image's url from this javascript
16 | */
17 | @property (nonatomic, strong) NSString *imageJS;
18 |
19 | /**
20 | * image's qr code string, if have
21 | * or nil
22 | */
23 | @property (nonatomic, strong) NSString *qrCodeString;
24 |
25 | /**
26 | * image
27 | */
28 | @property (strong, nonatomic) UIImage *image;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/WebViewLongPressTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WebViewLongPress/MainViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 2017/2/15.
6 | // Copyright © 2017年 cvte. All rights reserved.
7 | //
8 |
9 | #import "MainViewController.h"
10 |
11 | @interface MainViewController ()
12 |
13 | @end
14 |
15 | @implementation MainViewController
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 | self.navigationController.navigationBar.translucent = NO;
20 | // Do any additional setup after loading the view.
21 | }
22 |
23 | - (void)didReceiveMemoryWarning {
24 | [super didReceiveMemoryWarning];
25 | // Dispose of any resources that can be recreated.
26 | }
27 |
28 | /*
29 | #pragma mark - Navigation
30 |
31 | // In a storyboard-based application, you will often want to do a little preparation before navigation
32 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
33 | // Get the new view controller using [segue destinationViewController].
34 | // Pass the selected object to the new view controller.
35 | }
36 | */
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/WebViewLongPressTests/WebViewLongPressTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewLongPressTests.m
3 | // WebViewLongPressTests
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WebViewLongPressTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation WebViewLongPressTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetConfig.m
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/8/16.
6 | // Copyright © 2016 Steven. All rights reserved.
7 | //
8 |
9 | #import "FSActionSheetConfig.h"
10 |
11 | // float
12 | CGFloat const FSActionSheetDefaultMargin = 10; ///< 默认边距 (标题四边边距, 选项靠左或靠右时距离边缘的距离)
13 | CGFloat const FSActionSheetContentMaxScale = 0.65; ///< 弹窗内容高度与屏幕高度的默认比例
14 | CGFloat const FSActionSheetRowHeight = 52; ///< 行高
15 | CGFloat const FSActionSheetTitleLineSpacing = 2.5; ///< 标题行距
16 | CGFloat const FSActionSheetTitleKernSpacing = 0.5; ///< 标题字距
17 | CGFloat const FSActionSheetItemTitleFontSize = 16; ///< 选项文字字体大小, default is 16.
18 | CGFloat const FSActionSheetItemContentSpacing = 5; ///< 选项图片和文字的间距
19 | // color
20 | NSString * const FSActionSheetTitleColor = @"#888888"; ///< 标题颜色
21 | NSString * const FSActionSheetBackColor = @"#E8E8ED"; ///< 背景颜色
22 | NSString * const FSActionSheetRowNormalColor = @"#FBFBFE"; ///< 单元格背景颜色
23 | NSString * const FSActionSheetRowHighlightedColor = @"#F1F1F5"; ///< 选中高亮颜色
24 | NSString * const FSActionSheetRowTopLineColor = @"#D7D7D8"; ///< 单元格顶部线条颜色
25 | NSString * const FSActionSheetItemNormalColor = @"#000000"; ///< 选项默认颜色
26 | NSString * const FSActionSheetItemHighlightedColor = @"#E64340"; ///< 选项高亮颜色
27 |
--------------------------------------------------------------------------------
/WebViewLongPress/SwizzeMethod.m:
--------------------------------------------------------------------------------
1 | //
2 | // SwizzeMethod.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import "SwizzeMethod.h"
10 |
11 | #import
12 |
13 | /**
14 | * swizzle method implementation
15 | * instance or class methods supported.
16 | */
17 | void SwizzlingMethod(Class class, SEL originSEL, SEL swizzledSEL)
18 | {
19 | Method originMethod = class_getInstanceMethod(class, originSEL);
20 | Method swizzledMethod = nil;
21 |
22 | if (!originMethod) {
23 | originMethod = class_getClassMethod(class, originSEL);
24 | if (!originMethod) {
25 | return;
26 | }
27 | swizzledMethod = class_getClassMethod(class, swizzledSEL);
28 | if (!swizzledMethod) {
29 | return;
30 | }
31 | } else {
32 | swizzledMethod = class_getInstanceMethod(class, swizzledSEL);
33 | if (!swizzledMethod){
34 | return;
35 | }
36 | }
37 |
38 | if(class_addMethod(class, originSEL, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
39 | class_replaceMethod(class, swizzledSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
40 | } else {
41 | method_exchangeImplementations(originMethod, swizzledMethod);
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/WebViewLongPress/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/WebViewLongPress/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSAppTransportSecurity
6 |
7 | NSAllowsArbitraryLoads
8 |
9 |
10 | CFBundleDevelopmentRegion
11 | en
12 | CFBundleExecutable
13 | $(EXECUTABLE_NAME)
14 | CFBundleIdentifier
15 | $(PRODUCT_BUNDLE_IDENTIFIER)
16 | CFBundleInfoDictionaryVersion
17 | 6.0
18 | CFBundleName
19 | $(PRODUCT_NAME)
20 | CFBundlePackageType
21 | APPL
22 | CFBundleShortVersionString
23 | 1.0
24 | CFBundleSignature
25 | ????
26 | CFBundleVersion
27 | 1
28 | LSRequiresIPhoneOS
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UISupportedInterfaceOrientations~ipad
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationPortraitUpsideDown
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheet.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheet.h
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/7/16.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "FSActionSheetItem.h"
11 | #import "FSActionSheetConfig.h"
12 |
13 | @class FSActionSheet;
14 | @protocol FSActionSheetDelegate
15 |
16 | @optional
17 | - (void)FSActionSheet:(FSActionSheet *)actionSheet selectedIndex:(NSInteger)selectedIndex;
18 |
19 | @end
20 |
21 | @interface FSActionSheet : UIView
22 |
23 | @property (nonatomic, weak) id delegate; ///< 代理对象
24 | @property (nonatomic, assign) FSContentAlignment contentAlignment; ///< 默认是FSContentAlignmentCenter.
25 | @property (nonatomic, assign) BOOL hideOnTouchOutside; ///< 是否开启点击半透明层隐藏弹窗, 默认为YES.
26 |
27 | /*! @author Steven
28 | * @brief 单文本选项快速初始化
29 | * @param title 标题
30 | * @param delegate 代理
31 | * @param cancelButtonTitle 取消按钮标题
32 | * @param highlightedButtonTitle 高亮按钮标题
33 | * @param otherButtonTitles 其他按钮标题集合
34 | */
35 | - (instancetype)initWithTitle:(NSString *)title delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle highlightedButtonTitle:(NSString *)highlightedButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles;
36 |
37 | /*! @author Steven
38 | * @brief 在外部组装选项按钮item
39 | * @param title 标题
40 | * @param cancelTitle 取消按钮标题
41 | * @param items 选项按钮item
42 | */
43 | - (instancetype)initWithTitle:(NSString *)title cancelTitle:(NSString *)cancelTitle items:(NSArray *)items;
44 |
45 | /*! @author Steven
46 | * @brief 单展示, 不绑定block回调
47 | */
48 | - (void)show;
49 |
50 | /*! @author Steven
51 | * @brief 展示并绑定block回调
52 | * @param selectedHandler 选择选项按钮回调.
53 | */
54 | - (void)showWithSelectedCompletion:(FSActionSheetHandler)selectedHandler;
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/WebViewLongPress/CVWebViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // CVWebViewController.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import "CVWebViewController.h"
10 |
11 | @implementation CVWebViewController
12 |
13 | - (void)viewDidLoad
14 | {
15 | [super viewDidLoad];
16 |
17 | self.title = @"UIWebView";
18 |
19 | NSString *urlString = @"mp.weixin.qq.com/s?__biz=MzI2ODAzODAzMw==&mid=2650057120&idx=2&sn=c875f7d03ea3823e8dcb3dc4d0cff51d&scene=0#wechat_redirect";
20 |
21 | self.url = [self autoFillURL:[NSURL URLWithString:urlString]];
22 |
23 | [self.view addSubview:self.webView];
24 |
25 | [self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.url]];
26 | }
27 |
28 | - (UIWebView *)webView
29 | {
30 | if (_webView == nil) {
31 | _webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
32 | _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
33 | _webView.delegate = self;
34 | }
35 | return _webView;
36 | }
37 |
38 | #pragma mark - UIWebViewDelegate
39 |
40 | - (void)webViewDidStartLoad:(UIWebView *)webView
41 | {
42 | NSLog(@"start load");
43 | }
44 |
45 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
46 | {
47 | return YES;
48 | }
49 |
50 | - (void)webViewDidFinishLoad:(UIWebView *)webView
51 | {
52 | NSLog(@"finish load");
53 | }
54 |
55 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
56 | {
57 | NSLog(@"load fail");
58 | }
59 |
60 | #pragma mark - private methods
61 |
62 | - (NSURL *)autoFillURL:(NSURL *)url
63 | {
64 | //If no URL scheme was supplied, defer back to HTTP.
65 | if (url.scheme.length == 0) {
66 | url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", [url absoluteString]]];
67 | }
68 |
69 | return url;
70 | }
71 |
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/WebViewLongPress/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "RNCachingURLProtocol.h"
11 | #import "CVWebViewController+ImageHelper.h"
12 |
13 | @interface AppDelegate ()
14 |
15 | @end
16 |
17 | @implementation AppDelegate
18 |
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 | // Override point for customization after application launch.
21 | [NSURLProtocol registerClass:[RNCachingURLProtocol class]];
22 | return YES;
23 | }
24 |
25 | - (void)applicationWillResignActive:(UIApplication *)application {
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 throttle down OpenGL ES frame rates. Games should use this method to pause the game.
28 | }
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
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 | - (void)applicationWillEnterForeground:(UIApplication *)application {
36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
37 | }
38 |
39 | - (void)applicationDidBecomeActive:(UIApplication *)application {
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 | - (void)applicationWillTerminate:(UIApplication *)application {
44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetItem.h
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 16/5/11.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "FSActionSheetConfig.h"
11 |
12 | @interface FSActionSheetItem : NSObject
13 |
14 | @property (nonatomic, assign) FSActionSheetType type; ///< 选项类型, 有 默认 和 高亮 两种类型.
15 | @property (nonatomic, strong) UIImage *image; ///< 选项图标, 建议image的size为 @2x: 46x46, @3x: 69x69.
16 | @property (nonatomic, copy) NSString *title; ///< 选项标题
17 | @property (nonatomic, strong) UIColor *tintColor; ///< 选项前景色, 如果设置了这个颜色的话, 则无论选项设置的图标是什么颜色都会被修改为当前设置的这个颜色,
18 | ///< 同时这个颜色也会是标题的文本颜色.
19 |
20 | + (instancetype)itemWithType:(FSActionSheetType)type image:(UIImage *)image title:(NSString *)title tintColor:(UIColor *)tintColor;
21 |
22 | @end
23 |
24 | /*! @author Steven
25 | * @brief 单标题的选项
26 | * @param type 类型
27 | * @param title 标题
28 | */
29 | NS_INLINE FSActionSheetItem *FSActionSheetTitleItemMake(FSActionSheetType type, NSString *title) {
30 | return [FSActionSheetItem itemWithType:type image:nil title:title tintColor:nil];
31 | }
32 |
33 | /*! @author Steven
34 | * @brief 标题和图标的选项
35 | * @param type 类型
36 | * @param image 图片
37 | * @param title 标题
38 | */
39 | NS_INLINE FSActionSheetItem *FSActionSheetTitleWithImageItemMake(FSActionSheetType type, UIImage *image, NSString *title) {
40 | return [FSActionSheetItem itemWithType:type image:image title:title tintColor:nil];
41 | }
42 |
43 | /*! @author Steven
44 | * @brief 单标题且自定义前景色的选项
45 | * @param type 类型
46 | * @param title 标题
47 | * @param tintColor 自定义前景色
48 | */
49 | NS_INLINE FSActionSheetItem *FSActionSheetTitleWithColorItemMake(FSActionSheetType type, NSString *title, UIColor *tintColor) {
50 | return [FSActionSheetItem itemWithType:type image:nil title:title tintColor:tintColor];
51 | }
52 |
53 | /*! @author Steven
54 | * @brief 标题和图片并且自定义前景色的选项
55 | * @param type 类型
56 | * @param title 标题
57 | * @param image 图片
58 | * @param tintColor 自定义前景色
59 | */
60 | NS_INLINE FSActionSheetItem *FSActionSheetItemMake(FSActionSheetType type, UIImage *image, NSString *title, UIColor *tintColor) {
61 | return [FSActionSheetItem itemWithType:type image:image title:title tintColor:tintColor];
62 | }
63 |
64 |
65 |
--------------------------------------------------------------------------------
/WebViewLongPress/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 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/RNCachingURLProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // RNCachingURLProtocol.h
3 | //
4 | // Created by Robert Napier on 1/10/12.
5 | // Copyright (c) 2012 Rob Napier. All rights reserved.
6 | //
7 | // This code is licensed under the MIT License:
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a
10 | // copy of this software and associated documentation files (the "Software"),
11 | // to deal in the Software without restriction, including without limitation
12 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 | // and/or sell copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 | // DEALINGS IN THE SOFTWARE.
26 | //
27 |
28 | // RNCachingURLProtocol is a simple shim for the HTTP protocol (that’s not
29 | // nearly as scary as it sounds). Anytime a URL is download, the response is
30 | // cached to disk. Anytime a URL is requested, if we’re online then things
31 | // proceed normally. If we’re offline, then we retrieve the cached version.
32 | //
33 | // The point of RNCachingURLProtocol is mostly to demonstrate how this is done.
34 | // The current implementation is extremely simple. In particular, it doesn’t
35 | // worry about cleaning up the cache. The assumption is that you’re caching just
36 | // a few simple things, like your “Latest News” page (which was the problem I
37 | // was solving). It caches all HTTP traffic, so without some modifications, it’s
38 | // not appropriate for an app that has a lot of HTTP connections (see
39 | // MKNetworkKit for that). But if you need to cache some URLs and not others,
40 | // that is easy to implement.
41 | //
42 | // You should also look at [AFCache](https://github.com/artifacts/AFCache) for a
43 | // more powerful caching engine that is currently integrating the ideas of
44 | // RNCachingURLProtocol.
45 | //
46 | // A quick rundown of how to use it:
47 | //
48 | // 1. To build, you will need the Reachability code from Apple (included). That requires that you link with
49 | // `SystemConfiguration.framework`.
50 | //
51 | // 2. At some point early in the program (application:didFinishLaunchingWithOptions:),
52 | // call the following:
53 | //
54 | // `[NSURLProtocol registerClass:[RNCachingURLProtocol class]];`
55 | //
56 | // 3. There is no step 3.
57 | //
58 | // For more details see
59 | // [Drop-in offline caching for UIWebView (and NSURLProtocol)](http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588).
60 |
61 | #import
62 |
63 | @interface RNCachingURLProtocol : NSURLProtocol
64 |
65 | + (NSSet *)supportedSchemes;
66 | + (void)setSupportedSchemes:(NSSet *)supportedSchemes;
67 |
68 | /**
69 | * Add to support url string.
70 | */
71 | + (NSString *)cachePathForURLString:(NSString *)url;
72 |
73 | - (NSString *)cachePathForRequest:(NSURLRequest *)aRequest;
74 | - (BOOL) useCache;
75 |
76 | @end
77 |
78 | @interface RNCachedData : NSObject
79 | @property (nonatomic, readwrite, strong) NSData *data;
80 | @property (nonatomic, readwrite, strong) NSURLResponse *response;
81 | @property (nonatomic, readwrite, strong) NSURLRequest *redirectRequest;
82 | @end
83 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetConfig.h
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/7/16.
6 | // Copyright © 2016 Steven. All rights reserved.
7 | //
8 |
9 |
10 | #import
11 | #import
12 |
13 | // 选择选项block回调
14 | typedef void(^FSActionSheetHandler)(NSInteger selectedIndex);
15 |
16 | // 选项类型枚举
17 | typedef NS_ENUM(NSInteger, FSActionSheetType) {
18 | FSActionSheetTypeNormal = 0,
19 | FSActionSheetTypeHighlighted,
20 | };
21 |
22 | // 内容偏移枚举
23 | typedef NS_ENUM(NSInteger, FSContentAlignment) {
24 | FSContentAlignmentLeft = 0, ///< 内容紧靠左边
25 | FSContentAlignmentCenter, ///< 内容居中
26 | FSContentAlignmentRight, ///< 内容紧靠右边
27 | };
28 |
29 |
30 | // float
31 | UIKIT_EXTERN CGFloat const FSActionSheetDefaultMargin; ///< 默认边距 (标题四边边距, 选项靠左或靠右时距离边缘的距离), default is 10.
32 | UIKIT_EXTERN CGFloat const FSActionSheetContentMaxScale; ///< 弹窗内容高度与屏幕高度的默认比例, default is 0.65.
33 | UIKIT_EXTERN CGFloat const FSActionSheetRowHeight; ///< 行高, default is 44.
34 | UIKIT_EXTERN CGFloat const FSActionSheetTitleLineSpacing; ///< 标题行距, default is 2.5.
35 | UIKIT_EXTERN CGFloat const FSActionSheetTitleKernSpacing; ///< 标题字距, default is 0.5.
36 | UIKIT_EXTERN CGFloat const FSActionSheetItemTitleFontSize; ///< 选项文字字体大小, default is 16.
37 | UIKIT_EXTERN CGFloat const FSActionSheetItemContentSpacing; ///< 选项图片和文字的间距, default is 5.
38 | // color
39 | UIKIT_EXTERN NSString * const FSActionSheetTitleColor; ///< 标题颜色, default is #888888
40 | UIKIT_EXTERN NSString * const FSActionSheetBackColor; ///< 背景颜色, default is #E8E8ED
41 | UIKIT_EXTERN NSString * const FSActionSheetRowNormalColor; ///< 单元格背景颜色, default is #FBFBFE
42 | UIKIT_EXTERN NSString * const FSActionSheetRowHighlightedColor; ///< 选中高亮颜色, default is #F1F1F5
43 | UIKIT_EXTERN NSString * const FSActionSheetRowTopLineColor; ///< 单元格顶部线条颜色, default is #D7D7D8
44 | UIKIT_EXTERN NSString * const FSActionSheetItemNormalColor; ///< 选项默认颜色, default is #000000
45 | UIKIT_EXTERN NSString * const FSActionSheetItemHighlightedColor; ///< 选项高亮颜色, default is #E64340
46 |
47 | /*! @author Steven
48 | * @brief 获取颜色
49 | * @param aColorString 十六进制颜色字符串
50 | */
51 | NS_INLINE UIColor *FSColorWithString(NSString *aColorString) {
52 | if (aColorString.length == 0) {
53 | return nil;
54 | }
55 |
56 | if ([aColorString hasPrefix:@"#"]) {
57 | aColorString = [aColorString substringFromIndex:1];
58 | }
59 |
60 | if (aColorString.length == 6) {
61 | int len = (int)aColorString.length/3;
62 | unsigned int a[3];
63 | for (int i=0; i<3; i++) {
64 | NSRange range;
65 | range.location = i*len;
66 | range.length = len;
67 | NSString *str = [aColorString substringWithRange:range];
68 | [[NSScanner scannerWithString:str] scanHexInt:a+i];
69 | if (len == 1) {
70 | a[i] *= 17;
71 | }
72 | }
73 | return [UIColor colorWithRed:a[0]/255.0f green:a[1]/255.0f blue:a[2]/255.0f alpha:1];
74 | }
75 | else if (aColorString.length == 8) {
76 | int len = (int)aColorString.length/4;
77 | unsigned int a[4];
78 | for (int i=0; i<4; i++) {
79 | NSRange range;
80 | range.location = i*len;
81 | range.length = len;
82 | NSString *str = [aColorString substringWithRange:range];
83 | [[NSScanner scannerWithString:str] scanHexInt:a+i];
84 | if (len == 1) {
85 | a[i] *= 17;
86 | }
87 | }
88 | return [UIColor colorWithRed:a[0]/255.0f green:a[1]/255.0f blue:a[2]/255.0f alpha:a[3]/255.0f];
89 | }
90 | else if (aColorString.length <= 2) {
91 | unsigned int gray;
92 | [[NSScanner scannerWithString:aColorString] scanHexInt:&gray];
93 | if (aColorString.length == 1)
94 | {
95 | gray *= 17;
96 | }
97 | return [UIColor colorWithWhite:gray/255.0f alpha:1];
98 | }
99 |
100 | return nil;
101 | }
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/Reachability.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | File: Reachability.h
4 | Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
5 |
6 | Version: 2.2
7 |
8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
9 | ("Apple") in consideration of your agreement to the following terms, and your
10 | use, installation, modification or redistribution of this Apple software
11 | constitutes acceptance of these terms. If you do not agree with these terms,
12 | please do not use, install, modify or redistribute this Apple software.
13 |
14 | In consideration of your agreement to abide by the following terms, and subject
15 | to these terms, Apple grants you a personal, non-exclusive license, under
16 | Apple's copyrights in this original Apple software (the "Apple Software"), to
17 | use, reproduce, modify and redistribute the Apple Software, with or without
18 | modifications, in source and/or binary forms; provided that if you redistribute
19 | the Apple Software in its entirety and without modifications, you must retain
20 | this notice and the following text and disclaimers in all such redistributions
21 | of the Apple Software.
22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used
23 | to endorse or promote products derived from the Apple Software without specific
24 | prior written permission from Apple. Except as expressly stated in this notice,
25 | no other rights or licenses, express or implied, are granted by Apple herein,
26 | including but not limited to any patent rights that may be infringed by your
27 | derivative works or by other works in which the Apple Software may be
28 | incorporated.
29 |
30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
34 | COMBINATION WITH YOUR PRODUCTS.
35 |
36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 |
44 | Copyright (C) 2010 Apple Inc. All Rights Reserved.
45 |
46 | */
47 |
48 |
49 | #import
50 | #import
51 | #import
52 |
53 | typedef enum
54 | {
55 | NotReachable = 0,
56 | ReachableViaWiFi,
57 | ReachableViaWWAN
58 | } NetworkStatus;
59 | #define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification"
60 |
61 | @interface Reachability : NSObject
62 | {
63 | BOOL localWiFiRef;
64 | SCNetworkReachabilityRef reachabilityRef;
65 | }
66 |
67 | //reachabilityWithHostName- Use to check the reachability of a particular host name.
68 | + (Reachability *)reachabilityWithHostName:(NSString *)hostName;
69 |
70 | //reachabilityWithAddress- Use to check the reachability of a particular IP address.
71 | + (Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
72 |
73 | //reachabilityForInternetConnection- checks whether the default route is available.
74 | // Should be used by applications that do not connect to a particular host
75 | + (Reachability *)reachabilityForInternetConnection;
76 |
77 | //reachabilityForLocalWiFi- checks whether a local wifi connection is available.
78 | + (Reachability *)reachabilityForLocalWiFi;
79 |
80 | //Start listening for reachability notifications on the current run loop
81 | - (BOOL)startNotifier;
82 | - (void)stopNotifier;
83 |
84 | - (NetworkStatus)currentReachabilityStatus;
85 | //WWAN may be available, but not active until a connection has been established.
86 | //WiFi may require a connection for VPN on Demand.
87 | - (BOOL)connectionRequired;
88 | @end
89 |
90 |
91 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheetCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheetCell.m
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/7/16.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import "FSActionSheetCell.h"
10 | #import "FSActionSheetItem.h"
11 |
12 | @interface FSActionSheetCell ()
13 |
14 | @property (nonatomic, strong) UIButton *titleButton;
15 | @property (nonatomic, weak) UIView *topLine; ///< 顶部线条
16 |
17 | @end
18 |
19 | @implementation FSActionSheetCell
20 |
21 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
22 | [super setHighlighted:highlighted animated:animated];
23 |
24 | if (highlighted) {
25 | self.contentView.backgroundColor = FSColorWithString(FSActionSheetRowHighlightedColor);
26 | } else {
27 | [UIView animateWithDuration:0.25 animations:^{
28 | self.contentView.backgroundColor = self.backgroundColor;
29 | }];
30 | }
31 | }
32 |
33 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
34 | if (!(self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) return nil;
35 |
36 | self.backgroundColor = FSColorWithString(FSActionSheetRowNormalColor);
37 | self.contentView.backgroundColor = self.backgroundColor;
38 | self.selectionStyle = UITableViewCellSelectionStyleNone;
39 | _contentAlignment = FSContentAlignmentCenter;
40 |
41 | [self setupSubviews];
42 |
43 | return self;
44 | }
45 |
46 | - (void)setupSubviews {
47 | _titleButton = [UIButton buttonWithType:UIButtonTypeSystem];
48 | _titleButton.tintColor = FSColorWithString(FSActionSheetItemNormalColor);
49 | _titleButton.titleLabel.font = [UIFont systemFontOfSize:FSActionSheetItemTitleFontSize];
50 | _titleButton.userInteractionEnabled = NO;
51 | _titleButton.translatesAutoresizingMaskIntoConstraints = NO;
52 | [self.contentView addSubview:_titleButton];
53 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_titleButton]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_titleButton)]];
54 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_titleButton]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_titleButton)]];
55 |
56 | // 顶部线条
57 | UIView *topLine = [[UIView alloc] init];
58 | topLine.backgroundColor = FSColorWithString(FSActionSheetRowTopLineColor);
59 | topLine.translatesAutoresizingMaskIntoConstraints = NO;
60 | [self.contentView addSubview:topLine];
61 | self.topLine = topLine;
62 | CGFloat lineHeight = 1/[UIScreen mainScreen].scale; ///< 线条高度
63 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[topLine]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(topLine)]];
64 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topLine(lineHeight)]" options:0 metrics:@{@"lineHeight":@(lineHeight)} views:NSDictionaryOfVariableBindings(topLine)]];
65 | }
66 |
67 | - (void)setItem:(FSActionSheetItem *)item {
68 | _item = item;
69 |
70 | // 前景色设置, 如果有自定义前景色则使用自定义的前景色, 否则使用预配置的颜色值.
71 | UIColor *tintColor;
72 | if (item.tintColor) {
73 | tintColor = item.tintColor;
74 | } else {
75 | if (_item.type == FSActionSheetTypeNormal) {
76 | tintColor = FSColorWithString(FSActionSheetItemNormalColor);
77 | } else {
78 | tintColor = FSColorWithString(FSActionSheetItemHighlightedColor);
79 | }
80 | }
81 | _titleButton.tintColor = tintColor;
82 |
83 | // 调整图片与标题的间距
84 | _titleButton.imageEdgeInsets = UIEdgeInsetsMake(0, _item.image?-FSActionSheetItemContentSpacing/2:0,
85 | _item.image?1:0, _item.image?FSActionSheetItemContentSpacing/2:0);
86 | _titleButton.titleEdgeInsets = UIEdgeInsetsMake(_item.image?1:0, _item.image?FSActionSheetItemContentSpacing/2:0,
87 | 0, _item.image?-FSActionSheetItemContentSpacing/2:0);
88 | // 设置图片与标题
89 | [_titleButton setTitle:item.title forState:UIControlStateNormal];
90 | [_titleButton setImage:item.image forState:UIControlStateNormal];
91 | }
92 |
93 | - (void)setContentAlignment:(FSContentAlignment)contentAlignment {
94 | if (_contentAlignment == contentAlignment) return;
95 |
96 | _contentAlignment = contentAlignment;
97 | // 更新button的图片和标题Edge
98 | [self updateButtonContentEdge];
99 | // 设置内容偏移
100 | switch (_contentAlignment) {
101 | // 局左
102 | case FSContentAlignmentLeft: {
103 | _titleButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
104 | _titleButton.contentEdgeInsets = UIEdgeInsetsMake(0, FSActionSheetDefaultMargin, 0, -FSActionSheetDefaultMargin);
105 | break;
106 | }
107 | // 居中
108 | case FSContentAlignmentCenter: {
109 | _titleButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
110 | _titleButton.contentEdgeInsets = UIEdgeInsetsZero;
111 | break;
112 | }
113 | // 居右
114 | case FSContentAlignmentRight: {
115 | _titleButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
116 | _titleButton.contentEdgeInsets = UIEdgeInsetsMake(0, -FSActionSheetDefaultMargin, 0, FSActionSheetDefaultMargin);
117 | break;
118 | }
119 | }
120 | }
121 |
122 | // 更新button图片与标题的edge
123 | - (void)updateButtonContentEdge {
124 | if (!_item.image) return;
125 | if (_contentAlignment == FSContentAlignmentRight) {
126 | CGFloat titleWidth = [[_titleButton titleForState:UIControlStateNormal] sizeWithAttributes:@{NSFontAttributeName:_titleButton.titleLabel.font}].width;
127 | _titleButton.imageEdgeInsets = UIEdgeInsetsMake(0, titleWidth, 1, -titleWidth);
128 | _titleButton.titleEdgeInsets = UIEdgeInsetsMake(1, -_item.image.size.width-FSActionSheetItemContentSpacing,
129 | 0, _item.image.size.width+FSActionSheetItemContentSpacing);
130 | } else {
131 | _titleButton.imageEdgeInsets = UIEdgeInsetsMake(0, -FSActionSheetItemContentSpacing/2, 1, FSActionSheetItemContentSpacing/2);
132 | _titleButton.titleEdgeInsets = UIEdgeInsetsMake(1, FSActionSheetItemContentSpacing/2, 0, -FSActionSheetItemContentSpacing/2);
133 | }
134 | }
135 |
136 | - (void)setHideTopLine:(BOOL)hideTopLine {
137 | _topLine.hidden = hideTopLine;
138 | }
139 |
140 | @end
141 |
142 |
--------------------------------------------------------------------------------
/WebViewLongPress/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
33 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/Reachability.m:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | File: Reachability.m
4 | Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
5 |
6 | Version: 2.2
7 |
8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
9 | ("Apple") in consideration of your agreement to the following terms, and your
10 | use, installation, modification or redistribution of this Apple software
11 | constitutes acceptance of these terms. If you do not agree with these terms,
12 | please do not use, install, modify or redistribute this Apple software.
13 |
14 | In consideration of your agreement to abide by the following terms, and subject
15 | to these terms, Apple grants you a personal, non-exclusive license, under
16 | Apple's copyrights in this original Apple software (the "Apple Software"), to
17 | use, reproduce, modify and redistribute the Apple Software, with or without
18 | modifications, in source and/or binary forms; provided that if you redistribute
19 | the Apple Software in its entirety and without modifications, you must retain
20 | this notice and the following text and disclaimers in all such redistributions
21 | of the Apple Software.
22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used
23 | to endorse or promote products derived from the Apple Software without specific
24 | prior written permission from Apple. Except as expressly stated in this notice,
25 | no other rights or licenses, express or implied, are granted by Apple herein,
26 | including but not limited to any patent rights that may be infringed by your
27 | derivative works or by other works in which the Apple Software may be
28 | incorporated.
29 |
30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
34 | COMBINATION WITH YOUR PRODUCTS.
35 |
36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 |
44 | Copyright (C) 2010 Apple Inc. All Rights Reserved.
45 |
46 | */
47 |
48 | #import
49 | #import
50 |
51 | #import "Reachability.h"
52 |
53 | #define kShouldPrintReachabilityFlags 0
54 |
55 | static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char *comment) {
56 | #if kShouldPrintReachabilityFlags
57 |
58 | (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
59 | (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
60 |
61 | (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
62 | (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
63 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
64 | (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
65 | (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
66 | (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
67 | (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
68 | comment
69 | );
70 | #endif
71 | }
72 |
73 |
74 | @implementation Reachability
75 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
76 | #pragma unused (target, flags)
77 | NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
78 | NSCAssert([(__bridge id)info isKindOfClass:[Reachability class]], @"info was wrong class in ReachabilityCallback");
79 |
80 | Reachability *noteObject = (__bridge Reachability *)info;
81 | // Post a notification to notify the client that the network reachability changed.
82 | [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
83 | }
84 |
85 | - (BOOL)startNotifier
86 | {
87 | BOOL retVal = NO;
88 | SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
89 | if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) {
90 | if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
91 | retVal = YES;
92 | }
93 | }
94 | return retVal;
95 | }
96 |
97 | - (void)stopNotifier
98 | {
99 | if (reachabilityRef != NULL) {
100 | SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
101 | }
102 | }
103 |
104 | - (void)dealloc
105 | {
106 | [self stopNotifier];
107 | if (reachabilityRef != NULL) {
108 | CFRelease(reachabilityRef);
109 | }
110 | }
111 |
112 | + (Reachability *)reachabilityWithHostName:(NSString *)hostName;
113 | {
114 | Reachability *retVal = NULL;
115 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
116 | if (reachability != NULL) {
117 | retVal = [[self alloc] init];
118 | if (retVal != NULL) {
119 | retVal->reachabilityRef = reachability;
120 | retVal->localWiFiRef = NO;
121 | }
122 | }
123 | return retVal;
124 | }
125 |
126 | + (Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
127 | {
128 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
129 | Reachability *retVal = NULL;
130 | if (reachability != NULL) {
131 | retVal = [[self alloc] init];
132 | if (retVal != NULL) {
133 | retVal->reachabilityRef = reachability;
134 | retVal->localWiFiRef = NO;
135 | }
136 | }
137 | return retVal;
138 | }
139 |
140 | + (Reachability *)reachabilityForInternetConnection;
141 | {
142 | struct sockaddr_in zeroAddress;
143 | bzero(&zeroAddress, sizeof(zeroAddress));
144 | zeroAddress.sin_len = sizeof(zeroAddress);
145 | zeroAddress.sin_family = AF_INET;
146 | return [self reachabilityWithAddress:&zeroAddress];
147 | }
148 |
149 | + (Reachability *)reachabilityForLocalWiFi;
150 | {
151 | struct sockaddr_in localWifiAddress;
152 | bzero(&localWifiAddress, sizeof(localWifiAddress));
153 | localWifiAddress.sin_len = sizeof(localWifiAddress);
154 | localWifiAddress.sin_family = AF_INET;
155 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0
156 | localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
157 | Reachability *retVal = [self reachabilityWithAddress:&localWifiAddress];
158 | if (retVal != NULL) {
159 | retVal->localWiFiRef = YES;
160 | }
161 | return retVal;
162 | }
163 |
164 | #pragma mark Network Flag Handling
165 |
166 | - (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
167 | {
168 | PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
169 |
170 | BOOL retVal = NotReachable;
171 | if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) {
172 | retVal = ReachableViaWiFi;
173 | }
174 | return retVal;
175 | }
176 |
177 | - (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
178 | {
179 | PrintReachabilityFlags(flags, "networkStatusForFlags");
180 | if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
181 | // if target host is not reachable
182 | return NotReachable;
183 | }
184 |
185 | BOOL retVal = NotReachable;
186 |
187 | if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
188 | // if target host is reachable and no connection is required
189 | // then we'll assume (for now) that your on Wi-Fi
190 | retVal = ReachableViaWiFi;
191 | }
192 |
193 |
194 | if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
195 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
196 | // ... and the connection is on-demand (or on-traffic) if the
197 | // calling application is using the CFSocketStream or higher APIs
198 |
199 | if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
200 | // ... and no [user] intervention is needed
201 | retVal = ReachableViaWiFi;
202 | }
203 | }
204 |
205 | if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
206 | // ... but WWAN connections are OK if the calling application
207 | // is using the CFNetwork (CFSocketStream?) APIs.
208 | retVal = ReachableViaWWAN;
209 | }
210 | return retVal;
211 | }
212 |
213 | - (BOOL)connectionRequired;
214 | {
215 | NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
216 | SCNetworkReachabilityFlags flags;
217 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
218 | return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
219 | }
220 | return NO;
221 | }
222 |
223 | - (NetworkStatus)currentReachabilityStatus
224 | {
225 | NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
226 | NetworkStatus retVal = NotReachable;
227 | SCNetworkReachabilityFlags flags;
228 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
229 | if (localWiFiRef) {
230 | retVal = [self localWiFiStatusForFlags:flags];
231 | }
232 | else {
233 | retVal = [self networkStatusForFlags:flags];
234 | }
235 | }
236 | return retVal;
237 | }
238 | @end
239 |
--------------------------------------------------------------------------------
/WebViewLongPress/WKWebViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WKWebViewController.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 2017/2/15.
6 | // Copyright © 2017年 cvte. All rights reserved.
7 | //
8 |
9 | #import "WKWebViewController.h"
10 | #import "FSActionSheet.h"
11 |
12 | #define iOS7_OR_EARLY ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0)
13 |
14 | static const NSTimeInterval KLongGestureInterval = 0.6f;
15 |
16 | typedef NS_ENUM(NSInteger, WKSelectItem) {
17 | WKSelectItemSaveImage,
18 | WKSelectItemQRExtract
19 | };
20 |
21 | @interface WKWebViewController ()
22 |
23 | @property (nonatomic, strong) NSString *qrCodeString;
24 |
25 | @property (nonatomic, strong) UIImage *saveimage;
26 |
27 | @end
28 |
29 | @implementation WKWebViewController
30 |
31 |
32 | - (void)viewDidLoad
33 | {
34 | [super viewDidLoad];
35 |
36 | self.title = @"WKWebView";
37 |
38 | NSString *urlString = @"mp.weixin.qq.com/s?__biz=MzI2ODAzODAzMw==&mid=2650057120&idx=2&sn=c875f7d03ea3823e8dcb3dc4d0cff51d&scene=0#wechat_redirect";
39 |
40 | self.url = [self autoFillURL:[NSURL URLWithString:urlString]];
41 |
42 | [self.view addSubview:self.webView];
43 |
44 | [self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.url]];
45 | }
46 |
47 | - (WKWebView *)webView
48 | {
49 | if (_webView == nil) {
50 |
51 | // this js script help to disable callouts in WKWebView.
52 | NSString *source = @"var style = document.createElement('style'); \
53 | style.type = 'text/css'; \
54 | style.innerText = '*:not(input):not(textarea) { -webkit-user-select: none; -webkit-touch-callout: none; }'; \
55 | var head = document.getElementsByTagName('head')[0];\
56 | head.appendChild(style);";
57 |
58 | WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
59 |
60 | // Create the user content controller and add the script to it
61 | WKUserContentController *userContentController = [WKUserContentController new];
62 | [userContentController addUserScript:script];
63 |
64 | // Create the configuration with the user content controller
65 | WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
66 | configuration.userContentController = userContentController;
67 |
68 |
69 | //begin
70 | //I don't know why? When I add this code snip, it works fine! Otherwise, it works bad.
71 | UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
72 | [self.view addSubview:view];
73 | // end
74 |
75 | _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
76 | _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
77 | _webView.contentMode = UIViewContentModeRedraw;
78 | _webView.navigationDelegate = self;
79 | _webView.allowsBackForwardNavigationGestures = YES;
80 |
81 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
82 | longPress.minimumPressDuration = KLongGestureInterval;
83 | longPress.allowableMovement = 20.f;
84 | longPress.delegate = self;
85 | [_webView addGestureRecognizer:longPress];
86 | }
87 | return _webView;
88 | }
89 |
90 |
91 | #pragma mark - WKNavigationDelegate
92 |
93 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
94 | {
95 | NSLog(@"start load");
96 | }
97 |
98 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
99 | {
100 | NSLog(@"fail load");
101 | }
102 |
103 | - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
104 | {
105 | NSLog(@"fali navigation");
106 | }
107 |
108 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
109 | {
110 | NSLog(@"finish load");
111 | }
112 |
113 | #pragma mark - UIGestureRecognizerDelegate
114 |
115 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
116 | {
117 | return YES;
118 | }
119 |
120 | #pragma mark - FSActionSheetDelegate
121 |
122 | - (void)FSActionSheet:(FSActionSheet *)actionSheet selectedIndex:(NSInteger)selectedIndex
123 | {
124 | switch (selectedIndex) {
125 | case WKSelectItemSaveImage:
126 | {
127 | UIImageWriteToSavedPhotosAlbum(self.saveimage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
128 | }
129 | break;
130 | case WKSelectItemQRExtract:
131 | {
132 | NSURL *qrUrl = [NSURL URLWithString:self.qrCodeString];
133 | //open with safari
134 | if ([[UIApplication sharedApplication] canOpenURL:qrUrl]) {
135 | [[UIApplication sharedApplication] openURL:qrUrl];
136 | }
137 | // open in inner webview
138 | //[self.webView loadRequest:[NSURLRequest requestWithURL:qrUrl]];
139 | }
140 | break;
141 |
142 | default:
143 | break;
144 | }
145 | }
146 |
147 | #pragma mark - Save image callback
148 |
149 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
150 | {
151 | NSString *message = @"Succeed";
152 |
153 | if (error) {
154 | message = @"Fail";
155 | }
156 | NSLog(@"save result :%@", message);
157 | }
158 |
159 | #pragma mark - Private methods
160 |
161 | - (void)handleLongPress:(UILongPressGestureRecognizer *)sender
162 | {
163 | if (sender.state != UIGestureRecognizerStateBegan) {
164 | return;
165 | }
166 |
167 | CGPoint touchPoint = [sender locationInView:self.webView];
168 | // get image url where pressed.
169 | NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
170 |
171 | [self.webView evaluateJavaScript:imgJS completionHandler:^(id _Nullable imageUrl, NSError * _Nullable error) {
172 |
173 | if (imageUrl) {
174 |
175 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
176 |
177 | UIImage *image = [UIImage imageWithData:data];
178 | if (!image) {
179 | NSLog(@"read fail");
180 | return;
181 | }
182 | self.saveimage = image;
183 |
184 | FSActionSheet *actionSheet = nil;
185 |
186 | if ([self isAvailableQRcodeIn:image]) {
187 |
188 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
189 | delegate:self
190 | cancelButtonTitle:@"Cancel"
191 | highlightedButtonTitle:nil
192 | otherButtonTitles:@[@"Save Image", @"Extract QR code"]];
193 |
194 | } else {
195 |
196 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
197 | delegate:self
198 | cancelButtonTitle:@"Cancel"
199 | highlightedButtonTitle:nil
200 | otherButtonTitles:@[@"Save Image"]];
201 | }
202 | [actionSheet show];
203 | }
204 | }];
205 | }
206 |
207 | - (BOOL)isAvailableQRcodeIn:(UIImage *)img
208 | {
209 | if (iOS7_OR_EARLY) {
210 | return NO;
211 | }
212 |
213 | //Extract QR code by screenshot
214 | //UIImage *image = [self snapshot:self.view];
215 |
216 | UIImage *image = [self imageByInsetEdge:UIEdgeInsetsMake(-20, -20, -20, -20) withColor:[UIColor lightGrayColor] withImage:img];
217 |
218 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{}];
219 |
220 | NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
221 |
222 | if (features.count >= 1) {
223 | CIQRCodeFeature *feature = [features objectAtIndex:0];
224 |
225 | self.qrCodeString = [feature.messageString copy];
226 |
227 | NSLog(@"QR result :%@", self.qrCodeString);
228 |
229 | return YES;
230 | } else {
231 | NSLog(@"No QR");
232 | return NO;
233 | }
234 | }
235 |
236 | // you can also implement by UIView category
237 | - (UIImage *)snapshot:(UIView *)view
238 | {
239 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, view.window.screen.scale);
240 |
241 | if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
242 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
243 | }
244 | UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
245 |
246 | UIGraphicsEndImageContext();
247 |
248 | return image;
249 | }
250 |
251 | // you can also implement by UIImage category
252 | - (UIImage *)imageByInsetEdge:(UIEdgeInsets)insets withColor:(UIColor *)color withImage:(UIImage *)image
253 | {
254 | CGSize size = image.size;
255 | size.width -= insets.left + insets.right;
256 | size.height -= insets.top + insets.bottom;
257 | if (size.width <= 0 || size.height <= 0) {
258 | return nil;
259 | }
260 | CGRect rect = CGRectMake(-insets.left, -insets.top, image.size.width, image.size.height);
261 | UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
262 | CGContextRef context = UIGraphicsGetCurrentContext();
263 | if (color) {
264 | CGContextSetFillColorWithColor(context, color.CGColor);
265 | CGMutablePathRef path = CGPathCreateMutable();
266 | CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
267 | CGPathAddRect(path, NULL, rect);
268 | CGContextAddPath(context, path);
269 | CGContextEOFillPath(context);
270 | CGPathRelease(path);
271 | }
272 | [image drawInRect:rect];
273 | UIImage *insetEdgedImage = UIGraphicsGetImageFromCurrentImageContext();
274 | UIGraphicsEndImageContext();
275 |
276 | return insetEdgedImage;
277 | }
278 |
279 | - (NSURL *)autoFillURL:(NSURL *)url
280 | {
281 | //If no URL scheme was supplied, defer back to HTTP.
282 | if (url.scheme.length == 0) {
283 | url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", [url absoluteString]]];
284 | }
285 |
286 | return url;
287 | }
288 |
289 | @end
290 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/RNCachingURLProtocol/RNCachingURLProtocol.m:
--------------------------------------------------------------------------------
1 | //
2 | // RNCachingURLProtocol.m
3 | //
4 | // Created by Robert Napier on 1/10/12.
5 | // Copyright (c) 2012 Rob Napier.
6 | //
7 | // This code is licensed under the MIT License:
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a
10 | // copy of this software and associated documentation files (the "Software"),
11 | // to deal in the Software without restriction, including without limitation
12 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 | // and/or sell copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 | // DEALINGS IN THE SOFTWARE.
26 | //
27 |
28 | #import "RNCachingURLProtocol.h"
29 | #import "Reachability.h"
30 | #import "NSString+Sha1.h"
31 |
32 | #define WORKAROUND_MUTABLE_COPY_LEAK 1
33 |
34 | #if WORKAROUND_MUTABLE_COPY_LEAK
35 | // required to workaround http://openradar.appspot.com/11596316
36 | @interface NSURLRequest(MutableCopyWorkaround)
37 |
38 | - (id) mutableCopyWorkaround;
39 |
40 | @end
41 | #endif
42 |
43 |
44 |
45 | static NSString *RNCachingURLHeader = @"X-RNCache";
46 |
47 | @interface RNCachingURLProtocol () // iOS5-only
48 | @property (nonatomic, readwrite, strong) NSURLConnection *connection;
49 | @property (nonatomic, readwrite, strong) NSMutableData *data;
50 | @property (nonatomic, readwrite, strong) NSURLResponse *response;
51 | - (void)appendData:(NSData *)newData;
52 | @end
53 |
54 | static NSObject *RNCachingSupportedSchemesMonitor;
55 | static NSSet *RNCachingSupportedSchemes;
56 |
57 | @implementation RNCachingURLProtocol
58 | @synthesize connection = connection_;
59 | @synthesize data = data_;
60 | @synthesize response = response_;
61 |
62 | + (void)initialize
63 | {
64 | if (self == [RNCachingURLProtocol class])
65 | {
66 | static dispatch_once_t onceToken;
67 | dispatch_once(&onceToken, ^{
68 | RNCachingSupportedSchemesMonitor = [NSObject new];
69 | });
70 |
71 | [self setSupportedSchemes:[NSSet setWithObject:@"http"]];
72 | }
73 | }
74 |
75 | + (BOOL)canInitWithRequest:(NSURLRequest *)request
76 | {
77 | // only handle http requests we haven't marked with our header.
78 | if ([[self supportedSchemes] containsObject:[[request URL] scheme]] &&
79 | ([request valueForHTTPHeaderField:RNCachingURLHeader] == nil))
80 | {
81 | return YES;
82 | }
83 | return NO;
84 | }
85 |
86 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
87 | {
88 | return request;
89 | }
90 |
91 | + (NSString *)cachePathForURLString:(NSString *)url{
92 | NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
93 | NSString *fileName = [url sha1];
94 | return [cachesPath stringByAppendingPathComponent:fileName];
95 | }
96 |
97 | - (NSString *)cachePathForRequest:(NSURLRequest *)aRequest
98 | {
99 | // This stores in the Caches directory, which can be deleted when space is low, but we only use it for offline access
100 | NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
101 | NSString *fileName = [[[aRequest URL] absoluteString] sha1];
102 |
103 | return [cachesPath stringByAppendingPathComponent:fileName];
104 | }
105 |
106 | - (void)startLoading
107 | {
108 | if (![self useCache]) {
109 | NSMutableURLRequest *connectionRequest =
110 | #if WORKAROUND_MUTABLE_COPY_LEAK
111 | [[self request] mutableCopyWorkaround];
112 | #else
113 | [[self request] mutableCopy];
114 | #endif
115 | // we need to mark this request with our header so we know not to handle it in +[NSURLProtocol canInitWithRequest:].
116 | [connectionRequest setValue:@"" forHTTPHeaderField:RNCachingURLHeader];
117 | NSURLConnection *connection = [NSURLConnection connectionWithRequest:connectionRequest
118 | delegate:self];
119 | [self setConnection:connection];
120 | }
121 | else {
122 | RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]];
123 | if (cache) {
124 | NSData *data = [cache data];
125 | NSURLResponse *response = [cache response];
126 | NSURLRequest *redirectRequest = [cache redirectRequest];
127 | if (redirectRequest) {
128 | [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response];
129 | } else {
130 |
131 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; // we handle caching ourselves.
132 | [[self client] URLProtocol:self didLoadData:data];
133 | [[self client] URLProtocolDidFinishLoading:self];
134 | }
135 | }
136 | else {
137 | [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotConnectToHost userInfo:nil]];
138 | }
139 | }
140 | }
141 |
142 | - (void)stopLoading
143 | {
144 | [[self connection] cancel];
145 | }
146 |
147 | // NSURLConnection delegates (generally we pass these on to our client)
148 |
149 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
150 | {
151 | // Thanks to Nick Dowell https://gist.github.com/1885821
152 | if (response != nil) {
153 | NSMutableURLRequest *redirectableRequest =
154 | #if WORKAROUND_MUTABLE_COPY_LEAK
155 | [request mutableCopyWorkaround];
156 | #else
157 | [request mutableCopy];
158 | #endif
159 | // We need to remove our header so we know to handle this request and cache it.
160 | // There are 3 requests in flight: the outside request, which we handled, the internal request,
161 | // which we marked with our header, and the redirectableRequest, which we're modifying here.
162 | // The redirectable request will cause a new outside request from the NSURLProtocolClient, which
163 | // must not be marked with our header.
164 | [redirectableRequest setValue:nil forHTTPHeaderField:RNCachingURLHeader];
165 |
166 | NSString *cachePath = [self cachePathForRequest:[self request]];
167 | RNCachedData *cache = [RNCachedData new];
168 | [cache setResponse:response];
169 | [cache setData:[self data]];
170 | [cache setRedirectRequest:redirectableRequest];
171 | [NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
172 | [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response];
173 | return redirectableRequest;
174 | } else {
175 | return request;
176 | }
177 | }
178 |
179 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
180 | {
181 | [[self client] URLProtocol:self didLoadData:data];
182 | [self appendData:data];
183 | }
184 |
185 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
186 | {
187 | [[self client] URLProtocol:self didFailWithError:error];
188 | [self setConnection:nil];
189 | [self setData:nil];
190 | [self setResponse:nil];
191 | }
192 |
193 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
194 | {
195 | [self setResponse:response];
196 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; // We cache ourselves.
197 | }
198 |
199 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection
200 | {
201 | [[self client] URLProtocolDidFinishLoading:self];
202 |
203 | NSString *cachePath = [self cachePathForRequest:[self request]];
204 | RNCachedData *cache = [RNCachedData new];
205 | [cache setResponse:[self response]];
206 | [cache setData:[self data]];
207 | [NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
208 |
209 | [self setConnection:nil];
210 | [self setData:nil];
211 | [self setResponse:nil];
212 | }
213 |
214 | - (BOOL) useCache
215 | {
216 | BOOL reachable = (BOOL) [[Reachability reachabilityWithHostName:[[[self request] URL] host]] currentReachabilityStatus] != NotReachable;
217 | return !reachable;
218 | }
219 |
220 | - (void)appendData:(NSData *)newData
221 | {
222 | if ([self data] == nil) {
223 | [self setData:[newData mutableCopy]];
224 | }
225 | else {
226 | [[self data] appendData:newData];
227 | }
228 | }
229 |
230 | + (NSSet *)supportedSchemes {
231 | NSSet *supportedSchemes;
232 | @synchronized(RNCachingSupportedSchemesMonitor)
233 | {
234 | supportedSchemes = RNCachingSupportedSchemes;
235 | }
236 | return supportedSchemes;
237 | }
238 |
239 | + (void)setSupportedSchemes:(NSSet *)supportedSchemes
240 | {
241 | @synchronized(RNCachingSupportedSchemesMonitor)
242 | {
243 | RNCachingSupportedSchemes = supportedSchemes;
244 | }
245 | }
246 |
247 | @end
248 |
249 | static NSString *const kDataKey = @"data";
250 | static NSString *const kResponseKey = @"response";
251 | static NSString *const kRedirectRequestKey = @"redirectRequest";
252 |
253 | @implementation RNCachedData
254 | @synthesize data = data_;
255 | @synthesize response = response_;
256 | @synthesize redirectRequest = redirectRequest_;
257 |
258 | - (void)encodeWithCoder:(NSCoder *)aCoder
259 | {
260 | [aCoder encodeObject:[self data] forKey:kDataKey];
261 | [aCoder encodeObject:[self response] forKey:kResponseKey];
262 | [aCoder encodeObject:[self redirectRequest] forKey:kRedirectRequestKey];
263 | }
264 |
265 | - (id)initWithCoder:(NSCoder *)aDecoder
266 | {
267 | self = [super init];
268 | if (self != nil) {
269 | [self setData:[aDecoder decodeObjectForKey:kDataKey]];
270 | [self setResponse:[aDecoder decodeObjectForKey:kResponseKey]];
271 | [self setRedirectRequest:[aDecoder decodeObjectForKey:kRedirectRequestKey]];
272 | }
273 |
274 | return self;
275 | }
276 |
277 | @end
278 |
279 | #if WORKAROUND_MUTABLE_COPY_LEAK
280 | @implementation NSURLRequest(MutableCopyWorkaround)
281 |
282 | - (id) mutableCopyWorkaround {
283 | NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequest alloc] initWithURL:[self URL]
284 | cachePolicy:[self cachePolicy]
285 | timeoutInterval:[self timeoutInterval]];
286 | [mutableURLRequest setAllHTTPHeaderFields:[self allHTTPHeaderFields]];
287 | if ([self HTTPBodyStream]) {
288 | [mutableURLRequest setHTTPBodyStream:[self HTTPBodyStream]];
289 | } else {
290 | [mutableURLRequest setHTTPBody:[self HTTPBody]];
291 | }
292 | [mutableURLRequest setHTTPMethod:[self HTTPMethod]];
293 |
294 | return mutableURLRequest;
295 | }
296 |
297 | @end
298 | #endif
299 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## UIWebView长按保存图片和识别图片二维码的实现方案(使用缓存)
2 | ### 0x00 需求:长按识别UIWebView中的二维码,如下图
3 |
4 | 
5 |
6 | ### 0x01 方案1:
7 | 给UIWebView增加一个长按手势,激活长按手势时获取当前UIWebView的截图,分析是否包含二维码。
8 |
9 | **核心代码**:略
10 |
11 | **优点**:流程简单,可以快速实现。
12 |
13 | **不足**:无法实现保存UIWebView中图片,如果当前WebView二维码显示不全或者多个二维码,使用这种方式实现的二维码识别也会有问题;
14 |
15 | ### 0x02 方案2:
16 | 长按UIWebView时,获取手指单击位置的图片的URL地址。这种方案是通过获取手指点击的位置,然后获取该位置的标签的src属性,进而获取到url。
17 |
18 | **核心代码**:
19 |
20 | ```
21 | @interface CVWebViewController ()
22 |
23 | @property (weak, nonatomic) IBOutlet UIWebView *webView;
24 |
25 | @end
26 |
27 | @implementation CVWebViewController
28 |
29 | - (void)viewDidLoad
30 | {
31 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://mp.weixin.qq.com/s?__biz=MzI2ODAzODAzMw==&mid=2650057120&idx=2&sn=c875f7d03ea3823e8dcb3dc4d0cff51d&scene=0#wechat_redirect"]]];
32 | UILongPressGestureRecognizer *longPressed = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressed:)];
33 | longPressed.delegate = self;
34 | [self.webView addGestureRecognizer:longPressed];
35 | }
36 |
37 | - (void)longPressed:(UITapGestureRecognizer*)recognizer
38 | {
39 | if (recognizer.state != UIGestureRecognizerStateBegan) {
40 | return;
41 | }
42 | CGPoint touchPoint = [recognizer locationInView:self.webView];
43 | NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
44 | NSString *imageUrl = [self.webView stringByEvaluatingJavaScriptFromString:js];
45 | if (imageUrl.length == 0) {
46 | return;
47 | }
48 | NSLog(@"image url:%@",imageUrl);
49 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
50 |
51 | UIImage *image = [UIImage imageWithData:data];
52 | if (image) {
53 | //......
54 | //save image or Extract QR code
55 | }
56 | }
57 |
58 | -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
59 | {
60 | return YES;
61 | }
62 | ```
63 | 上述代码实现的核心部分就是
64 |
65 | ```
66 | NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
67 | NSString *imageUrl = [self.webView stringByEvaluatingJavaScriptFromString:js];
68 | ```
69 | 第一行代码是通过js获取点击位置的标签的src属性;
70 |
71 | 第二行代码是接受向webview注入第一行的js代码后返回的src属性。
72 |
73 | 如果点击位置是图片,那么久可以通过img.src拿到图片的url地址,如果不是就返回空值。
74 |
75 | **效果**:
76 | 
77 | **注意**:由于UIWebView内部是有一个ScrollView,默认情况下不支持多个手势的,因此需要实现UIGestureRecognizerDelegate中的gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:协议,以支持多个手势(增加长按手势)。
78 |
79 | **优点**:通过识别img标签的url属性,既可以实现保存图片的功能,也可以实现识别图片中二维码的功能;该方案不仅仅可以获取img标签的属性,也可以根据需要获取其他标签,例如链接标签\的属性(需调整部分代码,识别tagName)。
80 |
81 | **不足**:每次获取图片,都需要根据url获取,相当于从网络获取,万一图片太大或者网络不好,势必会影响用户体验,方案3中会介绍如何从缓存中获取image数据。
82 |
83 | ### 0x03 方案3:
84 | 利用Runtime,动态地为UIWebView注入一段js代码,获取IMG标签的src属性,然后从UIWebView的缓存中获取image数据。
85 |
86 | webview加载完成图片完成之后,图片数据已经缓存在webview里了,只需找到从缓存中获取这些数据的方法。
87 |
88 | 方案3使用的从缓存中获取image数据原理是:使用NSURLProtocol,webview在处理请求的过程中会调用
89 |
90 | ```
91 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
92 | ```
93 | NSURLProtocol把webView请求返回来的data用压缩的方式的存储在cache的文件夹下, 发出请求的时候会先去读取缓存。
94 |
95 | 在github上找到了一个[RNCachingURLProtocol](https://github.com/rnapier/RNCachingURLProtocol),可以方便地从缓存中获取数据。
96 |
97 | 关于[NSURLProtocol](http://www.jianshu.com/p/7c89b8c5482a),能够让你去重新定义苹果的URL[加载系统](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165-BCICJDHA) (URL Loading System)的行为,URL Loading System里有许多类用于处理URL请求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSession等,当URL Loading System使用NSURLRequest去获取资源的时候,它会创建一个NSURLProtocol子类的实例,你不应该直接实例化一个NSURLProtocol,NSURLProtocol看起来像是一个协议,但其实这是一个类,而且必须使用该类的子类,并且需要被注册。
98 |
99 | **核心代码**:
100 |
101 | ```
102 | static NSString *const kTouchJavaScriptString =
103 | @"document.ontouchstart=function(event){\
104 | x=event.targetTouches[0].clientX;\
105 | y=event.targetTouches[0].clientY;\
106 | document.location=\"myweb:touch:start:\"+x+\":\"+y;};\
107 | document.ontouchmove=function(event){\
108 | x=event.targetTouches[0].clientX;\
109 | y=event.targetTouches[0].clientY;\
110 | document.location=\"myweb:touch:move:\"+x+\":\"+y;};\
111 | document.ontouchcancel=function(event){\
112 | document.location=\"myweb:touch:cancel\";};\
113 | document.ontouchend=function(event){\
114 | document.location=\"myweb:touch:end\";};";
115 |
116 | static NSString *const kImageJS = @"keyForImageJS";
117 | static NSString *const kImage = @"keyForImage";
118 | static NSString *const kImageQRString = @"keyForQR";
119 |
120 | static const NSTimeInterval KLongGestureInterval = 0.8f;
121 | ......
122 | SwizzlingMethod([self class], @selector(webViewDidStartLoad:), @selector(sl_webViewDidStartLoad:));
123 | SwizzlingMethod([self class], @selector(webView:shouldStartLoadWithRequest:navigationType:), @selector(sl_webView:shouldStartLoadWithRequest:navigationType:));
124 | SwizzlingMethod([self class], @selector(webViewDidFinishLoad:), @selector(sl_webViewDidFinishLoad:));
125 | ......
126 | - (void)sl_webViewDidStartLoad:(UIWebView *)webView
127 | {
128 | //Add long press gresture for web view
129 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
130 | longPress.minimumPressDuration = KLongGestureInterval;
131 | longPress.delegate = self;
132 | [self.webView addGestureRecognizer:longPress];
133 |
134 | [self sl_webViewDidStartLoad:webView];
135 | }
136 |
137 | - (BOOL)sl_webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
138 | {
139 | NSString *requestString = [[request URL] absoluteString];
140 |
141 | NSArray *components = [requestString componentsSeparatedByString:@":"];
142 |
143 | if ([components count] > 1 && [(NSString *)[components objectAtIndex:0] isEqualToString:@"myweb"]) {
144 |
145 | if([(NSString *)[components objectAtIndex:1] isEqualToString:@"touch"]) {
146 |
147 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"start"]) {
148 |
149 | NSLog(@"touch start!");
150 |
151 | float pointX = [[components objectAtIndex:3] floatValue];
152 | float pointY = [[components objectAtIndex:4] floatValue];
153 |
154 | NSLog(@"touch point (%f, %f)", pointX, pointY);
155 |
156 | NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).tagName", pointX, pointY];
157 |
158 | NSString * tagName = [self.webView stringByEvaluatingJavaScriptFromString:js];
159 |
160 | self.imageJS = nil;
161 | if ([tagName isEqualToString:@"IMG"]) {
162 |
163 | self.imageJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", pointX, pointY];
164 |
165 | }
166 |
167 | } else {
168 |
169 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"move"]) {
170 | NSLog(@"you are move");
171 | } else {
172 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"end"]) {
173 | NSLog(@"touch end");
174 | }
175 | }
176 | }
177 | }
178 |
179 | if (self.imageJS) {
180 | NSLog(@"touching image");
181 | }
182 |
183 | return NO;
184 | }
185 |
186 | return [self sl_webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
187 | }
188 | - (void)sl_webViewDidFinishLoad:(UIWebView *)webView
189 | {
190 | //inject js
191 | [webView stringByEvaluatingJavaScriptFromString:kTouchJavaScriptString];
192 |
193 | [self sl_webViewDidFinishLoad:webView];
194 | }
195 | ......
196 | - (void)handleLongPress:(UILongPressGestureRecognizer *)sender
197 | {
198 | if (sender.state != UIGestureRecognizerStateBegan) {
199 | return;
200 | }
201 |
202 | NSString *imageUrl = [self.webView stringByEvaluatingJavaScriptFromString:self.imageJS];
203 |
204 | if (imageUrl) {
205 |
206 | NSData *data = nil;
207 | NSString *fileName = [RNCachingURLProtocol cachePathForURLString:imageUrl];
208 |
209 | RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:fileName];
210 |
211 | if (cache) {
212 | NSLog(@"read from cache");
213 | data = cache.data;
214 | } else{
215 | NSLog(@"read from url");
216 | data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
217 | }
218 |
219 | UIImage *image = [UIImage imageWithData:data];
220 | if (!image) {
221 | NSLog(@"read fail");
222 | return;
223 | }
224 | self.image = image;
225 |
226 | FSActionSheet *actionSheet = nil;
227 |
228 | if ([self isAvailableQRcodeIn:image]) {
229 |
230 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
231 | delegate:self
232 | cancelButtonTitle:@"Cancel"
233 | highlightedButtonTitle:nil
234 | otherButtonTitles:@[@"Save Image", @"Extract QR code"]];
235 |
236 | } else {
237 |
238 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
239 | delegate:self
240 | cancelButtonTitle:@"Cancel"
241 | highlightedButtonTitle:nil
242 | otherButtonTitles:@[@"Save Image"]];
243 | }
244 | [actionSheet show];
245 |
246 | }
247 | }
248 | ......
249 | - (BOOL)isAvailableQRcodeIn:(UIImage *)img
250 | {
251 | if (iOS7_OR_EARLY) {
252 | return NO;
253 | }
254 |
255 | //Extract QR code by screenshot
256 | //UIImage *image = [self snapshot:self.view];
257 |
258 | // IF image is a full qr code, CIDetector can not detect qr string, I am not sure why.
259 | UIImage *image = [self imageByInsetEdge:UIEdgeInsetsMake(-20, -20, -20, -20) withColor:[UIColor lightGrayColor] withImage:img];
260 |
261 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{}];
262 |
263 | NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
264 |
265 | if (features.count >= 1) {
266 | CIQRCodeFeature *feature = [features objectAtIndex:0];
267 |
268 | self.qrCodeString = [feature.messageString copy];
269 |
270 | NSLog(@"QR result :%@", self.qrCodeString);
271 |
272 | return YES;
273 | } else {
274 | NSLog(@"No QR");
275 | return NO;
276 | }
277 | }
278 | ......
279 |
280 | ```
281 | **效果**:
282 |
283 | 
284 | 
285 |
286 | **优点**:充分利用了缓存,提高了用户体验。
287 |
288 | **不足**:实现略复杂。
289 | ### 0x04 思考
290 | 如果是非IMG标签提供的图片,例如div的background image,该如何获取和保存?
291 |
292 | WKWebView上的实现。
293 |
294 | ### 0x05 Demo地址
295 |
296 | [https://github.com/guoxiucai/WebViewLongPress](https://github.com/guoxiucai/WebViewLongPress)
297 |
298 | ### 0x06 参考和致谢
299 | [iOS QRcode识别及相册图片二维码读取识别](http://www.jianshu.com/p/48e44fe67c1d)
300 |
301 | [UIWebView保存图片](http://www.cocoachina.com/ios/20160616/16660.html)
302 |
303 | [RNCachingURLProtocol](https://github.com/rnapier/RNCachingURLProtocol)
304 |
305 | [EndLess](https://github.com/jcs/endless)
306 |
307 | [FSActionSheet](https://github.com/lifution/FSActionSheet)
308 |
309 |
310 |
311 |
312 |
--------------------------------------------------------------------------------
/WebViewLongPress/CVWebViewController+ImageHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // CVWebViewController+ImageHelper.m
3 | // WebViewLongPress
4 | //
5 | // Created by guoqingwei on 16/6/14.
6 | // Copyright © 2016年 cvte. All rights reserved.
7 | //
8 |
9 | #import "CVWebViewController+ImageHelper.h"
10 | #import "SwizzeMethod.h"
11 | #import "RNCachingURLProtocol.h"
12 | #import
13 |
14 | typedef NS_ENUM(NSInteger, SelectItem) {
15 | SelectItemSaveImage,
16 | SelectItemQRExtract
17 | };
18 |
19 | #define iOS7_OR_EARLY ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0)
20 |
21 | //injected javascript
22 | static NSString *const kTouchJavaScriptString =
23 | @"document.ontouchstart=function(event){\
24 | x=event.targetTouches[0].clientX;\
25 | y=event.targetTouches[0].clientY;\
26 | document.location=\"myweb:touch:start:\"+x+\":\"+y;};\
27 | document.ontouchmove=function(event){\
28 | x=event.targetTouches[0].clientX;\
29 | y=event.targetTouches[0].clientY;\
30 | document.location=\"myweb:touch:move:\"+x+\":\"+y;};\
31 | document.ontouchcancel=function(event){\
32 | document.location=\"myweb:touch:cancel\";};\
33 | document.ontouchend=function(event){\
34 | document.location=\"myweb:touch:end\";};";
35 |
36 | static NSString *const kImageJS = @"keyForImageJS";
37 | static NSString *const kImage = @"keyForImage";
38 | static NSString *const kImageQRString = @"keyForQR";
39 |
40 | static const NSTimeInterval KLongGestureInterval = 0.8f;
41 |
42 |
43 | @implementation CVWebViewController (ImageHelper)
44 |
45 | +(void)load
46 | {
47 | [super load];
48 | static dispatch_once_t onceToken;
49 | dispatch_once(&onceToken, ^{
50 | [self hookWebView];
51 | });
52 | }
53 |
54 | + (void)hookWebView
55 | {
56 | SwizzlingMethod([self class], @selector(webViewDidStartLoad:), @selector(sl_webViewDidStartLoad:));
57 | SwizzlingMethod([self class], @selector(webView:shouldStartLoadWithRequest:navigationType:), @selector(sl_webView:shouldStartLoadWithRequest:navigationType:));
58 | SwizzlingMethod([self class], @selector(webViewDidFinishLoad:), @selector(sl_webViewDidFinishLoad:));
59 | }
60 |
61 | #pragma mark - seter and getter
62 |
63 | - (void)setImageJS:(NSString *)imageJS
64 | {
65 | objc_setAssociatedObject(self, &kImageJS, imageJS, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
66 | }
67 |
68 | - (NSString *)imageJS
69 | {
70 | return objc_getAssociatedObject(self, &kImageJS);
71 | }
72 |
73 | - (void)setQrCodeString:(NSString *)qrCodeString
74 | {
75 | objc_setAssociatedObject(self, &kImageQRString, qrCodeString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
76 | }
77 |
78 | - (NSString *)qrCodeString
79 | {
80 | return objc_getAssociatedObject(self, &kImageQRString);
81 | }
82 |
83 | - (void)setImage:(UIImage *)image
84 | {
85 | objc_setAssociatedObject(self, &kImage, image, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
86 | }
87 |
88 | - (UIImage *)image
89 | {
90 | return objc_getAssociatedObject(self, &kImage);
91 | }
92 |
93 | #pragma mark - Save image callback
94 |
95 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
96 | {
97 | NSString *message = @"Succeed";
98 |
99 | if (error) {
100 | message = @"Fail";
101 | }
102 | NSLog(@"save result :%@", message);
103 | }
104 |
105 | #pragma mark - FSActionSheetDelegate
106 |
107 | - (void)FSActionSheet:(FSActionSheet *)actionSheet selectedIndex:(NSInteger)selectedIndex
108 | {
109 | [self.webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='text';"];
110 |
111 | switch (selectedIndex) {
112 | case SelectItemSaveImage:
113 | {
114 | UIImageWriteToSavedPhotosAlbum(self.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
115 | }
116 | break;
117 | case SelectItemQRExtract:
118 | {
119 | NSURL *qrUrl = [NSURL URLWithString:self.qrCodeString];
120 | //open with safari
121 | if ([[UIApplication sharedApplication] canOpenURL:qrUrl]) {
122 | [[UIApplication sharedApplication] openURL:qrUrl];
123 | }
124 | // open in inner webview
125 | //[self.webView loadRequest:[NSURLRequest requestWithURL:qrUrl]];
126 | }
127 | break;
128 |
129 | default:
130 | break;
131 | }
132 | }
133 |
134 | #pragma mark - swizing
135 |
136 | - (BOOL)sl_webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
137 | {
138 | NSString *requestString = [[request URL] absoluteString];
139 |
140 | NSArray *components = [requestString componentsSeparatedByString:@":"];
141 |
142 | if ([components count] > 1 && [(NSString *)[components objectAtIndex:0] isEqualToString:@"myweb"]) {
143 |
144 | if([(NSString *)[components objectAtIndex:1] isEqualToString:@"touch"]) {
145 |
146 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"start"]) {
147 |
148 | NSLog(@"touch start!");
149 |
150 | float pointX = [[components objectAtIndex:3] floatValue];
151 | float pointY = [[components objectAtIndex:4] floatValue];
152 |
153 | NSLog(@"touch point (%f, %f)", pointX, pointY);
154 |
155 | NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).tagName", pointX, pointY];
156 |
157 | NSString * tagName = [self.webView stringByEvaluatingJavaScriptFromString:js];
158 |
159 | self.imageJS = nil;
160 | if ([tagName isEqualToString:@"IMG"]) {
161 |
162 | self.imageJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", pointX, pointY];
163 |
164 | }
165 |
166 | } else {
167 |
168 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"move"]) {
169 | NSLog(@"you are move");
170 | } else {
171 | if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"end"]) {
172 | NSLog(@"touch end");
173 | }
174 | }
175 | }
176 | }
177 |
178 | if (self.imageJS) {
179 | NSLog(@"touching image");
180 | }
181 |
182 | return NO;
183 | }
184 |
185 | return [self sl_webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
186 | }
187 |
188 | - (void)sl_webViewDidStartLoad:(UIWebView *)webView
189 | {
190 | //Add long press gresture for web view
191 | UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
192 | longPress.minimumPressDuration = KLongGestureInterval;
193 | longPress.allowableMovement = 20;
194 | longPress.delegate = self;
195 | [self.webView addGestureRecognizer:longPress];
196 |
197 | [self sl_webViewDidStartLoad:webView];
198 | }
199 |
200 | - (void)sl_webViewDidFinishLoad:(UIWebView *)webView
201 | {
202 | //cache manager
203 | [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"WebKitCacheModelPreferenceKey"];
204 |
205 | //inject js
206 | [webView stringByEvaluatingJavaScriptFromString:kTouchJavaScriptString];
207 |
208 | [self sl_webViewDidFinishLoad:webView];
209 | }
210 |
211 | #pragma mark - UIGestureRecognizerDelegate
212 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
213 | {
214 | if (![gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
215 | return NO;
216 |
217 | if ([self isTouchingImage]) {
218 | if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
219 | otherGestureRecognizer.enabled = NO;
220 | otherGestureRecognizer.enabled = YES;
221 | }
222 |
223 | return YES;
224 | }
225 |
226 | return NO;
227 | }
228 |
229 | #pragma mark - private Method
230 | - (BOOL)isTouchingImage
231 | {
232 | if (self.imageJS) {
233 | return YES;
234 | }
235 | return NO;
236 | }
237 |
238 | - (void)handleLongPress:(UILongPressGestureRecognizer *)sender
239 | {
240 | if (sender.state != UIGestureRecognizerStateBegan) {
241 | return;
242 | }
243 |
244 | NSString *imageUrl = [self.webView stringByEvaluatingJavaScriptFromString:self.imageJS];
245 |
246 | if (imageUrl) {
247 |
248 | NSData *data = nil;
249 | NSString *fileName = [RNCachingURLProtocol cachePathForURLString:imageUrl];
250 |
251 | RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:fileName];
252 |
253 | if (cache) {
254 | NSLog(@"read from cache");
255 | data = cache.data;
256 | } else{
257 | NSLog(@"read from url");
258 | data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
259 | }
260 |
261 | UIImage *image = [UIImage imageWithData:data];
262 | if (!image) {
263 | NSLog(@"read fail");
264 | return;
265 | }
266 | self.image = image;
267 |
268 | FSActionSheet *actionSheet = nil;
269 |
270 | if ([self isAvailableQRcodeIn:image]) {
271 |
272 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
273 | delegate:self
274 | cancelButtonTitle:@"Cancel"
275 | highlightedButtonTitle:nil
276 | otherButtonTitles:@[@"Save Image", @"Extract QR code"]];
277 |
278 | } else {
279 |
280 | actionSheet = [[FSActionSheet alloc] initWithTitle:nil
281 | delegate:self
282 | cancelButtonTitle:@"Cancel"
283 | highlightedButtonTitle:nil
284 | otherButtonTitles:@[@"Save Image"]];
285 | }
286 | [actionSheet show];
287 |
288 | }
289 |
290 | }
291 |
292 | - (BOOL)isAvailableQRcodeIn:(UIImage *)img
293 | {
294 | if (iOS7_OR_EARLY) {
295 | return NO;
296 | }
297 |
298 | //Extract QR code by screenshot
299 | //UIImage *image = [self snapshot:self.view];
300 |
301 | UIImage *image = [self imageByInsetEdge:UIEdgeInsetsMake(-20, -20, -20, -20) withColor:[UIColor lightGrayColor] withImage:img];
302 |
303 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{}];
304 |
305 | NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
306 |
307 | if (features.count >= 1) {
308 | CIQRCodeFeature *feature = [features objectAtIndex:0];
309 |
310 | self.qrCodeString = [feature.messageString copy];
311 |
312 | NSLog(@"QR result :%@", self.qrCodeString);
313 |
314 | return YES;
315 | } else {
316 | NSLog(@"No QR");
317 | return NO;
318 | }
319 | }
320 |
321 | // you can also implement by UIView category
322 | - (UIImage *)snapshot:(UIView *)view
323 | {
324 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, view.window.screen.scale);
325 |
326 | if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
327 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
328 | }
329 | UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
330 |
331 | UIGraphicsEndImageContext();
332 |
333 | return image;
334 | }
335 |
336 | // you can also implement by UIImage category
337 | - (UIImage *)imageByInsetEdge:(UIEdgeInsets)insets withColor:(UIColor *)color withImage:(UIImage *)image
338 | {
339 | CGSize size = image.size;
340 | size.width -= insets.left + insets.right;
341 | size.height -= insets.top + insets.bottom;
342 | if (size.width <= 0 || size.height <= 0) {
343 | return nil;
344 | }
345 | CGRect rect = CGRectMake(-insets.left, -insets.top, image.size.width, image.size.height);
346 | UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
347 | CGContextRef context = UIGraphicsGetCurrentContext();
348 | if (color) {
349 | CGContextSetFillColorWithColor(context, color.CGColor);
350 | CGMutablePathRef path = CGPathCreateMutable();
351 | CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
352 | CGPathAddRect(path, NULL, rect);
353 | CGContextAddPath(context, path);
354 | CGContextEOFillPath(context);
355 | CGPathRelease(path);
356 | }
357 | [image drawInRect:rect];
358 | UIImage *insetEdgedImage = UIGraphicsGetImageFromCurrentImageContext();
359 | UIGraphicsEndImageContext();
360 |
361 | return insetEdgedImage;
362 | }
363 |
364 | @end
365 |
--------------------------------------------------------------------------------
/WebViewLongPress/Vendor/FSActionSheet/FSActionSheet.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSActionSheet.m
3 | // FSActionSheet
4 | //
5 | // Created by Steven on 6/7/16.
6 | // Copyright © 2016年 Steven. All rights reserved.
7 | //
8 |
9 | #import "FSActionSheet.h"
10 | #import "FSActionSheetCell.h"
11 |
12 | CGFloat const kFSActionSheetSectionHeight = 10; ///< 分区间距
13 |
14 | @interface FSActionSheet ()
15 |
16 | @property (nonatomic, copy) NSString *title;
17 | @property (nonatomic, copy) NSString *cancelTitle;
18 | @property (nonatomic, copy) NSArray *items;
19 | @property (nonatomic, copy) FSActionSheetHandler selectedHandler;
20 |
21 | @property (nonatomic, strong) UIWindow *window;
22 | @property (nonatomic, weak) UIView *controllerView;
23 | @property (nonatomic, strong) UIView *backView;
24 | @property (nonatomic, strong) UITableView *tableView;
25 | @property (nonatomic, weak) UILabel *titleLabel;
26 |
27 | @property (nonatomic, strong) NSLayoutConstraint *heightConstraint; ///< 内容高度约束
28 |
29 | @end
30 |
31 | static NSString * const kFSActionSheetCellIdentifier = @"kFSActionSheetCellIdentifier";
32 |
33 | @implementation FSActionSheet
34 |
35 | - (instancetype)init {
36 | return [self initWithTitle:nil cancelTitle:nil items:nil];
37 | }
38 |
39 | - (void)dealloc {
40 | [[NSNotificationCenter defaultCenter] removeObserver:self];
41 |
42 | // NSLog(@"-- FSActionSheet did dealloc -- [%p] -- [%p] -- [%p]", _window, _backView, _tableView);
43 | }
44 |
45 | /*! @brief 单文本选项快速初始化 */
46 | - (instancetype)initWithTitle:(NSString *)title delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle highlightedButtonTitle:(NSString *)highlightedButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles {
47 | if (!(self = [super init])) return nil;
48 |
49 | [self baseSetting];
50 |
51 | NSMutableArray *titleItems = [@[] mutableCopy];
52 | // 普通按钮
53 | for (NSString *otherTitle in otherButtonTitles) {
54 | if (otherTitle && otherTitle.length > 0) {
55 | [titleItems addObject:FSActionSheetTitleItemMake(FSActionSheetTypeNormal, otherTitle)];
56 | }
57 | }
58 | // 高亮按钮, 高亮按钮放在最下面.
59 | if (highlightedButtonTitle && highlightedButtonTitle.length > 0) {
60 | [titleItems addObject:FSActionSheetTitleItemMake(FSActionSheetTypeHighlighted, highlightedButtonTitle)];
61 | }
62 |
63 | self.title = title?:@"";
64 | self.delegate = delegate;
65 | self.cancelTitle = (cancelButtonTitle && cancelButtonTitle.length != 0)?cancelButtonTitle:@"取消";
66 | self.items = titleItems;
67 |
68 | [self addSubview:self.tableView];
69 |
70 | return self;
71 | }
72 |
73 | /*! @brief 在外部组装选项按钮item */
74 | - (instancetype)initWithTitle:(NSString *)title cancelTitle:(NSString *)cancelTitle items:(NSArray *)items {
75 | if (!(self = [super init])) return nil;
76 |
77 | [self baseSetting];
78 |
79 | self.title = title?:@"";
80 | self.cancelTitle = (cancelTitle && cancelTitle.length != 0)?cancelTitle:@"取消";
81 | self.items = items?:@[];
82 |
83 | [self addSubview:self.tableView];
84 |
85 | return self;
86 | }
87 |
88 | - (void)layoutSubviews {
89 | [super layoutSubviews];
90 | self.tableView.frame = self.bounds;
91 | }
92 |
93 | // 默认设置
94 | - (void)baseSetting {
95 | self.backgroundColor = FSColorWithString(FSActionSheetBackColor);
96 | self.translatesAutoresizingMaskIntoConstraints = NO; // 允许约束
97 |
98 | _contentAlignment = FSContentAlignmentCenter; // 默认样式为居中
99 | _hideOnTouchOutside = YES; // 默认点击半透明层隐藏弹窗
100 |
101 | // 监听屏幕旋转
102 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
103 | }
104 |
105 | // 屏幕旋转通知回调
106 | - (void)orientationDidChange:(NSNotification *)notification {
107 | if (_title.length > 0) {
108 | // 更新头部标题的高度
109 | CGFloat newHeaderHeight = [self heightForHeaderView];
110 | CGRect newHeaderRect = _tableView.tableHeaderView.frame;
111 | newHeaderRect.size.height = newHeaderHeight;
112 | _tableView.tableHeaderView.frame = newHeaderRect;
113 | self.tableView.tableHeaderView = self.tableView.tableHeaderView;
114 | // 适配当前内容高度
115 | [self fixContentHeight];
116 | }
117 | }
118 |
119 | #pragma mark - private
120 | // 计算title在设定宽度下的富文本高度
121 | - (CGFloat)heightForHeaderView {
122 | CGFloat labelHeight = [_titleLabel.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.window.frame)-FSActionSheetDefaultMargin*2, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size.height;
123 | CGFloat headerHeight = ceil(labelHeight)+FSActionSheetDefaultMargin*2;
124 |
125 | return headerHeight;
126 | }
127 |
128 | // 整个弹窗内容的高度
129 | - (CGFloat)contentHeight {
130 | CGFloat titleHeight = (_title.length > 0)?[self heightForHeaderView]:0;
131 | CGFloat rowHeightSum = (_items.count+1)*FSActionSheetRowHeight+kFSActionSheetSectionHeight;
132 | CGFloat contentHeight = titleHeight+rowHeightSum;
133 |
134 | return contentHeight;
135 | }
136 |
137 | // 适配屏幕高度, 弹出窗高度不应该高于屏幕的设定比例
138 | - (void)fixContentHeight {
139 | CGFloat contentMaxHeight = CGRectGetHeight(self.window.frame)*FSActionSheetContentMaxScale;
140 | CGFloat contentHeight = [self contentHeight];
141 | if (contentHeight > contentMaxHeight) {
142 | contentHeight = contentMaxHeight;
143 | self.tableView.scrollEnabled = YES;
144 | } else {
145 | self.tableView.scrollEnabled = NO;
146 | }
147 |
148 | _heightConstraint.constant = contentHeight;
149 | }
150 |
151 | // 适配标题偏移方向
152 | - (void)updateTitleAttributeText {
153 | if (_title.length == 0 || !_titleLabel) return;
154 |
155 | // 富文本相关配置
156 | NSRange attributeRange = NSMakeRange(0, _title.length);
157 | UIFont *titleFont = [UIFont systemFontOfSize:14];
158 | UIColor *titleTextColor = FSColorWithString(FSActionSheetTitleColor);
159 | CGFloat lineSpacing = FSActionSheetTitleLineSpacing;
160 | CGFloat kernSpacing = FSActionSheetTitleKernSpacing;
161 |
162 | NSMutableAttributedString *titleAttributeString = [[NSMutableAttributedString alloc] initWithString:_title];
163 | NSMutableParagraphStyle *titleStyle = [[NSMutableParagraphStyle alloc] init];
164 | // 行距
165 | titleStyle.lineSpacing = lineSpacing;
166 | // 内容偏移样式
167 | switch (_contentAlignment) {
168 | case FSContentAlignmentLeft: {
169 | titleStyle.alignment = NSTextAlignmentLeft;
170 | break;
171 | }
172 | case FSContentAlignmentCenter: {
173 | titleStyle.alignment = NSTextAlignmentCenter;
174 | break;
175 | }
176 | case FSContentAlignmentRight: {
177 | titleStyle.alignment = NSTextAlignmentRight;
178 | break;
179 | }
180 | }
181 | [titleAttributeString addAttribute:NSParagraphStyleAttributeName value:titleStyle range:attributeRange];
182 | // 字距
183 | [titleAttributeString addAttribute:NSKernAttributeName value:@(kernSpacing) range:attributeRange];
184 | // 字体
185 | [titleAttributeString addAttribute:NSFontAttributeName value:titleFont range:attributeRange];
186 | // 颜色
187 | [titleAttributeString addAttribute:NSForegroundColorAttributeName value:titleTextColor range:attributeRange];
188 | _titleLabel.attributedText = titleAttributeString;
189 | }
190 |
191 | // 点击背景半透明遮罩层隐藏
192 | - (void)backViewGesture {
193 | [self hideWithCompletion:nil];
194 | }
195 |
196 | // 隐藏
197 | - (void)hideWithCompletion:(void(^)())completion {
198 | [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
199 | _backView.alpha = 0;
200 | CGRect newFrame = self.frame;
201 | newFrame.origin.y = CGRectGetMaxY(_controllerView.frame);
202 | self.frame = newFrame;
203 | } completion:^(BOOL finished) {
204 | [[[UIApplication sharedApplication].delegate window] makeKeyWindow];
205 | if (completion) completion();
206 | [_backView removeFromSuperview];
207 | _backView = nil;
208 | [_tableView removeFromSuperview];
209 | _tableView = nil;
210 | [self removeFromSuperview];
211 | self.window = nil;
212 | self.selectedHandler = nil;
213 | }];
214 | }
215 |
216 | #pragma mark - public
217 | /*! @brief 单展示, 不绑定block回调 */
218 | - (void)show {
219 | [self showWithSelectedCompletion:NULL];
220 | }
221 |
222 | /*! @brief 展示并绑定block回调 */
223 | - (void)showWithSelectedCompletion:(FSActionSheetHandler)selectedHandler {
224 | self.selectedHandler = selectedHandler;
225 |
226 | _backView = [[UIView alloc] init];
227 | _backView.alpha = 0;
228 | _backView.backgroundColor = [UIColor blackColor];
229 | _backView.userInteractionEnabled = _hideOnTouchOutside;
230 | _backView.translatesAutoresizingMaskIntoConstraints = NO;
231 | [_backView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backViewGesture)]];
232 | [_controllerView addSubview:_backView];
233 | [_controllerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_backView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_backView)]];
234 | [_controllerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_backView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_backView)]];
235 |
236 | [self.tableView reloadData];
237 | // 内容高度
238 | CGFloat contentHeight = self.tableView.contentSize.height;
239 | // 适配屏幕高度
240 | CGFloat contentMaxHeight = CGRectGetHeight(self.window.frame)*FSActionSheetContentMaxScale;
241 | if (contentHeight > contentMaxHeight) {
242 | self.tableView.scrollEnabled = YES;
243 | contentHeight = contentMaxHeight;
244 | }
245 | [_controllerView addSubview:self];
246 |
247 | CGFloat selfW = CGRectGetWidth(_controllerView.frame);
248 | CGFloat selfH = contentHeight;
249 | CGFloat selfX = 0;
250 | CGFloat selfY = CGRectGetMaxY(_controllerView.frame);
251 | self.frame = CGRectMake(selfX, selfY, selfW, selfH);
252 |
253 | [self.window makeKeyAndVisible];
254 |
255 | [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
256 | _backView.alpha = 0.38;
257 | CGRect newFrame = self.frame;
258 | newFrame.origin.y = CGRectGetMaxY(_controllerView.frame)-selfH;
259 | self.frame = newFrame;
260 | } completion:^(BOOL finished) {
261 | // constraint
262 | [_controllerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[self]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self)]];
263 | [_controllerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[self]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self)]];
264 | self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:contentHeight];
265 | [_controllerView addConstraint:_heightConstraint];
266 | }];
267 | }
268 |
269 | #pragma mark - getter
270 | - (UIWindow *)window {
271 | if (_window) return _window;
272 |
273 | _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
274 | _window.windowLevel = UIWindowLevelStatusBar+1;
275 | _window.rootViewController = [[UIViewController alloc] init];
276 | self.controllerView = _window.rootViewController.view;
277 |
278 | return _window;
279 | }
280 | - (UITableView *)tableView {
281 | if (_tableView) return _tableView;
282 |
283 | _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
284 | _tableView.delegate = self;
285 | _tableView.dataSource = self;
286 | _tableView.scrollEnabled = NO;
287 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
288 | _tableView.backgroundColor = self.backgroundColor;
289 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
290 |
291 | [_tableView registerClass:[FSActionSheetCell class] forCellReuseIdentifier:kFSActionSheetCellIdentifier];
292 |
293 | if (_title.length > 0) {
294 | _tableView.tableHeaderView = [self headerView];
295 | }
296 |
297 | return _tableView;
298 | }
299 |
300 | // tableHeaderView作为title部分
301 | - (UIView *)headerView {
302 | UIView *headerView = [[UIView alloc] init];
303 | headerView.backgroundColor = FSColorWithString(FSActionSheetRowNormalColor);
304 |
305 | // 标题
306 | UILabel *titleLabel = [[UILabel alloc] init];
307 | titleLabel.numberOfLines = 0;
308 | titleLabel.backgroundColor = headerView.backgroundColor;
309 | titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
310 | [headerView addSubview:titleLabel];
311 | self.titleLabel = titleLabel;
312 | // 设置富文本标题内容
313 | [self updateTitleAttributeText];
314 |
315 | // 标题内容边距 (ps: 要修改这个边距不要在这里修改这个labelMargin, 要到配置类中修改 FSActionSheetDefaultMargin, 不然可能出现界面适配错乱).
316 | CGFloat labelMargin = FSActionSheetDefaultMargin;
317 | // 计算内容高度
318 | CGFloat headerHeight = [self heightForHeaderView];
319 | headerView.frame = CGRectMake(0, 0, CGRectGetWidth(self.window.frame), headerHeight);
320 |
321 | // titleLabel constraint
322 | [headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-labelMargin-[titleLabel]-labelMargin-|" options:0 metrics:@{@"labelMargin":@(labelMargin)} views:NSDictionaryOfVariableBindings(titleLabel)]];
323 | [headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-labelMargin-[titleLabel]" options:0 metrics:@{@"labelMargin":@(labelMargin)} views:NSDictionaryOfVariableBindings(titleLabel)]];
324 |
325 | return headerView;
326 | }
327 |
328 | #pragma mark - setter
329 | - (void)setContentAlignment:(FSContentAlignment)contentAlignment {
330 | if (_contentAlignment != contentAlignment) {
331 | _contentAlignment = contentAlignment;
332 | [self updateTitleAttributeText];
333 | }
334 | }
335 |
336 | #pragma mark - delegate
337 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
338 | return 2;
339 | }
340 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
341 | return (section == 1)?1:_items.count;
342 | }
343 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
344 | return FSActionSheetRowHeight;
345 | }
346 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
347 | return (section == 1)?kFSActionSheetSectionHeight:CGFLOAT_MIN;
348 | }
349 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
350 | FSActionSheetCell *cell = [tableView dequeueReusableCellWithIdentifier:kFSActionSheetCellIdentifier];
351 | return cell;
352 | }
353 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
354 | FSActionSheetCell *sheetCell = (FSActionSheetCell *)cell;
355 | if (indexPath.section == 0) {
356 | sheetCell.item = _items[indexPath.row];
357 | sheetCell.hideTopLine = NO;
358 | // 当有标题时隐藏第一单元格的顶部线条
359 | if (indexPath.row == 0 && (!_title || _title.length == 0)) {
360 | sheetCell.hideTopLine = YES;
361 | }
362 | } else {
363 | // 默认取消的单元格没有附带icon
364 | FSActionSheetItem *cancelItem = FSActionSheetTitleItemMake(FSActionSheetTypeNormal, _cancelTitle);
365 | // 如果其它单元格中附带icon的话则添加上默认的取消icon.
366 | for (FSActionSheetItem *item in _items) {
367 | if (item.image) {
368 | cancelItem = FSActionSheetTitleWithImageItemMake(FSActionSheetTypeNormal, [UIImage imageNamed:@"FSActionSheet_cancel"], _cancelTitle);
369 | break;
370 | }
371 | }
372 | sheetCell.item = cancelItem;
373 | sheetCell.hideTopLine = YES;
374 | }
375 | sheetCell.contentAlignment = _contentAlignment;
376 | }
377 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
378 | // 延迟0.1秒隐藏让用户既看到点击效果又不影响体验
379 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
380 | __weak __typeof(&*self)weakSelf = self;
381 | [self hideWithCompletion:^{
382 | if (indexPath.section == 0) {
383 | if (_selectedHandler) {
384 | _selectedHandler(indexPath.row);
385 | }
386 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(FSActionSheet:selectedIndex:)]) {
387 | [weakSelf.delegate FSActionSheet:self selectedIndex:indexPath.row];
388 | }
389 | }
390 | }];
391 | });
392 | }
393 |
394 | @end
395 |
--------------------------------------------------------------------------------
/WebViewLongPress.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4D6DF1FA1D0F96AC00299D2D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF1F91D0F96AC00299D2D /* main.m */; };
11 | 4D6DF1FD1D0F96AC00299D2D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF1FC1D0F96AC00299D2D /* AppDelegate.m */; };
12 | 4D6DF2031D0F96AC00299D2D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DF2011D0F96AC00299D2D /* Main.storyboard */; };
13 | 4D6DF2051D0F96AC00299D2D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DF2041D0F96AC00299D2D /* Assets.xcassets */; };
14 | 4D6DF2081D0F96AC00299D2D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DF2061D0F96AC00299D2D /* LaunchScreen.storyboard */; };
15 | 4D6DF2131D0F96AC00299D2D /* WebViewLongPressTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2121D0F96AC00299D2D /* WebViewLongPressTests.m */; };
16 | 4D6DF2361D0F9C4800299D2D /* FSActionSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2241D0F9C4800299D2D /* FSActionSheet.m */; };
17 | 4D6DF2371D0F9C4800299D2D /* FSActionSheetCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2261D0F9C4800299D2D /* FSActionSheetCell.m */; };
18 | 4D6DF2381D0F9C4800299D2D /* FSActionSheetConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2281D0F9C4800299D2D /* FSActionSheetConfig.m */; };
19 | 4D6DF2391D0F9C4800299D2D /* FSActionSheetItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF22A1D0F9C4800299D2D /* FSActionSheetItem.m */; };
20 | 4D6DF23A1D0F9C4800299D2D /* FSActionSheet_cancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DF22C1D0F9C4800299D2D /* FSActionSheet_cancel@2x.png */; };
21 | 4D6DF23B1D0F9C4800299D2D /* FSActionSheet_cancel@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DF22D1D0F9C4800299D2D /* FSActionSheet_cancel@3x.png */; };
22 | 4D6DF23C1D0F9C4800299D2D /* NSString+Sha1.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2301D0F9C4800299D2D /* NSString+Sha1.m */; };
23 | 4D6DF23D1D0F9C4800299D2D /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2321D0F9C4800299D2D /* Reachability.m */; };
24 | 4D6DF23E1D0F9C4800299D2D /* RNCachingURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2341D0F9C4800299D2D /* RNCachingURLProtocol.m */; };
25 | 4D6DF2411D0F9EF400299D2D /* SwizzeMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2401D0F9EF400299D2D /* SwizzeMethod.m */; };
26 | 4D6DF2471D0FA3F900299D2D /* CVWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2461D0FA3F900299D2D /* CVWebViewController.m */; };
27 | 4D6DF24A1D0FA47F00299D2D /* CVWebViewController+ImageHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6DF2491D0FA47F00299D2D /* CVWebViewController+ImageHelper.m */; };
28 | 4DE18C321E53F31C004C75DB /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE18C311E53F31C004C75DB /* MainViewController.m */; };
29 | 4DE18C351E53F3F8004C75DB /* WKWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE18C341E53F3F8004C75DB /* WKWebViewController.m */; };
30 | /* End PBXBuildFile section */
31 |
32 | /* Begin PBXContainerItemProxy section */
33 | 4D6DF20F1D0F96AC00299D2D /* PBXContainerItemProxy */ = {
34 | isa = PBXContainerItemProxy;
35 | containerPortal = 4D6DF1ED1D0F96AC00299D2D /* Project object */;
36 | proxyType = 1;
37 | remoteGlobalIDString = 4D6DF1F41D0F96AC00299D2D;
38 | remoteInfo = WebViewLongPress;
39 | };
40 | /* End PBXContainerItemProxy section */
41 |
42 | /* Begin PBXFileReference section */
43 | 4D6DF1F51D0F96AC00299D2D /* WebViewLongPress.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebViewLongPress.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 4D6DF1F91D0F96AC00299D2D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
45 | 4D6DF1FB1D0F96AC00299D2D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
46 | 4D6DF1FC1D0F96AC00299D2D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
47 | 4D6DF2021D0F96AC00299D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
48 | 4D6DF2041D0F96AC00299D2D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
49 | 4D6DF2071D0F96AC00299D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
50 | 4D6DF2091D0F96AC00299D2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 4D6DF20E1D0F96AC00299D2D /* WebViewLongPressTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebViewLongPressTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 4D6DF2121D0F96AC00299D2D /* WebViewLongPressTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebViewLongPressTests.m; sourceTree = ""; };
53 | 4D6DF2141D0F96AC00299D2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
54 | 4D6DF2231D0F9C4800299D2D /* FSActionSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSActionSheet.h; sourceTree = ""; };
55 | 4D6DF2241D0F9C4800299D2D /* FSActionSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSActionSheet.m; sourceTree = ""; };
56 | 4D6DF2251D0F9C4800299D2D /* FSActionSheetCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSActionSheetCell.h; sourceTree = ""; };
57 | 4D6DF2261D0F9C4800299D2D /* FSActionSheetCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSActionSheetCell.m; sourceTree = ""; };
58 | 4D6DF2271D0F9C4800299D2D /* FSActionSheetConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSActionSheetConfig.h; sourceTree = ""; };
59 | 4D6DF2281D0F9C4800299D2D /* FSActionSheetConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSActionSheetConfig.m; sourceTree = ""; };
60 | 4D6DF2291D0F9C4800299D2D /* FSActionSheetItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSActionSheetItem.h; sourceTree = ""; };
61 | 4D6DF22A1D0F9C4800299D2D /* FSActionSheetItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSActionSheetItem.m; sourceTree = ""; };
62 | 4D6DF22C1D0F9C4800299D2D /* FSActionSheet_cancel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "FSActionSheet_cancel@2x.png"; sourceTree = ""; };
63 | 4D6DF22D1D0F9C4800299D2D /* FSActionSheet_cancel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "FSActionSheet_cancel@3x.png"; sourceTree = ""; };
64 | 4D6DF22F1D0F9C4800299D2D /* NSString+Sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Sha1.h"; sourceTree = ""; };
65 | 4D6DF2301D0F9C4800299D2D /* NSString+Sha1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Sha1.m"; sourceTree = ""; };
66 | 4D6DF2311D0F9C4800299D2D /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; };
67 | 4D6DF2321D0F9C4800299D2D /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; };
68 | 4D6DF2331D0F9C4800299D2D /* RNCachingURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCachingURLProtocol.h; sourceTree = ""; };
69 | 4D6DF2341D0F9C4800299D2D /* RNCachingURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCachingURLProtocol.m; sourceTree = ""; };
70 | 4D6DF23F1D0F9EF400299D2D /* SwizzeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwizzeMethod.h; sourceTree = ""; };
71 | 4D6DF2401D0F9EF400299D2D /* SwizzeMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwizzeMethod.m; sourceTree = ""; };
72 | 4D6DF2451D0FA3F900299D2D /* CVWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CVWebViewController.h; sourceTree = ""; };
73 | 4D6DF2461D0FA3F900299D2D /* CVWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CVWebViewController.m; sourceTree = ""; };
74 | 4D6DF2481D0FA47F00299D2D /* CVWebViewController+ImageHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CVWebViewController+ImageHelper.h"; sourceTree = ""; };
75 | 4D6DF2491D0FA47F00299D2D /* CVWebViewController+ImageHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CVWebViewController+ImageHelper.m"; sourceTree = ""; };
76 | 4DE18C301E53F31C004C75DB /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; };
77 | 4DE18C311E53F31C004C75DB /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; };
78 | 4DE18C331E53F3F8004C75DB /* WKWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewController.h; sourceTree = ""; };
79 | 4DE18C341E53F3F8004C75DB /* WKWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewController.m; sourceTree = ""; };
80 | /* End PBXFileReference section */
81 |
82 | /* Begin PBXFrameworksBuildPhase section */
83 | 4D6DF1F21D0F96AC00299D2D /* Frameworks */ = {
84 | isa = PBXFrameworksBuildPhase;
85 | buildActionMask = 2147483647;
86 | files = (
87 | );
88 | runOnlyForDeploymentPostprocessing = 0;
89 | };
90 | 4D6DF20B1D0F96AC00299D2D /* Frameworks */ = {
91 | isa = PBXFrameworksBuildPhase;
92 | buildActionMask = 2147483647;
93 | files = (
94 | );
95 | runOnlyForDeploymentPostprocessing = 0;
96 | };
97 | /* End PBXFrameworksBuildPhase section */
98 |
99 | /* Begin PBXGroup section */
100 | 4D6DF1EC1D0F96AC00299D2D = {
101 | isa = PBXGroup;
102 | children = (
103 | 4D6DF1F71D0F96AC00299D2D /* WebViewLongPress */,
104 | 4D6DF2111D0F96AC00299D2D /* WebViewLongPressTests */,
105 | 4D6DF1F61D0F96AC00299D2D /* Products */,
106 | );
107 | sourceTree = "";
108 | };
109 | 4D6DF1F61D0F96AC00299D2D /* Products */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 4D6DF1F51D0F96AC00299D2D /* WebViewLongPress.app */,
113 | 4D6DF20E1D0F96AC00299D2D /* WebViewLongPressTests.xctest */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 4D6DF1F71D0F96AC00299D2D /* WebViewLongPress */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 4DE18C2F1E53EFCF004C75DB /* WebKit */,
122 | 4DE18C2E1E53EFC0004C75DB /* UIWeb */,
123 | 4D6DF21D1D0F9C4800299D2D /* Vendor */,
124 | 4D6DF23F1D0F9EF400299D2D /* SwizzeMethod.h */,
125 | 4D6DF2401D0F9EF400299D2D /* SwizzeMethod.m */,
126 | 4D6DF1FB1D0F96AC00299D2D /* AppDelegate.h */,
127 | 4D6DF1FC1D0F96AC00299D2D /* AppDelegate.m */,
128 | 4DE18C301E53F31C004C75DB /* MainViewController.h */,
129 | 4DE18C311E53F31C004C75DB /* MainViewController.m */,
130 | 4D6DF2011D0F96AC00299D2D /* Main.storyboard */,
131 | 4D6DF2041D0F96AC00299D2D /* Assets.xcassets */,
132 | 4D6DF2061D0F96AC00299D2D /* LaunchScreen.storyboard */,
133 | 4D6DF2091D0F96AC00299D2D /* Info.plist */,
134 | 4D6DF1F81D0F96AC00299D2D /* Supporting Files */,
135 | );
136 | path = WebViewLongPress;
137 | sourceTree = "";
138 | };
139 | 4D6DF1F81D0F96AC00299D2D /* Supporting Files */ = {
140 | isa = PBXGroup;
141 | children = (
142 | 4D6DF1F91D0F96AC00299D2D /* main.m */,
143 | );
144 | name = "Supporting Files";
145 | sourceTree = "";
146 | };
147 | 4D6DF2111D0F96AC00299D2D /* WebViewLongPressTests */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 4D6DF2121D0F96AC00299D2D /* WebViewLongPressTests.m */,
151 | 4D6DF2141D0F96AC00299D2D /* Info.plist */,
152 | );
153 | path = WebViewLongPressTests;
154 | sourceTree = "";
155 | };
156 | 4D6DF21D1D0F9C4800299D2D /* Vendor */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 4D6DF2221D0F9C4800299D2D /* FSActionSheet */,
160 | 4D6DF22E1D0F9C4800299D2D /* RNCachingURLProtocol */,
161 | );
162 | path = Vendor;
163 | sourceTree = "";
164 | };
165 | 4D6DF2221D0F9C4800299D2D /* FSActionSheet */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 4D6DF2231D0F9C4800299D2D /* FSActionSheet.h */,
169 | 4D6DF2241D0F9C4800299D2D /* FSActionSheet.m */,
170 | 4D6DF2251D0F9C4800299D2D /* FSActionSheetCell.h */,
171 | 4D6DF2261D0F9C4800299D2D /* FSActionSheetCell.m */,
172 | 4D6DF2271D0F9C4800299D2D /* FSActionSheetConfig.h */,
173 | 4D6DF2281D0F9C4800299D2D /* FSActionSheetConfig.m */,
174 | 4D6DF2291D0F9C4800299D2D /* FSActionSheetItem.h */,
175 | 4D6DF22A1D0F9C4800299D2D /* FSActionSheetItem.m */,
176 | 4D6DF22B1D0F9C4800299D2D /* FSActionSheetResources */,
177 | );
178 | path = FSActionSheet;
179 | sourceTree = "";
180 | };
181 | 4D6DF22B1D0F9C4800299D2D /* FSActionSheetResources */ = {
182 | isa = PBXGroup;
183 | children = (
184 | 4D6DF22C1D0F9C4800299D2D /* FSActionSheet_cancel@2x.png */,
185 | 4D6DF22D1D0F9C4800299D2D /* FSActionSheet_cancel@3x.png */,
186 | );
187 | path = FSActionSheetResources;
188 | sourceTree = "";
189 | };
190 | 4D6DF22E1D0F9C4800299D2D /* RNCachingURLProtocol */ = {
191 | isa = PBXGroup;
192 | children = (
193 | 4D6DF22F1D0F9C4800299D2D /* NSString+Sha1.h */,
194 | 4D6DF2301D0F9C4800299D2D /* NSString+Sha1.m */,
195 | 4D6DF2311D0F9C4800299D2D /* Reachability.h */,
196 | 4D6DF2321D0F9C4800299D2D /* Reachability.m */,
197 | 4D6DF2331D0F9C4800299D2D /* RNCachingURLProtocol.h */,
198 | 4D6DF2341D0F9C4800299D2D /* RNCachingURLProtocol.m */,
199 | );
200 | path = RNCachingURLProtocol;
201 | sourceTree = "";
202 | };
203 | 4DE18C2E1E53EFC0004C75DB /* UIWeb */ = {
204 | isa = PBXGroup;
205 | children = (
206 | 4D6DF2481D0FA47F00299D2D /* CVWebViewController+ImageHelper.h */,
207 | 4D6DF2491D0FA47F00299D2D /* CVWebViewController+ImageHelper.m */,
208 | 4D6DF2451D0FA3F900299D2D /* CVWebViewController.h */,
209 | 4D6DF2461D0FA3F900299D2D /* CVWebViewController.m */,
210 | );
211 | name = UIWeb;
212 | sourceTree = "";
213 | };
214 | 4DE18C2F1E53EFCF004C75DB /* WebKit */ = {
215 | isa = PBXGroup;
216 | children = (
217 | 4DE18C331E53F3F8004C75DB /* WKWebViewController.h */,
218 | 4DE18C341E53F3F8004C75DB /* WKWebViewController.m */,
219 | );
220 | name = WebKit;
221 | sourceTree = "";
222 | };
223 | /* End PBXGroup section */
224 |
225 | /* Begin PBXNativeTarget section */
226 | 4D6DF1F41D0F96AC00299D2D /* WebViewLongPress */ = {
227 | isa = PBXNativeTarget;
228 | buildConfigurationList = 4D6DF2171D0F96AC00299D2D /* Build configuration list for PBXNativeTarget "WebViewLongPress" */;
229 | buildPhases = (
230 | 4D6DF1F11D0F96AC00299D2D /* Sources */,
231 | 4D6DF1F21D0F96AC00299D2D /* Frameworks */,
232 | 4D6DF1F31D0F96AC00299D2D /* Resources */,
233 | );
234 | buildRules = (
235 | );
236 | dependencies = (
237 | );
238 | name = WebViewLongPress;
239 | productName = WebViewLongPress;
240 | productReference = 4D6DF1F51D0F96AC00299D2D /* WebViewLongPress.app */;
241 | productType = "com.apple.product-type.application";
242 | };
243 | 4D6DF20D1D0F96AC00299D2D /* WebViewLongPressTests */ = {
244 | isa = PBXNativeTarget;
245 | buildConfigurationList = 4D6DF21A1D0F96AC00299D2D /* Build configuration list for PBXNativeTarget "WebViewLongPressTests" */;
246 | buildPhases = (
247 | 4D6DF20A1D0F96AC00299D2D /* Sources */,
248 | 4D6DF20B1D0F96AC00299D2D /* Frameworks */,
249 | 4D6DF20C1D0F96AC00299D2D /* Resources */,
250 | );
251 | buildRules = (
252 | );
253 | dependencies = (
254 | 4D6DF2101D0F96AC00299D2D /* PBXTargetDependency */,
255 | );
256 | name = WebViewLongPressTests;
257 | productName = WebViewLongPressTests;
258 | productReference = 4D6DF20E1D0F96AC00299D2D /* WebViewLongPressTests.xctest */;
259 | productType = "com.apple.product-type.bundle.unit-test";
260 | };
261 | /* End PBXNativeTarget section */
262 |
263 | /* Begin PBXProject section */
264 | 4D6DF1ED1D0F96AC00299D2D /* Project object */ = {
265 | isa = PBXProject;
266 | attributes = {
267 | LastUpgradeCheck = 0730;
268 | ORGANIZATIONNAME = cvte;
269 | TargetAttributes = {
270 | 4D6DF1F41D0F96AC00299D2D = {
271 | CreatedOnToolsVersion = 7.3;
272 | DevelopmentTeam = E48S8MXDB9;
273 | };
274 | 4D6DF20D1D0F96AC00299D2D = {
275 | CreatedOnToolsVersion = 7.3;
276 | TestTargetID = 4D6DF1F41D0F96AC00299D2D;
277 | };
278 | };
279 | };
280 | buildConfigurationList = 4D6DF1F01D0F96AC00299D2D /* Build configuration list for PBXProject "WebViewLongPress" */;
281 | compatibilityVersion = "Xcode 3.2";
282 | developmentRegion = English;
283 | hasScannedForEncodings = 0;
284 | knownRegions = (
285 | en,
286 | Base,
287 | );
288 | mainGroup = 4D6DF1EC1D0F96AC00299D2D;
289 | productRefGroup = 4D6DF1F61D0F96AC00299D2D /* Products */;
290 | projectDirPath = "";
291 | projectRoot = "";
292 | targets = (
293 | 4D6DF1F41D0F96AC00299D2D /* WebViewLongPress */,
294 | 4D6DF20D1D0F96AC00299D2D /* WebViewLongPressTests */,
295 | );
296 | };
297 | /* End PBXProject section */
298 |
299 | /* Begin PBXResourcesBuildPhase section */
300 | 4D6DF1F31D0F96AC00299D2D /* Resources */ = {
301 | isa = PBXResourcesBuildPhase;
302 | buildActionMask = 2147483647;
303 | files = (
304 | 4D6DF2081D0F96AC00299D2D /* LaunchScreen.storyboard in Resources */,
305 | 4D6DF23A1D0F9C4800299D2D /* FSActionSheet_cancel@2x.png in Resources */,
306 | 4D6DF2051D0F96AC00299D2D /* Assets.xcassets in Resources */,
307 | 4D6DF2031D0F96AC00299D2D /* Main.storyboard in Resources */,
308 | 4D6DF23B1D0F9C4800299D2D /* FSActionSheet_cancel@3x.png in Resources */,
309 | );
310 | runOnlyForDeploymentPostprocessing = 0;
311 | };
312 | 4D6DF20C1D0F96AC00299D2D /* Resources */ = {
313 | isa = PBXResourcesBuildPhase;
314 | buildActionMask = 2147483647;
315 | files = (
316 | );
317 | runOnlyForDeploymentPostprocessing = 0;
318 | };
319 | /* End PBXResourcesBuildPhase section */
320 |
321 | /* Begin PBXSourcesBuildPhase section */
322 | 4D6DF1F11D0F96AC00299D2D /* Sources */ = {
323 | isa = PBXSourcesBuildPhase;
324 | buildActionMask = 2147483647;
325 | files = (
326 | 4D6DF2381D0F9C4800299D2D /* FSActionSheetConfig.m in Sources */,
327 | 4D6DF2361D0F9C4800299D2D /* FSActionSheet.m in Sources */,
328 | 4D6DF2411D0F9EF400299D2D /* SwizzeMethod.m in Sources */,
329 | 4DE18C351E53F3F8004C75DB /* WKWebViewController.m in Sources */,
330 | 4D6DF2371D0F9C4800299D2D /* FSActionSheetCell.m in Sources */,
331 | 4DE18C321E53F31C004C75DB /* MainViewController.m in Sources */,
332 | 4D6DF23D1D0F9C4800299D2D /* Reachability.m in Sources */,
333 | 4D6DF23C1D0F9C4800299D2D /* NSString+Sha1.m in Sources */,
334 | 4D6DF23E1D0F9C4800299D2D /* RNCachingURLProtocol.m in Sources */,
335 | 4D6DF2391D0F9C4800299D2D /* FSActionSheetItem.m in Sources */,
336 | 4D6DF1FD1D0F96AC00299D2D /* AppDelegate.m in Sources */,
337 | 4D6DF24A1D0FA47F00299D2D /* CVWebViewController+ImageHelper.m in Sources */,
338 | 4D6DF1FA1D0F96AC00299D2D /* main.m in Sources */,
339 | 4D6DF2471D0FA3F900299D2D /* CVWebViewController.m in Sources */,
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | };
343 | 4D6DF20A1D0F96AC00299D2D /* Sources */ = {
344 | isa = PBXSourcesBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | 4D6DF2131D0F96AC00299D2D /* WebViewLongPressTests.m in Sources */,
348 | );
349 | runOnlyForDeploymentPostprocessing = 0;
350 | };
351 | /* End PBXSourcesBuildPhase section */
352 |
353 | /* Begin PBXTargetDependency section */
354 | 4D6DF2101D0F96AC00299D2D /* PBXTargetDependency */ = {
355 | isa = PBXTargetDependency;
356 | target = 4D6DF1F41D0F96AC00299D2D /* WebViewLongPress */;
357 | targetProxy = 4D6DF20F1D0F96AC00299D2D /* PBXContainerItemProxy */;
358 | };
359 | /* End PBXTargetDependency section */
360 |
361 | /* Begin PBXVariantGroup section */
362 | 4D6DF2011D0F96AC00299D2D /* Main.storyboard */ = {
363 | isa = PBXVariantGroup;
364 | children = (
365 | 4D6DF2021D0F96AC00299D2D /* Base */,
366 | );
367 | name = Main.storyboard;
368 | sourceTree = "";
369 | };
370 | 4D6DF2061D0F96AC00299D2D /* LaunchScreen.storyboard */ = {
371 | isa = PBXVariantGroup;
372 | children = (
373 | 4D6DF2071D0F96AC00299D2D /* Base */,
374 | );
375 | name = LaunchScreen.storyboard;
376 | sourceTree = "";
377 | };
378 | /* End PBXVariantGroup section */
379 |
380 | /* Begin XCBuildConfiguration section */
381 | 4D6DF2151D0F96AC00299D2D /* Debug */ = {
382 | isa = XCBuildConfiguration;
383 | buildSettings = {
384 | ALWAYS_SEARCH_USER_PATHS = NO;
385 | CLANG_ANALYZER_NONNULL = YES;
386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
387 | CLANG_CXX_LIBRARY = "libc++";
388 | CLANG_ENABLE_MODULES = YES;
389 | CLANG_ENABLE_OBJC_ARC = YES;
390 | CLANG_WARN_BOOL_CONVERSION = YES;
391 | CLANG_WARN_CONSTANT_CONVERSION = YES;
392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
393 | CLANG_WARN_EMPTY_BODY = YES;
394 | CLANG_WARN_ENUM_CONVERSION = YES;
395 | CLANG_WARN_INT_CONVERSION = YES;
396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
397 | CLANG_WARN_UNREACHABLE_CODE = YES;
398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
399 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
400 | COPY_PHASE_STRIP = NO;
401 | DEBUG_INFORMATION_FORMAT = dwarf;
402 | ENABLE_STRICT_OBJC_MSGSEND = YES;
403 | ENABLE_TESTABILITY = YES;
404 | GCC_C_LANGUAGE_STANDARD = gnu99;
405 | GCC_DYNAMIC_NO_PIC = NO;
406 | GCC_NO_COMMON_BLOCKS = YES;
407 | GCC_OPTIMIZATION_LEVEL = 0;
408 | GCC_PREPROCESSOR_DEFINITIONS = (
409 | "DEBUG=1",
410 | "$(inherited)",
411 | );
412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
414 | GCC_WARN_UNDECLARED_SELECTOR = YES;
415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
416 | GCC_WARN_UNUSED_FUNCTION = YES;
417 | GCC_WARN_UNUSED_VARIABLE = YES;
418 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
419 | MTL_ENABLE_DEBUG_INFO = YES;
420 | ONLY_ACTIVE_ARCH = YES;
421 | SDKROOT = iphoneos;
422 | TARGETED_DEVICE_FAMILY = "1,2";
423 | };
424 | name = Debug;
425 | };
426 | 4D6DF2161D0F96AC00299D2D /* Release */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | ALWAYS_SEARCH_USER_PATHS = NO;
430 | CLANG_ANALYZER_NONNULL = YES;
431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
432 | CLANG_CXX_LIBRARY = "libc++";
433 | CLANG_ENABLE_MODULES = YES;
434 | CLANG_ENABLE_OBJC_ARC = YES;
435 | CLANG_WARN_BOOL_CONVERSION = YES;
436 | CLANG_WARN_CONSTANT_CONVERSION = YES;
437 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
438 | CLANG_WARN_EMPTY_BODY = YES;
439 | CLANG_WARN_ENUM_CONVERSION = YES;
440 | CLANG_WARN_INT_CONVERSION = YES;
441 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
442 | CLANG_WARN_UNREACHABLE_CODE = YES;
443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
445 | COPY_PHASE_STRIP = NO;
446 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
447 | ENABLE_NS_ASSERTIONS = NO;
448 | ENABLE_STRICT_OBJC_MSGSEND = YES;
449 | GCC_C_LANGUAGE_STANDARD = gnu99;
450 | GCC_NO_COMMON_BLOCKS = YES;
451 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
452 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
453 | GCC_WARN_UNDECLARED_SELECTOR = YES;
454 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
455 | GCC_WARN_UNUSED_FUNCTION = YES;
456 | GCC_WARN_UNUSED_VARIABLE = YES;
457 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
458 | MTL_ENABLE_DEBUG_INFO = NO;
459 | SDKROOT = iphoneos;
460 | TARGETED_DEVICE_FAMILY = "1,2";
461 | VALIDATE_PRODUCT = YES;
462 | };
463 | name = Release;
464 | };
465 | 4D6DF2181D0F96AC00299D2D /* Debug */ = {
466 | isa = XCBuildConfiguration;
467 | buildSettings = {
468 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
469 | CODE_SIGN_IDENTITY = "iPhone Developer";
470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
471 | DEVELOPMENT_TEAM = E48S8MXDB9;
472 | INFOPLIST_FILE = WebViewLongPress/Info.plist;
473 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
475 | PRODUCT_BUNDLE_IDENTIFIER = com.cvte.WebViewLongPress;
476 | PRODUCT_NAME = "$(TARGET_NAME)";
477 | PROVISIONING_PROFILE = "";
478 | };
479 | name = Debug;
480 | };
481 | 4D6DF2191D0F96AC00299D2D /* Release */ = {
482 | isa = XCBuildConfiguration;
483 | buildSettings = {
484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
485 | CODE_SIGN_IDENTITY = "iPhone Developer";
486 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
487 | DEVELOPMENT_TEAM = E48S8MXDB9;
488 | INFOPLIST_FILE = WebViewLongPress/Info.plist;
489 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
490 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
491 | PRODUCT_BUNDLE_IDENTIFIER = com.cvte.WebViewLongPress;
492 | PRODUCT_NAME = "$(TARGET_NAME)";
493 | PROVISIONING_PROFILE = "";
494 | };
495 | name = Release;
496 | };
497 | 4D6DF21B1D0F96AC00299D2D /* Debug */ = {
498 | isa = XCBuildConfiguration;
499 | buildSettings = {
500 | BUNDLE_LOADER = "$(TEST_HOST)";
501 | INFOPLIST_FILE = WebViewLongPressTests/Info.plist;
502 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
503 | PRODUCT_BUNDLE_IDENTIFIER = com.cvte.WebViewLongPressTests;
504 | PRODUCT_NAME = "$(TARGET_NAME)";
505 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebViewLongPress.app/WebViewLongPress";
506 | };
507 | name = Debug;
508 | };
509 | 4D6DF21C1D0F96AC00299D2D /* Release */ = {
510 | isa = XCBuildConfiguration;
511 | buildSettings = {
512 | BUNDLE_LOADER = "$(TEST_HOST)";
513 | INFOPLIST_FILE = WebViewLongPressTests/Info.plist;
514 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
515 | PRODUCT_BUNDLE_IDENTIFIER = com.cvte.WebViewLongPressTests;
516 | PRODUCT_NAME = "$(TARGET_NAME)";
517 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebViewLongPress.app/WebViewLongPress";
518 | };
519 | name = Release;
520 | };
521 | /* End XCBuildConfiguration section */
522 |
523 | /* Begin XCConfigurationList section */
524 | 4D6DF1F01D0F96AC00299D2D /* Build configuration list for PBXProject "WebViewLongPress" */ = {
525 | isa = XCConfigurationList;
526 | buildConfigurations = (
527 | 4D6DF2151D0F96AC00299D2D /* Debug */,
528 | 4D6DF2161D0F96AC00299D2D /* Release */,
529 | );
530 | defaultConfigurationIsVisible = 0;
531 | defaultConfigurationName = Release;
532 | };
533 | 4D6DF2171D0F96AC00299D2D /* Build configuration list for PBXNativeTarget "WebViewLongPress" */ = {
534 | isa = XCConfigurationList;
535 | buildConfigurations = (
536 | 4D6DF2181D0F96AC00299D2D /* Debug */,
537 | 4D6DF2191D0F96AC00299D2D /* Release */,
538 | );
539 | defaultConfigurationIsVisible = 0;
540 | defaultConfigurationName = Release;
541 | };
542 | 4D6DF21A1D0F96AC00299D2D /* Build configuration list for PBXNativeTarget "WebViewLongPressTests" */ = {
543 | isa = XCConfigurationList;
544 | buildConfigurations = (
545 | 4D6DF21B1D0F96AC00299D2D /* Debug */,
546 | 4D6DF21C1D0F96AC00299D2D /* Release */,
547 | );
548 | defaultConfigurationIsVisible = 0;
549 | defaultConfigurationName = Release;
550 | };
551 | /* End XCConfigurationList section */
552 | };
553 | rootObject = 4D6DF1ED1D0F96AC00299D2D /* Project object */;
554 | }
555 |
--------------------------------------------------------------------------------