├── .gitignore ├── README.md ├── Reveal.framework ├── Headers ├── Reveal └── Versions │ ├── A │ ├── Headers │ │ ├── IBANetServiceTypes.h │ │ ├── IBARevealLoader.h │ │ ├── IBARevealLogger.h │ │ └── Reveal.h │ └── Reveal │ └── Current ├── RxWebViewController ├── NJKWebViewProgress │ ├── NJKWebViewProgress.h │ ├── NJKWebViewProgress.m │ ├── NJKWebViewProgressView.h │ └── NJKWebViewProgressView.m ├── RxWebViewController.h ├── RxWebViewController.m ├── RxWebViewNavigationViewController.h ├── RxWebViewNavigationViewController.m ├── backItemImage@2x.png └── backItemImage_hl@2x.png ├── RxWebViewControllerTest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── roxasora.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── roxasora.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── RxWebViewController.xcscheme │ │ └── xcschememanagement.plist │ └── zzy.xcuserdatad │ └── xcschemes │ ├── RxWebViewControllerTest.xcscheme │ └── xcschememanagement.plist └── RxWebViewControllerTest ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon-120.png │ └── icon-121.png ├── Contents.json └── icon-nav-backButton-bg.imageset │ ├── Contents.json │ └── icon-nav-backButton-bg.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Info.plist ├── RxLabel ├── RxLabel.h ├── RxLabel.m ├── RxTextLinkTapView.h └── RxTextLinkTapView.m ├── ViewController.h ├── ViewController.m ├── demo.gif ├── icon-140.png ├── main.m ├── myNavigationViewController.h ├── myNavigationViewController.m ├── myNormalViewController.h ├── myNormalViewController.m └── myNormalViewController.xib /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.xcuserstate 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxWebViewController 2 | 3 | 更新-15.12.9 为了解决普通 viewController 会出现的各种 pop 问题,弃用之前的 category 形式,改为子类化了 UINavigationController,所以大家需要继承 ```RxWebViewNavigationViewController``` 作为你的 navigationController! 4 | 5 | Update - 15.12.9 In order to solve the issues of pop normal viewControllers ,I've deprecated the category in RXWebViewController and added the subclass of UINavigationController, so you need subclass the ```RxWebViewNavigationViewController``` as your custom navigationController 6 | 7 | ---- 8 | 9 | ### it's a custom UIWebViewController that navigate like navigationController,just like wechat do. 10 | ### 实现类似微信的webView导航效果,左滑返回上个网页,就像UINavigationController 11 | 12 | 13 | like the screen shot gif 14 | 15 | ![image](http://img.hb.aicdn.com/c13d6827dfde42ba7ed7ba1a64c58c0a911efd2f126ca3-pys6eT_fw658) 16 | 17 | ### usage使用 18 | 19 | ### Install 安装 20 | 21 | ------- 22 | 23 | You just need to drag/copy the "RxWebViewController" folder and drop in your project 24 | 将“RxWebViewController”文件夹拖进你的工程中即可 25 | 26 | ### init and push 27 | 28 | ------- 29 | 30 | usage is simple 31 | 32 | -------WARNING-------- first,you should subclass a navigationController 33 | 34 | #import "RxWebViewNavigationViewController.h" 35 | 36 | @interface myNavigationViewController : RxWebViewNavigationViewController 37 | 38 | @end 39 | 40 | then use webviewController as normal viewController 41 | 42 | NSString* urlStr = @"http://github.com"; 43 | RxWebViewController* webViewController = [[RxWebViewController alloc] initWithUrl:[NSURL URLWithString:urlStr]]; 44 | [self.navigationController pushViewController:webViewController animated:YES]; 45 | 46 | and if you want to do some custom things with webview,just subclass it 如果你需要webview的更进一步自定义,子类化即可 47 | 48 | 49 | @interface myWebViewController : RxWebViewController 50 | 51 | //do your custom things 52 | 53 | @end 54 | 55 | 56 | navigation bar tint color and back button style 导航栏的颜色和返回按钮样式 57 | 58 | 59 | 导航栏中出现的 返回 和 关闭 ,均会继承你的 navigationController 中对 navigationBar 的设置,比如: 60 | 61 | UIColor* tintColor = [UIColor whiteColor]; 62 | UIColor* barTintColor = [UIColor blueColor]; 63 | self.navigationController.navigationBar.tintColor = tintColor; 64 | self.navigationController.navigationBar.barTintColor = barTintColor; 65 | [self.navigationController.navigationBar setTitleTextAttributes:@{ NSForegroundColorAttributeName:tintColor 66 | }]; 67 | 68 | 这样来自定义你的navigationBar各控件颜色,webViewController中会遵循此设置,如图 69 | ![image](http://img.hb.aicdn.com/4287d071d7fa4dd8e1276506ed904093a7489352da24-56cRLk_fw658) 70 | 71 | 72 | **也可以像微信那样在你的 navigationBar 中使用自定义的 backButtonBackgroundImage,如图** 73 | 74 | ![image](http://img.hb.aicdn.com/ab84843887791178ba8764b9bde04f4b34f338cc10f8e-1umnI5_fw658) 75 | 76 | 77 | ### Thanks 78 | 79 | ------- 80 | 81 | **I used [NJKWebViewProgress](https://github.com/ninjinkun/NJKWebViewProgress) to make navigation progress, it helps a lot** 82 | -------------------------------------------------------------------------------- /Reveal.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Reveal.framework/Reveal: -------------------------------------------------------------------------------- 1 | Versions/Current/Reveal -------------------------------------------------------------------------------- /Reveal.framework/Versions/A/Headers/IBANetServiceTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Itty Bitty Apps Pty Ltd. All rights reserved. 3 | 4 | #ifndef reveal_core_IBANetServiceTypes_h 5 | #define reveal_core_IBANetServiceTypes_h 6 | #import 7 | 8 | typedef NS_ENUM(uint32_t, IBANetServiceInterface) { 9 | IBANetServiceInterfaceAny = kDNSServiceInterfaceIndexAny, 10 | IBANetServiceInterfaceLocalOnly = kDNSServiceInterfaceIndexLocalOnly, 11 | IBANetServiceInterfaceUnicast = kDNSServiceInterfaceIndexUnicast, 12 | IBANetServiceInterfaceP2P = kDNSServiceInterfaceIndexP2P 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Reveal.framework/Versions/A/Headers/IBARevealLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Itty Bitty Apps. All rights reserved. 3 | 4 | #import 5 | 6 | extern NSString * const IBARevealLoaderRequestStartNotification; 7 | extern NSString * const IBARevealLoaderRequestStopNotification; 8 | 9 | extern NSString * const IBARevealLoaderSetOptionsNotification; 10 | extern NSString * const IBARevealLoaderOptionsLogLevelMaskKey; 11 | 12 | @interface IBARevealLoader : NSObject 13 | 14 | + (void)startServer; 15 | + (void)stopServer; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Reveal.framework/Versions/A/Headers/IBARevealLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved. 2 | // 3 | 4 | #import 5 | 6 | CF_EXTERN_C_BEGIN 7 | 8 | /*! 9 | \brief The Reveal Log level bit flags. 10 | \discussion These flags are additive. i.e. you should bitwise OR them together. 11 | 12 | \seealso IBARevealLoggerSetLevelMask 13 | \seealso IBARevealLoggerGetLevelMask 14 | 15 | Example: 16 | 17 | // Enable Error, Warning and Info logger levels. 18 | IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo); 19 | 20 | */ 21 | typedef NS_OPTIONS(int32_t, IBARevealLogLevel) 22 | { 23 | IBARevealLogLevelNone = 0, 24 | IBARevealLogLevelDebug = (1 << 0), 25 | IBARevealLogLevelInfo = (1 << 1), 26 | IBARevealLogLevelWarn = (1 << 2), 27 | IBARevealLogLevelError = (1 << 3) 28 | }; 29 | 30 | /*! 31 | \brief Set the Reveal logger level mask. 32 | \param mask A bit mask which is a combination of the IBARevealLogLevel enum options. 33 | 34 | \discussion If you do not wish to see log messages from Reveal you should call this function with an appropriate level mask as early in your application's lifecycle as possible. For example in your application's main() function. 35 | 36 | Example: 37 | 38 | // Enable Error, Warning and Info logger levels. 39 | IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo); 40 | 41 | */ 42 | CF_EXPORT void IBARevealLoggerSetLevelMask(int32_t mask); 43 | 44 | /*! 45 | \brief Get the current Reveal logger level mask. 46 | \return A bit mask representing the levels at which Reveal is currently logging. 47 | \discussion The default Reveal Logger level mask is IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo. 48 | 49 | Example: 50 | 51 | // Turn off the Info log level. 52 | IBARevealLoggerSetLevelMask(IBARevealLoggerGetLevelMask() & ~IBARevealLogLevelInfo); 53 | 54 | */ 55 | CF_EXPORT int32_t IBARevealLoggerGetLevelMask(void); 56 | 57 | CF_EXTERN_C_END 58 | -------------------------------------------------------------------------------- /Reveal.framework/Versions/A/Headers/Reveal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved. 2 | // 3 | 4 | #import "IBARevealLogger.h" 5 | #import "IBARevealLoader.h" 6 | -------------------------------------------------------------------------------- /Reveal.framework/Versions/A/Reveal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/Reveal.framework/Versions/A/Reveal -------------------------------------------------------------------------------- /Reveal.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /RxWebViewController/NJKWebViewProgress/NJKWebViewProgress.h: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgress.h 3 | // 4 | // Created by Satoshi Aasano on 4/20/13. 5 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | #undef njk_weak 12 | #if __has_feature(objc_arc_weak) 13 | #define njk_weak weak 14 | #else 15 | #define njk_weak unsafe_unretained 16 | #endif 17 | 18 | extern const float NJKInitialProgressValue; 19 | extern const float NJKInteractiveProgressValue; 20 | extern const float NJKFinalProgressValue; 21 | 22 | typedef void (^NJKWebViewProgressBlock)(float progress); 23 | @protocol NJKWebViewProgressDelegate; 24 | @interface NJKWebViewProgress : NSObject 25 | @property (nonatomic, njk_weak) idprogressDelegate; 26 | @property (nonatomic, njk_weak) idwebViewProxyDelegate; 27 | @property (nonatomic, copy) NJKWebViewProgressBlock progressBlock; 28 | @property (nonatomic, readonly) float progress; // 0.0..1.0 29 | 30 | - (void)reset; 31 | @end 32 | 33 | @protocol NJKWebViewProgressDelegate 34 | - (void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress; 35 | @end 36 | 37 | -------------------------------------------------------------------------------- /RxWebViewController/NJKWebViewProgress/NJKWebViewProgress.m: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgress.m 3 | // 4 | // Created by Satoshi Aasano on 4/20/13. 5 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 6 | // 7 | 8 | #import "NJKWebViewProgress.h" 9 | 10 | NSString *completeRPCURLPath = @"/njkwebviewprogressproxy/complete"; 11 | 12 | const float NJKInitialProgressValue = 0.1f; 13 | const float NJKInteractiveProgressValue = 0.5f; 14 | const float NJKFinalProgressValue = 0.9f; 15 | 16 | @implementation NJKWebViewProgress 17 | { 18 | NSUInteger _loadingCount; 19 | NSUInteger _maxLoadCount; 20 | NSURL *_currentURL; 21 | BOOL _interactive; 22 | } 23 | 24 | - (id)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | _maxLoadCount = _loadingCount = 0; 29 | _interactive = NO; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)startProgress 35 | { 36 | if (_progress < NJKInitialProgressValue) { 37 | [self setProgress:NJKInitialProgressValue]; 38 | } 39 | } 40 | 41 | - (void)incrementProgress 42 | { 43 | float progress = self.progress; 44 | float maxProgress = _interactive ? NJKFinalProgressValue : NJKInteractiveProgressValue; 45 | float remainPercent = (float)_loadingCount / (float)_maxLoadCount; 46 | float increment = (maxProgress - progress) * remainPercent; 47 | progress += increment; 48 | progress = fmin(progress, maxProgress); 49 | [self setProgress:progress]; 50 | } 51 | 52 | - (void)completeProgress 53 | { 54 | [self setProgress:1.0]; 55 | } 56 | 57 | - (void)setProgress:(float)progress 58 | { 59 | // progress should be incremental only 60 | if (progress > _progress || progress == 0) { 61 | _progress = progress; 62 | if ([_progressDelegate respondsToSelector:@selector(webViewProgress:updateProgress:)]) { 63 | [_progressDelegate webViewProgress:self updateProgress:progress]; 64 | } 65 | if (_progressBlock) { 66 | _progressBlock(progress); 67 | } 68 | } 69 | } 70 | 71 | - (void)reset 72 | { 73 | _maxLoadCount = _loadingCount = 0; 74 | _interactive = NO; 75 | [self setProgress:0.0]; 76 | } 77 | 78 | #pragma mark - 79 | #pragma mark UIWebViewDelegate 80 | 81 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 82 | { 83 | if ([request.URL.path isEqualToString:completeRPCURLPath]) { 84 | [self completeProgress]; 85 | return NO; 86 | } 87 | 88 | BOOL ret = YES; 89 | if ([_webViewProxyDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { 90 | ret = [_webViewProxyDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; 91 | } 92 | 93 | BOOL isFragmentJump = NO; 94 | if (request.URL.fragment) { 95 | NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""]; 96 | isFragmentJump = [nonFragmentURL isEqualToString:webView.request.URL.absoluteString]; 97 | } 98 | 99 | BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL]; 100 | 101 | BOOL isHTTP = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"]; 102 | if (ret && !isFragmentJump && isHTTP && isTopLevelNavigation) { 103 | _currentURL = request.URL; 104 | [self reset]; 105 | } 106 | return ret; 107 | } 108 | 109 | - (void)webViewDidStartLoad:(UIWebView *)webView 110 | { 111 | if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { 112 | [_webViewProxyDelegate webViewDidStartLoad:webView]; 113 | } 114 | 115 | _loadingCount++; 116 | _maxLoadCount = fmax(_maxLoadCount, _loadingCount); 117 | 118 | [self startProgress]; 119 | } 120 | 121 | - (void)webViewDidFinishLoad:(UIWebView *)webView 122 | { 123 | if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { 124 | [_webViewProxyDelegate webViewDidFinishLoad:webView]; 125 | } 126 | 127 | _loadingCount--; 128 | [self incrementProgress]; 129 | 130 | NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; 131 | 132 | BOOL interactive = [readyState isEqualToString:@"interactive"]; 133 | if (interactive) { 134 | _interactive = YES; 135 | NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", webView.request.mainDocumentURL.scheme, webView.request.mainDocumentURL.host, completeRPCURLPath]; 136 | [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; 137 | } 138 | 139 | BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; 140 | BOOL complete = [readyState isEqualToString:@"complete"]; 141 | if (complete && isNotRedirect) { 142 | [self completeProgress]; 143 | } 144 | } 145 | 146 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 147 | { 148 | if ([_webViewProxyDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { 149 | [_webViewProxyDelegate webView:webView didFailLoadWithError:error]; 150 | } 151 | 152 | _loadingCount--; 153 | [self incrementProgress]; 154 | 155 | NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; 156 | 157 | BOOL interactive = [readyState isEqualToString:@"interactive"]; 158 | if (interactive) { 159 | _interactive = YES; 160 | NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", webView.request.mainDocumentURL.scheme, webView.request.mainDocumentURL.host, completeRPCURLPath]; 161 | [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; 162 | } 163 | 164 | BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; 165 | BOOL complete = [readyState isEqualToString:@"complete"]; 166 | if ((complete && isNotRedirect) || error) { 167 | [self completeProgress]; 168 | } 169 | } 170 | 171 | #pragma mark - 172 | #pragma mark Method Forwarding 173 | // for future UIWebViewDelegate impl 174 | 175 | - (BOOL)respondsToSelector:(SEL)aSelector 176 | { 177 | if ( [super respondsToSelector:aSelector] ) 178 | return YES; 179 | 180 | if ([self.webViewProxyDelegate respondsToSelector:aSelector]) 181 | return YES; 182 | 183 | return NO; 184 | } 185 | 186 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 187 | { 188 | NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 189 | if(!signature) { 190 | if([_webViewProxyDelegate respondsToSelector:selector]) { 191 | return [(NSObject *)_webViewProxyDelegate methodSignatureForSelector:selector]; 192 | } 193 | } 194 | return signature; 195 | } 196 | 197 | - (void)forwardInvocation:(NSInvocation*)invocation 198 | { 199 | if ([_webViewProxyDelegate respondsToSelector:[invocation selector]]) { 200 | [invocation invokeWithTarget:_webViewProxyDelegate]; 201 | } 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /RxWebViewController/NJKWebViewProgress/NJKWebViewProgressView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgressView.h 3 | // iOS 7 Style WebView Progress Bar 4 | // 5 | // Created by Satoshi Aasano on 11/16/13. 6 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NJKWebViewProgressView : UIView 12 | @property (nonatomic) float progress; 13 | 14 | @property (nonatomic)UIColor* progressColor; 15 | 16 | @property (nonatomic) UIView *progressBarView; 17 | @property (nonatomic) NSTimeInterval barAnimationDuration; // default 0.1 18 | @property (nonatomic) NSTimeInterval fadeAnimationDuration; // default 0.27 19 | @property (nonatomic) NSTimeInterval fadeOutDelay; // default 0.1 20 | 21 | - (void)setProgress:(float)progress animated:(BOOL)animated; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /RxWebViewController/NJKWebViewProgress/NJKWebViewProgressView.m: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgressView.m 3 | // 4 | // Created by Satoshi Aasanoon 11/16/13. 5 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 6 | // 7 | 8 | #import "NJKWebViewProgressView.h" 9 | 10 | @implementation NJKWebViewProgressView 11 | 12 | - (id)initWithFrame:(CGRect)frame 13 | { 14 | self = [super initWithFrame:frame]; 15 | if (self) { 16 | [self configureViews]; 17 | } 18 | return self; 19 | } 20 | 21 | - (void)awakeFromNib 22 | { 23 | [super awakeFromNib]; 24 | [self configureViews]; 25 | } 26 | 27 | -(void)configureViews 28 | { 29 | if (nil == self.progressColor) { 30 | self.progressColor = [UIColor redColor]; 31 | } 32 | self.userInteractionEnabled = NO; 33 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth; 34 | _progressBarView = [[UIView alloc] initWithFrame:self.bounds]; 35 | _progressBarView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 36 | //UIColor *tintColor = [UIColor colorWithRed:22.f / 255.f green:126.f / 255.f blue:251.f / 255.f alpha:1.0]; // iOS7 Safari bar color 37 | //UIColor* tintColor = [UIColor colorWithHue:0 saturation:0 brightness:0 alpha:0.4]; 38 | UIColor* tintColor = self.progressColor; 39 | // if ([UIApplication.sharedApplication.delegate.window respondsToSelector:@selector(setTintColor:)] && UIApplication.sharedApplication.delegate.window.tintColor) { 40 | // tintColor = UIApplication.sharedApplication.delegate.window.tintColor; 41 | // } 42 | _progressBarView.backgroundColor = tintColor; 43 | [self addSubview:_progressBarView]; 44 | 45 | _barAnimationDuration = 0.27f; 46 | _fadeAnimationDuration = 0.27f; 47 | _fadeOutDelay = 0.1f; 48 | } 49 | 50 | -(void)setProgressColor:(UIColor *)progressColor{ 51 | _progressColor = progressColor; 52 | _progressBarView.backgroundColor = progressColor; 53 | } 54 | 55 | -(void)setProgress:(float)progress 56 | { 57 | [self setProgress:progress animated:NO]; 58 | } 59 | 60 | - (void)setProgress:(float)progress animated:(BOOL)animated 61 | { 62 | BOOL isGrowing = progress > 0.0; 63 | [UIView animateWithDuration:(isGrowing && animated) ? _barAnimationDuration : 0.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 64 | CGRect frame = _progressBarView.frame; 65 | frame.size.width = progress * self.bounds.size.width; 66 | _progressBarView.frame = frame; 67 | } completion:nil]; 68 | 69 | if (progress >= 1.0) { 70 | [UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:_fadeOutDelay options:UIViewAnimationOptionCurveEaseInOut animations:^{ 71 | _progressBarView.alpha = 0.0; 72 | } completion:^(BOOL completed){ 73 | CGRect frame = _progressBarView.frame; 74 | frame.size.width = 0; 75 | _progressBarView.frame = frame; 76 | }]; 77 | } 78 | else { 79 | [UIView animateWithDuration:animated ? _fadeAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 80 | _progressBarView.alpha = 1.0; 81 | } completion:nil]; 82 | } 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /RxWebViewController/RxWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxWebViewController.h 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RxWebViewController : UIViewController 12 | 13 | /** 14 | * origin url 15 | */ 16 | @property (nonatomic)NSURL* url; 17 | 18 | /** 19 | * embed webView 20 | */ 21 | @property (nonatomic)UIWebView* webView; 22 | 23 | /** 24 | * tint color of progress view 25 | */ 26 | @property (nonatomic)UIColor* progressViewColor; 27 | 28 | /** 29 | * get instance with url 30 | * 31 | * @param url url 32 | * 33 | * @return instance 34 | */ 35 | -(instancetype)initWithUrl:(NSURL*)url; 36 | 37 | 38 | -(void)reloadWebView; 39 | 40 | @end 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /RxWebViewController/RxWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxWebViewController.m 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "RxWebViewController.h" 10 | #import "NJKWebViewProgress.h" 11 | #import "NJKWebViewProgressView.h" 12 | 13 | #define boundsWidth self.view.bounds.size.width 14 | #define boundsHeight self.view.bounds.size.height 15 | @interface RxWebViewController () 16 | 17 | @property (nonatomic)UIBarButtonItem* customBackBarItem; 18 | @property (nonatomic)UIBarButtonItem* closeButtonItem; 19 | 20 | @property (nonatomic)NJKWebViewProgress* progressProxy; 21 | @property (nonatomic)NJKWebViewProgressView* progressView; 22 | 23 | /** 24 | * array that hold snapshots 25 | */ 26 | @property (nonatomic)NSMutableArray* snapShotsArray; 27 | 28 | /** 29 | * current snapshotview displaying on screen when start swiping 30 | */ 31 | @property (nonatomic)UIView* currentSnapShotView; 32 | 33 | /** 34 | * previous view 35 | */ 36 | @property (nonatomic)UIView* prevSnapShotView; 37 | 38 | /** 39 | * background alpha black view 40 | */ 41 | @property (nonatomic)UIView* swipingBackgoundView; 42 | 43 | /** 44 | * left pan ges 45 | */ 46 | @property (nonatomic)UIPanGestureRecognizer* swipePanGesture; 47 | 48 | /** 49 | * if is swiping now 50 | */ 51 | @property (nonatomic)BOOL isSwipingBack; 52 | 53 | @end 54 | 55 | @implementation RxWebViewController 56 | 57 | -(UIStatusBarStyle) preferredStatusBarStyle{ 58 | return UIStatusBarStyleLightContent; 59 | } 60 | 61 | #pragma mark - init 62 | -(instancetype)initWithUrl:(NSURL *)url{ 63 | self = [super init]; 64 | if (self) { 65 | self.url = url; 66 | _progressViewColor = [UIColor colorWithRed:119.0/255 green:228.0/255 blue:115.0/255 alpha:1]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)viewDidLoad{ 72 | [super viewDidLoad]; 73 | 74 | self.title = @""; 75 | self.view.backgroundColor = [UIColor whiteColor]; 76 | 77 | //config navigation item 78 | self.navigationItem.leftItemsSupplementBackButton = YES; 79 | 80 | self.webView.delegate = self.progressProxy; 81 | [self.view addSubview:self.webView]; 82 | [self.webView loadRequest:[NSURLRequest requestWithURL:self.url]]; 83 | 84 | [self.navigationController.navigationBar addSubview:self.progressView]; 85 | // Do any additional setup after loading the view. 86 | } 87 | 88 | -(void)viewDidDisappear:(BOOL)animated{ 89 | [super viewDidDisappear:animated]; 90 | [self.progressView removeFromSuperview]; 91 | self.webView.delegate = nil; 92 | } 93 | 94 | 95 | #pragma mark - public funcs 96 | -(void)reloadWebView{ 97 | [self.webView reload]; 98 | } 99 | 100 | #pragma mark - logic of push and pop snap shot views 101 | -(void)pushCurrentSnapshotViewWithRequest:(NSURLRequest*)request{ 102 | // NSLog(@"push with request %@",request); 103 | NSURLRequest* lastRequest = (NSURLRequest*)[[self.snapShotsArray lastObject] objectForKey:@"request"]; 104 | 105 | //如果url是很奇怪的就不push 106 | if ([request.URL.absoluteString isEqualToString:@"about:blank"]) { 107 | // NSLog(@"about blank!! return"); 108 | return; 109 | } 110 | //如果url一样就不进行push 111 | if ([lastRequest.URL.absoluteString isEqualToString:request.URL.absoluteString]) { 112 | return; 113 | } 114 | 115 | UIView* currentSnapShotView = [self.webView snapshotViewAfterScreenUpdates:YES]; 116 | [self.snapShotsArray addObject: 117 | @{ 118 | @"request":request, 119 | @"snapShotView":currentSnapShotView 120 | } 121 | ]; 122 | // NSLog(@"now array count %d",self.snapShotsArray.count); 123 | } 124 | 125 | -(void)startPopSnapshotView{ 126 | if (self.isSwipingBack) { 127 | return; 128 | } 129 | if (!self.webView.canGoBack) { 130 | return; 131 | } 132 | self.isSwipingBack = YES; 133 | //create a center of scrren 134 | CGPoint center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2); 135 | 136 | self.currentSnapShotView = [self.webView snapshotViewAfterScreenUpdates:YES]; 137 | 138 | //add shadows just like UINavigationController 139 | self.currentSnapShotView.layer.shadowColor = [UIColor blackColor].CGColor; 140 | self.currentSnapShotView.layer.shadowOffset = CGSizeMake(3, 3); 141 | self.currentSnapShotView.layer.shadowRadius = 5; 142 | self.currentSnapShotView.layer.shadowOpacity = 0.75; 143 | 144 | //move to center of screen 145 | self.currentSnapShotView.center = center; 146 | 147 | self.prevSnapShotView = (UIView*)[[self.snapShotsArray lastObject] objectForKey:@"snapShotView"]; 148 | center.x -= 60; 149 | self.prevSnapShotView.center = center; 150 | self.prevSnapShotView.alpha = 1; 151 | self.view.backgroundColor = [UIColor blackColor]; 152 | 153 | [self.view addSubview:self.prevSnapShotView]; 154 | [self.view addSubview:self.swipingBackgoundView]; 155 | [self.view addSubview:self.currentSnapShotView]; 156 | } 157 | 158 | -(void)popSnapShotViewWithPanGestureDistance:(CGFloat)distance{ 159 | if (!self.isSwipingBack) { 160 | return; 161 | } 162 | 163 | if (distance <= 0) { 164 | return; 165 | } 166 | 167 | CGPoint currentSnapshotViewCenter = CGPointMake(boundsWidth/2, boundsHeight/2); 168 | currentSnapshotViewCenter.x += distance; 169 | CGPoint prevSnapshotViewCenter = CGPointMake(boundsWidth/2, boundsHeight/2); 170 | prevSnapshotViewCenter.x -= (boundsWidth - distance)*60/boundsWidth; 171 | // NSLog(@"prev center x%f",prevSnapshotViewCenter.x); 172 | 173 | self.currentSnapShotView.center = currentSnapshotViewCenter; 174 | self.prevSnapShotView.center = prevSnapshotViewCenter; 175 | self.swipingBackgoundView.alpha = (boundsWidth - distance)/boundsWidth; 176 | } 177 | 178 | -(void)endPopSnapShotView{ 179 | if (!self.isSwipingBack) { 180 | return; 181 | } 182 | 183 | //prevent the user touch for now 184 | self.view.userInteractionEnabled = NO; 185 | 186 | if (self.currentSnapShotView.center.x >= boundsWidth) { 187 | // pop success 188 | [UIView animateWithDuration:0.2 animations:^{ 189 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 190 | 191 | self.currentSnapShotView.center = CGPointMake(boundsWidth*3/2, boundsHeight/2); 192 | self.prevSnapShotView.center = CGPointMake(boundsWidth/2, boundsHeight/2); 193 | self.swipingBackgoundView.alpha = 0; 194 | }completion:^(BOOL finished) { 195 | [self.prevSnapShotView removeFromSuperview]; 196 | [self.swipingBackgoundView removeFromSuperview]; 197 | [self.currentSnapShotView removeFromSuperview]; 198 | [self.webView goBack]; 199 | [self.snapShotsArray removeLastObject]; 200 | self.view.userInteractionEnabled = YES; 201 | 202 | self.isSwipingBack = NO; 203 | }]; 204 | }else{ 205 | //pop fail 206 | [UIView animateWithDuration:0.2 animations:^{ 207 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 208 | 209 | self.currentSnapShotView.center = CGPointMake(boundsWidth/2, boundsHeight/2); 210 | self.prevSnapShotView.center = CGPointMake(boundsWidth/2-60, boundsHeight/2); 211 | self.prevSnapShotView.alpha = 1; 212 | }completion:^(BOOL finished) { 213 | [self.prevSnapShotView removeFromSuperview]; 214 | [self.swipingBackgoundView removeFromSuperview]; 215 | [self.currentSnapShotView removeFromSuperview]; 216 | self.view.userInteractionEnabled = YES; 217 | 218 | self.isSwipingBack = NO; 219 | }]; 220 | } 221 | } 222 | 223 | #pragma mark - update nav items 224 | 225 | -(void)updateNavigationItems{ 226 | if (self.webView.canGoBack) { 227 | UIBarButtonItem *spaceButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; 228 | spaceButtonItem.width = -6.5; 229 | 230 | self.navigationController.interactivePopGestureRecognizer.enabled = NO; 231 | [self.navigationItem setLeftBarButtonItems:@[self.closeButtonItem] animated:NO]; 232 | 233 | //弃用customBackBarItem,使用原生backButtonItem 234 | // [self.navigationItem setLeftBarButtonItems:@[spaceButtonItem,self.customBackBarItem,self.closeButtonItem] animated:NO]; 235 | }else{ 236 | self.navigationController.interactivePopGestureRecognizer.enabled = YES; 237 | [self.navigationItem setLeftBarButtonItems:nil]; 238 | } 239 | } 240 | 241 | #pragma mark - events handler 242 | -(void)swipePanGestureHandler:(UIPanGestureRecognizer*)panGesture{ 243 | CGPoint translation = [panGesture translationInView:self.webView]; 244 | CGPoint location = [panGesture locationInView:self.webView]; 245 | // NSLog(@"pan x %f,pan y %f",translation.x,translation.y); 246 | 247 | if (panGesture.state == UIGestureRecognizerStateBegan) { 248 | if (location.x <= 50 && translation.x > 0) { //开始动画 249 | [self startPopSnapshotView]; 250 | } 251 | }else if (panGesture.state == UIGestureRecognizerStateCancelled || panGesture.state == UIGestureRecognizerStateEnded){ 252 | [self endPopSnapShotView]; 253 | }else if (panGesture.state == UIGestureRecognizerStateChanged){ 254 | [self popSnapShotViewWithPanGestureDistance:translation.x]; 255 | } 256 | } 257 | 258 | -(void)customBackItemClicked{ 259 | [self.webView goBack]; 260 | } 261 | 262 | -(void)closeItemClicked{ 263 | [self.navigationController popViewControllerAnimated:YES]; 264 | } 265 | 266 | #pragma mark - webView delegate 267 | - (void)webViewDidStartLoad:(UIWebView *)webView { 268 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 269 | } 270 | 271 | -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 272 | // NSLog(@"navigation type %d",navigationType); 273 | switch (navigationType) { 274 | case UIWebViewNavigationTypeLinkClicked: { 275 | [self pushCurrentSnapshotViewWithRequest:request]; 276 | break; 277 | } 278 | case UIWebViewNavigationTypeFormSubmitted: { 279 | [self pushCurrentSnapshotViewWithRequest:request]; 280 | break; 281 | } 282 | case UIWebViewNavigationTypeBackForward: { 283 | break; 284 | } 285 | case UIWebViewNavigationTypeReload: { 286 | break; 287 | } 288 | case UIWebViewNavigationTypeFormResubmitted: { 289 | break; 290 | } 291 | case UIWebViewNavigationTypeOther: { 292 | [self pushCurrentSnapshotViewWithRequest:request]; 293 | break; 294 | } 295 | default: { 296 | break; 297 | } 298 | } 299 | [self updateNavigationItems]; 300 | return YES; 301 | } 302 | 303 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 304 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 305 | [self updateNavigationItems]; 306 | NSString *theTitle=[webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 307 | if (theTitle.length > 10) { 308 | theTitle = [[theTitle substringToIndex:9] stringByAppendingString:@"…"]; 309 | } 310 | self.title = theTitle; 311 | // [self.progressView setProgress:1 animated:NO]; 312 | } 313 | 314 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 315 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 316 | } 317 | 318 | #pragma mark - NJProgress delegate 319 | 320 | -(void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress 321 | { 322 | [self.progressView setProgress:progress animated:NO]; 323 | } 324 | 325 | 326 | #pragma mark - setters and getters 327 | -(void)setUrl:(NSURL *)url{ 328 | _url = url; 329 | } 330 | 331 | -(void)setProgressViewColor:(UIColor *)progressViewColor{ 332 | _progressViewColor = progressViewColor; 333 | self.progressView.progressColor = progressViewColor; 334 | } 335 | 336 | -(UIWebView*)webView{ 337 | if (!_webView) { 338 | _webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; 339 | _webView.delegate = (id)self; 340 | _webView.scalesPageToFit = YES; 341 | _webView.backgroundColor = [UIColor whiteColor]; 342 | [_webView addGestureRecognizer:self.swipePanGesture]; 343 | } 344 | return _webView; 345 | } 346 | 347 | -(UIBarButtonItem*)customBackBarItem{ 348 | if (!_customBackBarItem) { 349 | UIImage* backItemImage = [[UIImage imageNamed:@"backItemImage"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 350 | UIImage* backItemHlImage = [[UIImage imageNamed:@"backItemImage-hl"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 351 | 352 | UIButton* backButton = [[UIButton alloc] init]; 353 | [backButton setTitle:@"返回" forState:UIControlStateNormal]; 354 | [backButton setTitleColor:self.navigationController.navigationBar.tintColor forState:UIControlStateNormal]; 355 | [backButton setTitleColor:[self.navigationController.navigationBar.tintColor colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 356 | [backButton.titleLabel setFont:[UIFont systemFontOfSize:17]]; 357 | [backButton setImage:backItemImage forState:UIControlStateNormal]; 358 | [backButton setImage:backItemHlImage forState:UIControlStateHighlighted]; 359 | [backButton sizeToFit]; 360 | 361 | [backButton addTarget:self action:@selector(customBackItemClicked) forControlEvents:UIControlEventTouchUpInside]; 362 | _customBackBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; 363 | } 364 | return _customBackBarItem; 365 | } 366 | 367 | -(UIBarButtonItem*)closeButtonItem{ 368 | if (!_closeButtonItem) { 369 | _closeButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(closeItemClicked)]; 370 | } 371 | return _closeButtonItem; 372 | } 373 | 374 | -(UIView*)swipingBackgoundView{ 375 | if (!_swipingBackgoundView) { 376 | _swipingBackgoundView = [[UIView alloc] initWithFrame:self.view.bounds]; 377 | _swipingBackgoundView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]; 378 | } 379 | return _swipingBackgoundView; 380 | } 381 | 382 | -(NSMutableArray*)snapShotsArray{ 383 | if (!_snapShotsArray) { 384 | _snapShotsArray = [NSMutableArray array]; 385 | } 386 | return _snapShotsArray; 387 | } 388 | 389 | -(BOOL)isSwipingBack{ 390 | if (!_isSwipingBack) { 391 | _isSwipingBack = NO; 392 | } 393 | return _isSwipingBack; 394 | } 395 | 396 | -(UIPanGestureRecognizer*)swipePanGesture{ 397 | if (!_swipePanGesture) { 398 | _swipePanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipePanGestureHandler:)]; 399 | } 400 | return _swipePanGesture; 401 | } 402 | 403 | -(NJKWebViewProgress*)progressProxy{ 404 | if (!_progressProxy) { 405 | _progressProxy = [[NJKWebViewProgress alloc] init]; 406 | _progressProxy.webViewProxyDelegate = (id)self; 407 | _progressProxy.progressDelegate = (id)self; 408 | } 409 | return _progressProxy; 410 | } 411 | 412 | -(NJKWebViewProgressView*)progressView{ 413 | if (!_progressView) { 414 | CGFloat progressBarHeight = 3.0f; 415 | CGRect navigaitonBarBounds = self.navigationController.navigationBar.bounds; 416 | // CGRect barFrame = CGRectMake(0, navigaitonBarBounds.size.height - progressBarHeight-0.5, navigaitonBarBounds.size.width, progressBarHeight); 417 | CGRect barFrame = CGRectMake(0, navigaitonBarBounds.size.height, navigaitonBarBounds.size.width, progressBarHeight); 418 | _progressView = [[NJKWebViewProgressView alloc] initWithFrame:barFrame]; 419 | _progressView.progressColor = self.progressViewColor; 420 | _progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; 421 | } 422 | return _progressView; 423 | } 424 | 425 | @end -------------------------------------------------------------------------------- /RxWebViewController/RxWebViewNavigationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxWebViewNavigationViewController.h 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RxWebViewNavigationViewController : UINavigationController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RxWebViewController/RxWebViewNavigationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxWebViewNavigationViewController.m 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "RxWebViewNavigationViewController.h" 10 | #import "RxWebViewController.h" 11 | 12 | @interface RxWebViewNavigationViewController () 13 | 14 | /** 15 | * 由于 popViewController 会触发 shouldPopItems,因此用该布尔值记录是否应该正确 popItems 16 | */ 17 | @property BOOL shouldPopItemAfterPopViewController; 18 | 19 | @end 20 | 21 | @implementation RxWebViewNavigationViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | self.shouldPopItemAfterPopViewController = NO; 26 | // Do any additional setup after loading the view. 27 | } 28 | 29 | - (void)didReceiveMemoryWarning { 30 | [super didReceiveMemoryWarning]; 31 | // Dispose of any resources that can be recreated. 32 | } 33 | 34 | -(UIViewController*)popViewControllerAnimated:(BOOL)animated{ 35 | self.shouldPopItemAfterPopViewController = YES; 36 | return [super popViewControllerAnimated:animated]; 37 | } 38 | 39 | -(NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated{ 40 | self.shouldPopItemAfterPopViewController = YES; 41 | return [super popToViewController:viewController animated:animated]; 42 | } 43 | 44 | -(NSArray *)popToRootViewControllerAnimated:(BOOL)animated{ 45 | self.shouldPopItemAfterPopViewController = YES; 46 | return [super popToRootViewControllerAnimated:animated]; 47 | } 48 | -(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item{ 49 | 50 | //! 如果应该pop,说明是在 popViewController 之后,应该直接 popItems 51 | if (self.shouldPopItemAfterPopViewController) { 52 | self.shouldPopItemAfterPopViewController = NO; 53 | return YES; 54 | } 55 | 56 | //! 如果不应该 pop,说明是点击了导航栏的返回,这时候则要做出判断区分是不是在 webview 中 57 | if ([self.topViewController isKindOfClass:[RxWebViewController class]]) { 58 | RxWebViewController* webVC = (RxWebViewController*)self.viewControllers.lastObject; 59 | if (webVC.webView.canGoBack) { 60 | [webVC.webView goBack]; 61 | 62 | //!make sure the back indicator view alpha back to 1 63 | self.shouldPopItemAfterPopViewController = NO; 64 | [[self.navigationBar subviews] lastObject].alpha = 1; 65 | return NO; 66 | }else{ 67 | [self popViewControllerAnimated:YES]; 68 | return NO; 69 | } 70 | }else{ 71 | [self popViewControllerAnimated:YES]; 72 | return NO; 73 | } 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /RxWebViewController/backItemImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewController/backItemImage@2x.png -------------------------------------------------------------------------------- /RxWebViewController/backItemImage_hl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewController/backItemImage_hl@2x.png -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F53D31EC1BDB339D002322E1 /* Reveal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F53D31EB1BDB339D002322E1 /* Reveal.framework */; }; 11 | F59CDE9F1C1FBFB9008B5358 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE801C1FBFB9008B5358 /* AppDelegate.m */; }; 12 | F59CDEA01C1FBFB9008B5358 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE811C1FBFB9008B5358 /* Assets.xcassets */; }; 13 | F59CDEA11C1FBFB9008B5358 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE821C1FBFB9008B5358 /* LaunchScreen.storyboard */; }; 14 | F59CDEA21C1FBFB9008B5358 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE841C1FBFB9008B5358 /* Main.storyboard */; }; 15 | F59CDEA31C1FBFB9008B5358 /* demo.gif in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE861C1FBFB9008B5358 /* demo.gif */; }; 16 | F59CDEA41C1FBFB9008B5358 /* icon-140.png in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE871C1FBFB9008B5358 /* icon-140.png */; }; 17 | F59CDEA51C1FBFB9008B5358 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F59CDE881C1FBFB9008B5358 /* Info.plist */; }; 18 | F59CDEA61C1FBFB9008B5358 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE891C1FBFB9008B5358 /* main.m */; }; 19 | F59CDEA71C1FBFB9008B5358 /* myNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE8B1C1FBFB9008B5358 /* myNavigationViewController.m */; }; 20 | F59CDEA81C1FBFB9008B5358 /* RxLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */; }; 21 | F59CDEA91C1FBFB9008B5358 /* RxTextLinkTapView.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */; }; 22 | F59CDEB01C1FBFB9008B5358 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE9E1C1FBFB9008B5358 /* ViewController.m */; }; 23 | F59CDEB71C1FC06B008B5358 /* myNormalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEB51C1FC06B008B5358 /* myNormalViewController.m */; }; 24 | F59CDEB81C1FC06B008B5358 /* myNormalViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F59CDEB61C1FC06B008B5358 /* myNormalViewController.xib */; }; 25 | F59CDEC51C1FC6AE008B5358 /* backItemImage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */; }; 26 | F59CDEC61C1FC6AE008B5358 /* backItemImage_hl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */; }; 27 | F59CDEC71C1FC6AE008B5358 /* NJKWebViewProgress.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEBE1C1FC6AE008B5358 /* NJKWebViewProgress.m */; }; 28 | F59CDEC81C1FC6AE008B5358 /* NJKWebViewProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEC01C1FC6AE008B5358 /* NJKWebViewProgressView.m */; }; 29 | F59CDEC91C1FC6AE008B5358 /* RxWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */; }; 30 | F59CDECA1C1FC6AE008B5358 /* RxWebViewNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEC41C1FC6AE008B5358 /* RxWebViewNavigationViewController.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxWebViewControllerTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | F53D31EB1BDB339D002322E1 /* Reveal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Reveal.framework; sourceTree = ""; }; 36 | F59CDE7F1C1FBFB9008B5358 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | F59CDE801C1FBFB9008B5358 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | F59CDE811C1FBFB9008B5358 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 39 | F59CDE831C1FBFB9008B5358 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 40 | F59CDE851C1FBFB9008B5358 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | F59CDE861C1FBFB9008B5358 /* demo.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = demo.gif; sourceTree = ""; }; 42 | F59CDE871C1FBFB9008B5358 /* icon-140.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-140.png"; sourceTree = ""; }; 43 | F59CDE881C1FBFB9008B5358 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | F59CDE891C1FBFB9008B5358 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | F59CDE8A1C1FBFB9008B5358 /* myNavigationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = myNavigationViewController.h; sourceTree = ""; }; 46 | F59CDE8B1C1FBFB9008B5358 /* myNavigationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = myNavigationViewController.m; sourceTree = ""; }; 47 | F59CDE8D1C1FBFB9008B5358 /* RxLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxLabel.h; sourceTree = ""; }; 48 | F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLabel.m; sourceTree = ""; }; 49 | F59CDE8F1C1FBFB9008B5358 /* RxTextLinkTapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxTextLinkTapView.h; sourceTree = ""; }; 50 | F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxTextLinkTapView.m; sourceTree = ""; }; 51 | F59CDE9D1C1FBFB9008B5358 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 52 | F59CDE9E1C1FBFB9008B5358 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 53 | F59CDEB41C1FC06B008B5358 /* myNormalViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = myNormalViewController.h; sourceTree = ""; }; 54 | F59CDEB51C1FC06B008B5358 /* myNormalViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = myNormalViewController.m; sourceTree = ""; }; 55 | F59CDEB61C1FC06B008B5358 /* myNormalViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = myNormalViewController.xib; sourceTree = ""; }; 56 | F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backItemImage@2x.png"; sourceTree = ""; }; 57 | F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backItemImage_hl@2x.png"; sourceTree = ""; }; 58 | F59CDEBD1C1FC6AE008B5358 /* NJKWebViewProgress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJKWebViewProgress.h; sourceTree = ""; }; 59 | F59CDEBE1C1FC6AE008B5358 /* NJKWebViewProgress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJKWebViewProgress.m; sourceTree = ""; }; 60 | F59CDEBF1C1FC6AE008B5358 /* NJKWebViewProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJKWebViewProgressView.h; sourceTree = ""; }; 61 | F59CDEC01C1FC6AE008B5358 /* NJKWebViewProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJKWebViewProgressView.m; sourceTree = ""; }; 62 | F59CDEC11C1FC6AE008B5358 /* RxWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxWebViewController.h; sourceTree = ""; }; 63 | F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxWebViewController.m; sourceTree = ""; }; 64 | F59CDEC31C1FC6AE008B5358 /* RxWebViewNavigationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxWebViewNavigationViewController.h; sourceTree = ""; }; 65 | F59CDEC41C1FC6AE008B5358 /* RxWebViewNavigationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxWebViewNavigationViewController.m; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | F53D31961BDA4FCA002322E1 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | F53D31EC1BDB339D002322E1 /* Reveal.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | F53D31901BDA4FCA002322E1 = { 81 | isa = PBXGroup; 82 | children = ( 83 | F53D31EB1BDB339D002322E1 /* Reveal.framework */, 84 | F59CDEB91C1FC6AE008B5358 /* RxWebViewController */, 85 | F59CDE7E1C1FBFB9008B5358 /* RxWebViewControllerTest */, 86 | F53D319A1BDA4FCA002322E1 /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | F53D319A1BDA4FCA002322E1 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */, 94 | ); 95 | name = Products; 96 | sourceTree = ""; 97 | }; 98 | F59CDE7E1C1FBFB9008B5358 /* RxWebViewControllerTest */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | F59CDE8A1C1FBFB9008B5358 /* myNavigationViewController.h */, 102 | F59CDE8B1C1FBFB9008B5358 /* myNavigationViewController.m */, 103 | F59CDEB41C1FC06B008B5358 /* myNormalViewController.h */, 104 | F59CDEB51C1FC06B008B5358 /* myNormalViewController.m */, 105 | F59CDEB61C1FC06B008B5358 /* myNormalViewController.xib */, 106 | F59CDE7F1C1FBFB9008B5358 /* AppDelegate.h */, 107 | F59CDE801C1FBFB9008B5358 /* AppDelegate.m */, 108 | F59CDE9D1C1FBFB9008B5358 /* ViewController.h */, 109 | F59CDE9E1C1FBFB9008B5358 /* ViewController.m */, 110 | F59CDE811C1FBFB9008B5358 /* Assets.xcassets */, 111 | F59CDE821C1FBFB9008B5358 /* LaunchScreen.storyboard */, 112 | F59CDE841C1FBFB9008B5358 /* Main.storyboard */, 113 | F59CDE861C1FBFB9008B5358 /* demo.gif */, 114 | F59CDE871C1FBFB9008B5358 /* icon-140.png */, 115 | F59CDE881C1FBFB9008B5358 /* Info.plist */, 116 | F59CDE891C1FBFB9008B5358 /* main.m */, 117 | F59CDE8C1C1FBFB9008B5358 /* RxLabel */, 118 | ); 119 | path = RxWebViewControllerTest; 120 | sourceTree = ""; 121 | }; 122 | F59CDE8C1C1FBFB9008B5358 /* RxLabel */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | F59CDE8D1C1FBFB9008B5358 /* RxLabel.h */, 126 | F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */, 127 | F59CDE8F1C1FBFB9008B5358 /* RxTextLinkTapView.h */, 128 | F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */, 129 | ); 130 | path = RxLabel; 131 | sourceTree = ""; 132 | }; 133 | F59CDEB91C1FC6AE008B5358 /* RxWebViewController */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */, 137 | F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */, 138 | F59CDEBC1C1FC6AE008B5358 /* NJKWebViewProgress */, 139 | F59CDEC11C1FC6AE008B5358 /* RxWebViewController.h */, 140 | F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */, 141 | F59CDEC31C1FC6AE008B5358 /* RxWebViewNavigationViewController.h */, 142 | F59CDEC41C1FC6AE008B5358 /* RxWebViewNavigationViewController.m */, 143 | ); 144 | path = RxWebViewController; 145 | sourceTree = ""; 146 | }; 147 | F59CDEBC1C1FC6AE008B5358 /* NJKWebViewProgress */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | F59CDEBD1C1FC6AE008B5358 /* NJKWebViewProgress.h */, 151 | F59CDEBE1C1FC6AE008B5358 /* NJKWebViewProgress.m */, 152 | F59CDEBF1C1FC6AE008B5358 /* NJKWebViewProgressView.h */, 153 | F59CDEC01C1FC6AE008B5358 /* NJKWebViewProgressView.m */, 154 | ); 155 | path = NJKWebViewProgress; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXNativeTarget section */ 161 | F53D31981BDA4FCA002322E1 /* RxWebViewControllerTest */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = F53D31C61BDA4FCB002322E1 /* Build configuration list for PBXNativeTarget "RxWebViewControllerTest" */; 164 | buildPhases = ( 165 | F53D31951BDA4FCA002322E1 /* Sources */, 166 | F53D31961BDA4FCA002322E1 /* Frameworks */, 167 | F53D31971BDA4FCA002322E1 /* Resources */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | ); 173 | name = RxWebViewControllerTest; 174 | productName = RxWebViewController; 175 | productReference = F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */; 176 | productType = "com.apple.product-type.application"; 177 | }; 178 | /* End PBXNativeTarget section */ 179 | 180 | /* Begin PBXProject section */ 181 | F53D31911BDA4FCA002322E1 /* Project object */ = { 182 | isa = PBXProject; 183 | attributes = { 184 | LastUpgradeCheck = 0700; 185 | ORGANIZATIONNAME = roxasora; 186 | TargetAttributes = { 187 | F53D31981BDA4FCA002322E1 = { 188 | CreatedOnToolsVersion = 7.0.1; 189 | }; 190 | }; 191 | }; 192 | buildConfigurationList = F53D31941BDA4FCA002322E1 /* Build configuration list for PBXProject "RxWebViewControllerTest" */; 193 | compatibilityVersion = "Xcode 3.2"; 194 | developmentRegion = English; 195 | hasScannedForEncodings = 0; 196 | knownRegions = ( 197 | en, 198 | Base, 199 | ); 200 | mainGroup = F53D31901BDA4FCA002322E1; 201 | productRefGroup = F53D319A1BDA4FCA002322E1 /* Products */; 202 | projectDirPath = ""; 203 | projectRoot = ""; 204 | targets = ( 205 | F53D31981BDA4FCA002322E1 /* RxWebViewControllerTest */, 206 | ); 207 | }; 208 | /* End PBXProject section */ 209 | 210 | /* Begin PBXResourcesBuildPhase section */ 211 | F53D31971BDA4FCA002322E1 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | F59CDEA11C1FBFB9008B5358 /* LaunchScreen.storyboard in Resources */, 216 | F59CDEA01C1FBFB9008B5358 /* Assets.xcassets in Resources */, 217 | F59CDEA41C1FBFB9008B5358 /* icon-140.png in Resources */, 218 | F59CDEA31C1FBFB9008B5358 /* demo.gif in Resources */, 219 | F59CDEC51C1FC6AE008B5358 /* backItemImage@2x.png in Resources */, 220 | F59CDEB81C1FC06B008B5358 /* myNormalViewController.xib in Resources */, 221 | F59CDEC61C1FC6AE008B5358 /* backItemImage_hl@2x.png in Resources */, 222 | F59CDEA21C1FBFB9008B5358 /* Main.storyboard in Resources */, 223 | F59CDEA51C1FBFB9008B5358 /* Info.plist in Resources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXResourcesBuildPhase section */ 228 | 229 | /* Begin PBXSourcesBuildPhase section */ 230 | F53D31951BDA4FCA002322E1 /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | F59CDEC71C1FC6AE008B5358 /* NJKWebViewProgress.m in Sources */, 235 | F59CDEA61C1FBFB9008B5358 /* main.m in Sources */, 236 | F59CDE9F1C1FBFB9008B5358 /* AppDelegate.m in Sources */, 237 | F59CDEA81C1FBFB9008B5358 /* RxLabel.m in Sources */, 238 | F59CDEB01C1FBFB9008B5358 /* ViewController.m in Sources */, 239 | F59CDEA71C1FBFB9008B5358 /* myNavigationViewController.m in Sources */, 240 | F59CDECA1C1FC6AE008B5358 /* RxWebViewNavigationViewController.m in Sources */, 241 | F59CDEB71C1FC06B008B5358 /* myNormalViewController.m in Sources */, 242 | F59CDEC81C1FC6AE008B5358 /* NJKWebViewProgressView.m in Sources */, 243 | F59CDEA91C1FBFB9008B5358 /* RxTextLinkTapView.m in Sources */, 244 | F59CDEC91C1FC6AE008B5358 /* RxWebViewController.m in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXVariantGroup section */ 251 | F59CDE821C1FBFB9008B5358 /* LaunchScreen.storyboard */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | F59CDE831C1FBFB9008B5358 /* Base */, 255 | ); 256 | name = LaunchScreen.storyboard; 257 | sourceTree = ""; 258 | }; 259 | F59CDE841C1FBFB9008B5358 /* Main.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | F59CDE851C1FBFB9008B5358 /* Base */, 263 | ); 264 | name = Main.storyboard; 265 | sourceTree = ""; 266 | }; 267 | /* End PBXVariantGroup section */ 268 | 269 | /* Begin XCBuildConfiguration section */ 270 | F53D31C41BDA4FCB002322E1 /* Debug */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ALWAYS_SEARCH_USER_PATHS = NO; 274 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 275 | CLANG_CXX_LIBRARY = "libc++"; 276 | CLANG_ENABLE_MODULES = YES; 277 | CLANG_ENABLE_OBJC_ARC = YES; 278 | CLANG_WARN_BOOL_CONVERSION = YES; 279 | CLANG_WARN_CONSTANT_CONVERSION = YES; 280 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 307 | MTL_ENABLE_DEBUG_INFO = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | TARGETED_DEVICE_FAMILY = "1,2"; 311 | }; 312 | name = Debug; 313 | }; 314 | F53D31C51BDA4FCB002322E1 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 319 | CLANG_CXX_LIBRARY = "libc++"; 320 | CLANG_ENABLE_MODULES = YES; 321 | CLANG_ENABLE_OBJC_ARC = YES; 322 | CLANG_WARN_BOOL_CONVERSION = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | SDKROOT = iphoneos; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | VALIDATE_PRODUCT = YES; 349 | }; 350 | name = Release; 351 | }; 352 | F53D31C71BDA4FCB002322E1 /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | ENABLE_BITCODE = NO; 357 | FRAMEWORK_SEARCH_PATHS = ( 358 | "$(inherited)", 359 | "$(PROJECT_DIR)", 360 | ); 361 | INFOPLIST_FILE = "$(SRCROOT)/RxWebViewControllerTest/Info.plist"; 362 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 363 | OTHER_LDFLAGS = ( 364 | "-ObjC", 365 | "-lz", 366 | "-framework", 367 | Reveal, 368 | ); 369 | PRODUCT_BUNDLE_IDENTIFIER = com.roxasora.RxWebViewController; 370 | PRODUCT_NAME = RxWebViewControllerTest; 371 | }; 372 | name = Debug; 373 | }; 374 | F53D31C81BDA4FCB002322E1 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 378 | ENABLE_BITCODE = NO; 379 | FRAMEWORK_SEARCH_PATHS = ( 380 | "$(inherited)", 381 | "$(PROJECT_DIR)", 382 | ); 383 | INFOPLIST_FILE = "$(SRCROOT)/RxWebViewControllerTest/Info.plist"; 384 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 385 | OTHER_LDFLAGS = ( 386 | "-ObjC", 387 | "-lz", 388 | "-framework", 389 | Reveal, 390 | ); 391 | PRODUCT_BUNDLE_IDENTIFIER = com.roxasora.RxWebViewController; 392 | PRODUCT_NAME = RxWebViewControllerTest; 393 | }; 394 | name = Release; 395 | }; 396 | /* End XCBuildConfiguration section */ 397 | 398 | /* Begin XCConfigurationList section */ 399 | F53D31941BDA4FCA002322E1 /* Build configuration list for PBXProject "RxWebViewControllerTest" */ = { 400 | isa = XCConfigurationList; 401 | buildConfigurations = ( 402 | F53D31C41BDA4FCB002322E1 /* Debug */, 403 | F53D31C51BDA4FCB002322E1 /* Release */, 404 | ); 405 | defaultConfigurationIsVisible = 0; 406 | defaultConfigurationName = Release; 407 | }; 408 | F53D31C61BDA4FCB002322E1 /* Build configuration list for PBXNativeTarget "RxWebViewControllerTest" */ = { 409 | isa = XCConfigurationList; 410 | buildConfigurations = ( 411 | F53D31C71BDA4FCB002322E1 /* Debug */, 412 | F53D31C81BDA4FCB002322E1 /* Release */, 413 | ); 414 | defaultConfigurationIsVisible = 0; 415 | defaultConfigurationName = Release; 416 | }; 417 | /* End XCConfigurationList section */ 418 | }; 419 | rootObject = F53D31911BDA4FCA002322E1 /* Project object */; 420 | } 421 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/project.xcworkspace/xcuserdata/roxasora.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest.xcodeproj/project.xcworkspace/xcuserdata/roxasora.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/roxasora.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/roxasora.xcuserdatad/xcschemes/RxWebViewController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/roxasora.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RxWebViewController.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F53D31981BDA4FCA002322E1 16 | 17 | primary 18 | 19 | 20 | F53D31B11BDA4FCB002322E1 21 | 22 | primary 23 | 24 | 25 | F53D31BC1BDA4FCB002322E1 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/zzy.xcuserdatad/xcschemes/RxWebViewControllerTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/zzy.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RxWebViewControllerTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F53D31981BDA4FCA002322E1 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. 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 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "3x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "3x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "57x57", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "57x57", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "size" : "60x60", 40 | "idiom" : "iphone", 41 | "filename" : "icon-120.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "size" : "60x60", 46 | "idiom" : "iphone", 47 | "filename" : "icon-121.png", 48 | "scale" : "3x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "29x29", 53 | "scale" : "1x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "2x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "40x40", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "50x50", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "50x50", 78 | "scale" : "2x" 79 | }, 80 | { 81 | "idiom" : "ipad", 82 | "size" : "72x72", 83 | "scale" : "1x" 84 | }, 85 | { 86 | "idiom" : "ipad", 87 | "size" : "72x72", 88 | "scale" : "2x" 89 | }, 90 | { 91 | "idiom" : "ipad", 92 | "size" : "76x76", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "idiom" : "ipad", 97 | "size" : "76x76", 98 | "scale" : "2x" 99 | } 100 | ], 101 | "info" : { 102 | "version" : 1, 103 | "author" : "xcode" 104 | } 105 | } -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-120.png -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-121.png -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/icon-nav-backButton-bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "resizing" : { 9 | "mode" : "3-part-horizontal", 10 | "center" : { 11 | "mode" : "tile", 12 | "width" : 1 13 | }, 14 | "cap-insets" : { 15 | "right" : 0, 16 | "left" : 33 17 | } 18 | }, 19 | "idiom" : "universal", 20 | "filename" : "icon-nav-backButton-bg.png", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "universal", 25 | "scale" : "3x" 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | }, 32 | "properties" : { 33 | "template-rendering-intent" : "template" 34 | } 35 | } -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/icon-nav-backButton-bg.imageset/icon-nav-backButton-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest/Assets.xcassets/icon-nav-backButton-bg.imageset/icon-nav-backButton-bg.png -------------------------------------------------------------------------------- /RxWebViewControllerTest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UIStatusBarStyle 39 | UIStatusBarStyleLightContent 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/RxLabel/RxLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxLabel.h 3 | // coreTextDemo 4 | // 5 | // Created by roxasora on 15/10/8. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol RxLabelDelegate; 12 | 13 | @interface RxLabel : UIView 14 | 15 | @property id delegate; 16 | 17 | 18 | /** 19 | * text 20 | */ 21 | @property (nonatomic,copy)NSString* text; 22 | 23 | /** 24 | * textColor default is #333333 25 | */ 26 | @property (nonatomic,strong)UIColor* textColor; 27 | 28 | /** 29 | * text alignment with NSTextAlignment 30 | */ 31 | @property (nonatomic) NSTextAlignment textAlignment; 32 | 33 | /** 34 | * font default is 16 35 | */ 36 | @property (nonatomic,strong)UIFont* font; 37 | 38 | /** 39 | * linespacing default is 0 40 | */ 41 | @property (nonatomic)NSInteger linespacing; 42 | 43 | /** 44 | * color of link button,default is custom blue 45 | */ 46 | @property (nonatomic)UIColor* linkButtonColor; 47 | 48 | 49 | /** 50 | * custom the color array of your own urls, like orange taobao, red tmall, and green douban 51 | @[ 52 | @{ 53 | @"scheme":@"taobao", 54 | @"title":@"淘宝", 55 | @"color":@0Xff0000 56 | } 57 | ] 58 | */ 59 | @property (nonatomic,copy)NSArray* customUrlArray; 60 | 61 | //** 62 | // * add custom url button with your own config 63 | // * 64 | // * @param scheme scheme 65 | // * @param title title to display 66 | // * @param backgroundColor bgcolor 67 | // */ 68 | //-(void)addCustomUrlButtonWithScheme:(NSString*)scheme title:(NSString*)title backgroundColor:(UIColor*)backgroundColor; 69 | 70 | /** 71 | * get height with text width font and spacing 72 | * 73 | * @param text text 74 | * @param width width 75 | * @param font font 76 | * @param linespacing spacing 77 | * 78 | * @return float height 79 | */ 80 | 81 | /** 82 | * fit the best size 83 | */ 84 | -(void)sizeToFit; 85 | 86 | +(CGFloat)heightForText:(NSString*)text width:(CGFloat)width font:(UIFont*)font linespacing:(CGFloat)linespacing; 87 | +(void)filtUrlWithOriginText:(NSString*)originText urlArray:(NSMutableArray*)urlArray filteredText:(NSString**)filterText; 88 | 89 | @end 90 | 91 | @protocol RxLabelDelegate 92 | 93 | -(void)RxLabel:(RxLabel*)label didDetectedTapLinkWithUrlStr:(NSString*)urlStr; 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/RxLabel/RxLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxLabel.m 3 | // coreTextDemo 4 | // 5 | // Created by roxasora on 15/10/8. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "RxLabel.h" 10 | #import 11 | #import "RxTextLinkTapView.h" 12 | 13 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 14 | 15 | #define RxUrlRegular @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)" 16 | #define RxTopicRegular @"#[^#]+#" 17 | 18 | #define rxHighlightTextTypeUrl @"url" 19 | 20 | #define subviewsTag_linkTapViews -333 21 | #define lineHeight_correction 3 //correct the line height 22 | 23 | @interface RxLabel () 24 | 25 | @end 26 | 27 | @implementation RxLabel 28 | 29 | -(instancetype)initWithCoder:(NSCoder *)aDecoder{ 30 | self = [super initWithCoder:aDecoder]; 31 | if (self) { 32 | [self initProperties]; 33 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setNeedsDisplay) name:UIDeviceOrientationDidChangeNotification object:nil]; 34 | } 35 | return self; 36 | } 37 | 38 | -(id)initWithFrame:(CGRect)frame{ 39 | self = [super initWithFrame:frame]; 40 | if (self) { 41 | [self initProperties]; 42 | } 43 | return self; 44 | } 45 | 46 | -(void)initProperties{ 47 | _font = [UIFont systemFontOfSize:16]; 48 | _textColor = UIColorFromRGB(0X333333); 49 | _linkButtonColor = UIColorFromRGB(0X2081ef); 50 | _linespacing = 0; 51 | _textAlignment = NSTextAlignmentLeft; 52 | 53 | // self.backgroundColor = [UIColor clearColor]; 54 | } 55 | 56 | -(void)setFrame:(CGRect)frame{ 57 | [super setFrame:frame]; 58 | [self setNeedsDisplay]; 59 | } 60 | 61 | -(void)setText:(NSString *)text{ 62 | _text = text; 63 | // [self drawRect:self.bounds]; 64 | [self setNeedsDisplay]; 65 | } 66 | 67 | -(void)setFont:(UIFont *)font{ 68 | _font = font; 69 | [self setNeedsDisplay]; 70 | } 71 | 72 | -(void)setTextColor:(UIColor *)textColor{ 73 | _textColor = textColor; 74 | [self setNeedsDisplay]; 75 | } 76 | 77 | -(void)setTextAlignment:(NSTextAlignment)textAlignment{ 78 | _textAlignment = textAlignment; 79 | [self setNeedsDisplay]; 80 | } 81 | 82 | -(void)setLinkButtonColor:(UIColor *)linkButtonColor{ 83 | _linkButtonColor = linkButtonColor; 84 | for (UIView* subview in self.subviews) { 85 | if (subview.tag == NSIntegerMin) { 86 | RxTextLinkTapView* buttonView = nil; 87 | buttonView = (RxTextLinkTapView*)subview; 88 | if (buttonView.type == RxTextLinkTapViewTypeDefault) { 89 | buttonView.backgroundColor = linkButtonColor; 90 | } 91 | } 92 | } 93 | } 94 | 95 | -(void)setlinespacing:(NSInteger)linespacing{ 96 | _linespacing = linespacing; 97 | [self setNeedsDisplay]; 98 | } 99 | 100 | -(void)setCustomUrlArray:(NSArray*)customUrlArray{ 101 | _customUrlArray = customUrlArray; 102 | [self setNeedsDisplay]; 103 | } 104 | 105 | static CTTextAlignment CTTextAlignmentFromNSTextAlignment(NSTextAlignment alignment){ 106 | switch (alignment) { 107 | case NSTextAlignmentCenter: return kCTCenterTextAlignment; 108 | case NSTextAlignmentLeft: return kCTLeftTextAlignment; 109 | case NSTextAlignmentRight: return kCTRightTextAlignment; 110 | default: return kCTNaturalTextAlignment; 111 | } 112 | } 113 | 114 | 115 | #pragma mark - url replace run delegate 116 | static CGFloat ascentCallback(void *ref){ 117 | //!the height must fit the fontsize of titleView 118 | return [(__bridge UIFont*)ref pointSize] + 2; 119 | return [(NSNumber*)[(__bridge NSDictionary*)ref objectForKey:@"height"] floatValue]; 120 | } 121 | 122 | static CGFloat descentCallback(void *ref){ 123 | return 0; 124 | } 125 | 126 | static CGFloat widthCallback(void* ref){ 127 | return 70.0; 128 | return [(NSNumber*)[(__bridge NSDictionary*)ref objectForKey:@"width"] floatValue]; 129 | } 130 | 131 | #pragma mark - draw rect 132 | // Only override drawRect: if you perform custom drawing. 133 | // An empty implementation adversely affects performance during animation. 134 | - (void)drawRect:(CGRect)rect { 135 | // Drawing code 136 | [super drawRect:rect]; 137 | 138 | CGContextRef context = UIGraphicsGetCurrentContext(); 139 | CGContextClearRect(context, self.bounds); 140 | 141 | if (self.backgroundColor) { 142 | CGContextSaveGState(context); 143 | CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); 144 | CGContextFillRect(context, self.bounds); 145 | CGContextRestoreGState(context); 146 | } 147 | 148 | //translate the coordinate system to normal 149 | CGContextSetTextMatrix(context, CGAffineTransformIdentity); 150 | CGContextTranslateCTM(context, 0, self.bounds.size.height); 151 | CGContextScaleCTM(context, 1.0, -1.0); 152 | 153 | //create the draw path 154 | CGMutablePathRef path = CGPathCreateMutable(); 155 | 156 | //挪动path的bound,避免(ಥ_ಥ) 这样的符号会画不出来 157 | //move the bound of path,or some words like (ಥ_ಥ) won't be drawn 158 | CGRect pathRect = self.bounds; 159 | pathRect.size.height += 5; 160 | pathRect.origin.y -= 5; 161 | CGPathAddRect(path, NULL, pathRect); 162 | 163 | //set line height font color and break mode 164 | CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)self.font.fontName, self.font.pointSize, NULL); 165 | // CGFloat minLineHeight = self.font.pointSize + lineHeight_correction, 166 | // maxLineHeight = minLineHeight, 167 | CGFloat linespacing = self.linespacing; 168 | 169 | CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping; 170 | CTTextAlignment alignment = CTTextAlignmentFromNSTextAlignment(self.textAlignment); 171 | 172 | CTParagraphStyleRef style = CTParagraphStyleCreate((CTParagraphStyleSetting[4]){ 173 | {kCTParagraphStyleSpecifierAlignment,sizeof(alignment),&alignment}, 174 | // {kCTParagraphStyleSpecifierMinimumLineHeight,sizeof(minLineHeight),&minLineHeight}, 175 | // {kCTParagraphStyleSpecifierMaximumLineHeight,sizeof(maxLineHeight),&maxLineHeight}, 176 | {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(linespacing),&linespacing}, 177 | {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(linespacing),&linespacing}, 178 | {kCTParagraphStyleSpecifierLineBreakMode,sizeof(lineBreakMode),&lineBreakMode} 179 | }, 4); 180 | 181 | NSDictionary* initAttrbutes = @{ 182 | (NSString*)kCTFontAttributeName: (__bridge id)fontRef, 183 | (NSString*)kCTForegroundColorAttributeName:(id)self.textColor.CGColor, 184 | (NSString*)kCTParagraphStyleAttributeName:(id)style 185 | }; 186 | 187 | //先从self text 中过滤掉 url ,将其保存在array中 188 | //filter the url string from origin text and generate the urlArray and the filtered text string 189 | /** 190 | @[ 191 | @{ 192 | @"range":@(m,n), 193 | @"urlStr":@"http://dsadd" 194 | } 195 | ] 196 | */ 197 | NSMutableArray* urlArray = [NSMutableArray array]; 198 | NSString* filteredText = [[NSString alloc] init]; 199 | [RxLabel filtUrlWithOriginText:self.text urlArray:urlArray filteredText:&filteredText]; 200 | 201 | //init the attributed string 202 | NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:filteredText 203 | attributes:initAttrbutes]; 204 | //add url replaced run one by one with urlArray 205 | for (NSDictionary* urlItem in urlArray) { 206 | //init run callbacks 207 | CTRunDelegateCallbacks callbacks; 208 | memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks)); 209 | callbacks.version = kCTRunDelegateVersion1; 210 | callbacks.getAscent = ascentCallback; 211 | callbacks.getDescent = descentCallback; 212 | callbacks.getWidth = widthCallback; 213 | CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void*)(self.font)); 214 | 215 | NSRange range = [[urlItem objectForKey:@"range"] rangeValue]; 216 | NSString* urlStr = [urlItem objectForKey:@"urlStr"]; 217 | CFAttributedStringSetAttributes((CFMutableAttributedStringRef)attrStr, CFRangeMake(range.location, range.length), (CFDictionaryRef)@{ 218 | (NSString*)kCTRunDelegateAttributeName:(__bridge id)delegate, 219 | @"url":urlStr 220 | }, NO); 221 | CFRelease(delegate); 222 | } 223 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr); 224 | CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attrStr.length), path, NULL); 225 | 226 | //clear link tap views and create new link tap view 227 | for (UIView* subview in self.subviews) { 228 | if (subview.tag == NSIntegerMin) { 229 | [subview removeFromSuperview]; 230 | } 231 | } 232 | 233 | //get lines in frame 234 | NSArray* lines = (NSArray*)CTFrameGetLines(frame); 235 | CFIndex lineCount = [lines count]; 236 | 237 | //get origin point of each line 238 | CGPoint origins[lineCount]; 239 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 240 | 241 | for (CFIndex index = 0; index < lineCount; index++) { 242 | //get line ref of line 243 | CTLineRef line = CFArrayGetValueAtIndex((CFArrayRef)lines, index); 244 | 245 | //get run 246 | CFArrayRef glyphRuns = CTLineGetGlyphRuns(line); 247 | CFIndex glyphCount = CFArrayGetCount(glyphRuns); 248 | for (int i = 0; i < glyphCount; i++) { 249 | CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i); 250 | 251 | NSDictionary* attrbutes = (NSDictionary*)CTRunGetAttributes(run); 252 | //create hover frame 253 | if ([attrbutes objectForKey:@"url"]) { 254 | 255 | CGRect runBounds; 256 | 257 | CGFloat ascent; 258 | CGFloat descent; 259 | runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL); 260 | runBounds.size.height = ascent + descent; 261 | 262 | //!make sure you've add the origin of the line, or your alignment will not work on url replace runs 263 | runBounds.origin.x = origins[index].x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL); 264 | runBounds.origin.y = self.frame.size.height - origins[index].y - runBounds.size.height; 265 | 266 | //加上之前给 path 挪动位置时的修正 267 | //add correction of move the path 268 | runBounds.origin.y += 5; 269 | 270 | #ifdef RXDEBUG 271 | UIView* randomView = [[UIView alloc] initWithFrame:runBounds]; 272 | randomView.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:0.2]; 273 | [self addSubview:randomView]; 274 | #endif 275 | 276 | NSString* urlStr = attrbutes[@"url"]; 277 | RxTextLinkTapView* linkButtonView = [self linkButtonViewWithFrame:runBounds UrlStr:urlStr]; 278 | [self addSubview:linkButtonView]; 279 | } 280 | } 281 | } 282 | 283 | CTFrameDraw(frame, context); 284 | 285 | CFRelease(frame); 286 | CFRelease(frameSetter); 287 | CFRelease(path); 288 | } 289 | 290 | -(void)sizeToFit{ 291 | [super sizeToFit]; 292 | CGFloat height = [RxLabel heightForText:self.text width:self.bounds.size.width font:self.font linespacing:self.linespacing]; 293 | CGRect frame = self.frame; 294 | frame.size.height = height; 295 | self.frame = frame; 296 | } 297 | 298 | #pragma mark create link replace button with url 299 | -(RxTextLinkTapView*)linkButtonViewWithFrame:(CGRect)frame UrlStr:(NSString*)urlStr{ 300 | RxTextLinkTapView* buttonView = [[RxTextLinkTapView alloc] initWithFrame:frame 301 | urlStr:urlStr 302 | font:self.font 303 | linespacing:self.linespacing]; 304 | buttonView.tag = NSIntegerMin; 305 | buttonView.backgroundColor = self.linkButtonColor; 306 | buttonView.title = @"网页"; 307 | buttonView.delegate = self; 308 | 309 | //handle custom url array 310 | for (NSDictionary* item in self.customUrlArray) { 311 | NSString* scheme = item[@"scheme"]; 312 | //when match 313 | if ([urlStr rangeOfString:scheme].location != NSNotFound) { 314 | buttonView.type = RxTextLinkTapViewTypeCustom; 315 | buttonView.backgroundColor = UIColorFromRGB([item[@"color"] integerValue]); 316 | buttonView.title = item[@"title"]; 317 | } 318 | } 319 | 320 | return buttonView; 321 | } 322 | 323 | #pragma mark - filter url and generate display text and url array 324 | +(void)filtUrlWithOriginText:(NSString *)originText urlArray:(NSMutableArray *)urlArray filteredText:(NSString *__autoreleasing *)filterText{ 325 | *filterText = [NSString stringWithString:originText]; 326 | NSArray* urlMatches = [[NSRegularExpression regularExpressionWithPattern:RxUrlRegular 327 | options:NSRegularExpressionDotMatchesLineSeparators error:nil] 328 | matchesInString:originText 329 | options:0 330 | range:NSMakeRange(0, originText.length)]; 331 | 332 | //range 的偏移量,每次replace之后,下次循环中,要加上这个偏移量 333 | // NSLog(@"origin text %@ matched%@",originText,urlMatches); 334 | NSInteger rangeOffset = 0; 335 | for (NSTextCheckingResult* match in urlMatches) { 336 | NSRange range = match.range; 337 | NSString* urlStr = [originText substringWithRange:range]; 338 | 339 | range.location += rangeOffset; 340 | rangeOffset -= (range.length - 1); 341 | 342 | unichar objectReplacementChar = 0xFFFC; 343 | NSString * replaceContent = [NSString stringWithCharacters:&objectReplacementChar length:1]; 344 | *filterText = [*filterText stringByReplacingCharactersInRange:range withString:replaceContent]; 345 | 346 | range.length = 1; 347 | [urlArray addObject:@{ 348 | @"range":[NSValue valueWithRange:range], 349 | @"urlStr":urlStr 350 | }]; 351 | } 352 | } 353 | 354 | #pragma mark - get height for particular configs 355 | +(CGFloat)heightForText:(NSString *)text width:(CGFloat)width font:(UIFont *)font linespacing:(CGFloat)linespacing{ 356 | CGFloat height = 0; 357 | 358 | CGMutablePathRef path = CGPathCreateMutable(); 359 | CGPathAddRect(path, NULL, CGRectMake(0, 0, width, 9999)); 360 | 361 | //set line height font color and break mode 362 | CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); 363 | 364 | // CGFloat minLineHeight = font.pointSize + lineHeight_correction, 365 | // maxLineHeight = minLineHeight; 366 | 367 | CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping; 368 | CTTextAlignment alignment = kCTLeftTextAlignment; 369 | 370 | CTParagraphStyleRef style = CTParagraphStyleCreate((CTParagraphStyleSetting[4]){ 371 | {kCTParagraphStyleSpecifierAlignment,sizeof(alignment),&alignment}, 372 | // {kCTParagraphStyleSpecifierMinimumLineHeight,sizeof(minLineHeight),&minLineHeight}, 373 | // {kCTParagraphStyleSpecifierMaximumLineHeight,sizeof(maxLineHeight),&maxLineHeight}, 374 | {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(linespacing),&linespacing}, 375 | {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(linespacing),&linespacing}, 376 | {kCTParagraphStyleSpecifierLineBreakMode,sizeof(lineBreakMode),&lineBreakMode} 377 | }, 4); 378 | 379 | 380 | NSDictionary* initAttrbutes = @{ 381 | (NSString*)kCTFontAttributeName: (__bridge id)fontRef, 382 | (NSString*)kCTParagraphStyleAttributeName:(id)style 383 | }; 384 | 385 | NSMutableArray* urlArray = [NSMutableArray array]; 386 | NSString* filteredText = [[NSString alloc] init]; 387 | [RxLabel filtUrlWithOriginText:text urlArray:urlArray filteredText:&filteredText]; 388 | 389 | //create the initial attributed string 390 | NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:filteredText 391 | attributes:initAttrbutes]; 392 | for (NSDictionary* urlItem in urlArray) { 393 | //init run callbacks 394 | CTRunDelegateCallbacks callbacks; 395 | memset(&callbacks, 0, sizeof(CTRunDelegateCallbacks)); 396 | callbacks.version = kCTRunDelegateVersion1; 397 | callbacks.getAscent = ascentCallback; 398 | callbacks.getDescent = descentCallback; 399 | callbacks.getWidth = widthCallback; 400 | CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void*)(font)); 401 | 402 | NSRange range = [[urlItem objectForKey:@"range"] rangeValue]; 403 | NSString* urlStr = [urlItem objectForKey:@"urlStr"]; 404 | CFAttributedStringSetAttributes((CFMutableAttributedStringRef)attrStr, CFRangeMake(range.location, range.length), (CFDictionaryRef)@{ 405 | (NSString*)kCTRunDelegateAttributeName:(__bridge id)delegate, 406 | @"url":urlStr 407 | }, NO); 408 | CFRelease(delegate); 409 | } 410 | 411 | 412 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr); 413 | 414 | CGSize restrictSize = CGSizeMake(width, 10000); 415 | CGSize coreTestSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, 0), nil, restrictSize, nil); 416 | // NSLog(@"calcuted size %@",[NSValue valueWithCGSize:coreTestSize]); 417 | 418 | height = coreTestSize.height; 419 | height += 5; 420 | 421 | return height; 422 | } 423 | 424 | #pragma mark - RxTextLinkTapView delegate 425 | -(void)RxTextLinkTapView:(RxTextLinkTapView *)linkTapView didDetectTapWithUrlStr:(NSString *)urlStr{ 426 | // NSLog(@"link tapped !! %@",urlStr); 427 | if ([self.delegate respondsToSelector:@selector(RxLabel:didDetectedTapLinkWithUrlStr:)]) { 428 | [self.delegate RxLabel:self didDetectedTapLinkWithUrlStr:urlStr]; 429 | } 430 | } 431 | 432 | -(void)RxTextLinkTapView:(RxTextLinkTapView *)linkTapView didBeginHighlightedWithUrlStr:(NSString *)urlStr{ 433 | // [self setOtherLinkTapViewHightlighted:YES withUrlStr:urlStr]; 434 | } 435 | 436 | -(void)RxTextLinkTapView:(RxTextLinkTapView *)linkTapView didEndHighlightedWithUrlStr:(NSString *)urlStr{ 437 | // [self setOtherLinkTapViewHightlighted:NO withUrlStr:urlStr]; 438 | } 439 | 440 | -(void)setOtherLinkTapViewHightlighted:(BOOL)hightlighted withUrlStr:(NSString*)urlStr{ 441 | for (UIView* subview in self.subviews) { 442 | if (subview.tag == NSIntegerMin) { 443 | RxTextLinkTapView* lineTapView = (RxTextLinkTapView*)subview; 444 | if ([lineTapView.urlStr isEqualToString:urlStr] && lineTapView.highlighted != hightlighted) { 445 | [lineTapView setHighlighted:hightlighted]; 446 | } 447 | } 448 | } 449 | } 450 | 451 | @end 452 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/RxLabel/RxTextLinkTapView.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxTextLinkTapView.h 3 | // coreTextDemo 4 | // 5 | // Created by roxasora on 15/10/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | RxTextLinkTapViewTypeDefault, 13 | RxTextLinkTapViewTypeCustom 14 | } RxTextLinkTapViewType; 15 | 16 | @protocol RxTextLinkTapViewDelegate; 17 | 18 | @interface RxTextLinkTapView : UIView 19 | 20 | /** 21 | * create instance with frame and url 22 | * 23 | * @param frame frame 24 | * @param urlStr nsstring url 25 | * 26 | * @return instance 27 | */ 28 | -(id)initWithFrame:(CGRect)frame urlStr:(NSString*)urlStr font:(UIFont*)font linespacing:(CGFloat)linespacing; 29 | 30 | /** 31 | * delegate 32 | */ 33 | @property id delegate; 34 | 35 | /** 36 | * type of tap view,default has same bg color, custom has own bg color 37 | */ 38 | @property (nonatomic) RxTextLinkTapViewType type; 39 | 40 | /** 41 | * nsstring url 42 | */ 43 | @property (nonatomic)NSString* urlStr; 44 | 45 | /** 46 | * hightlighted just like uibutton 47 | */ 48 | @property (nonatomic)BOOL highlighted; 49 | 50 | /** 51 | * color when tapped 52 | */ 53 | @property (nonatomic)UIColor* tapColor; 54 | 55 | /** 56 | * line height of parent text view default is 0 57 | */ 58 | @property (nonatomic)CGFloat linespacing; 59 | 60 | /** 61 | * Deprecated... if replace the url,if yes then show a small round corner button with title, if no then show a hover layer 62 | */ 63 | //@property (nonatomic)BOOL isReplaceUrl; 64 | 65 | /** 66 | * replaced title 67 | */ 68 | @property (nonatomic)NSString* title; 69 | 70 | @end 71 | 72 | @protocol RxTextLinkTapViewDelegate 73 | 74 | -(void)RxTextLinkTapView:(RxTextLinkTapView*)linkTapView didDetectTapWithUrlStr:(NSString*)urlStr; 75 | -(void)RxTextLinkTapView:(RxTextLinkTapView*)linkTapView didBeginHighlightedWithUrlStr:(NSString*)urlStr; 76 | -(void)RxTextLinkTapView:(RxTextLinkTapView*)linkTapView didEndHighlightedWithUrlStr:(NSString*)urlStr; 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/RxLabel/RxTextLinkTapView.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxTextLinkTapView.m 3 | // coreTextDemo 4 | // 5 | // Created by roxasora on 15/10/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "RxTextLinkTapView.h" 10 | 11 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 12 | 13 | #define titleFontSize 12 14 | 15 | @interface RxTextLinkTapView () 16 | 17 | @property (nonatomic)UILabel* titleLabel; 18 | 19 | @end 20 | @implementation RxTextLinkTapView 21 | 22 | -(UILabel*)titleLabel{ 23 | if (!_titleLabel) { 24 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, (self.frame.size.height - titleFontSize)/2, self.frame.size.width, titleFontSize)]; 25 | _titleLabel.font = [UIFont systemFontOfSize:titleFontSize]; 26 | _titleLabel.textColor = [UIColor whiteColor]; 27 | _titleLabel.text = self.title; 28 | _titleLabel.textAlignment = NSTextAlignmentCenter; 29 | } 30 | return _titleLabel; 31 | } 32 | 33 | -(id)initWithFrame:(CGRect)frame{ 34 | self = [super initWithFrame:frame]; 35 | if (self) { 36 | self.layer.cornerRadius = 3; 37 | self.highlighted = NO; 38 | self.backgroundColor = [UIColor clearColor]; 39 | self.type = RxTextLinkTapViewTypeDefault; 40 | // self.isReplaceUrl =YES; 41 | 42 | self.title = @"网页"; 43 | self.linespacing = 0; 44 | 45 | [self addSubview:self.titleLabel]; 46 | 47 | UITapGestureRecognizer* tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; 48 | [self addGestureRecognizer:tapGes]; 49 | } 50 | return self; 51 | } 52 | 53 | -(id)initWithFrame:(CGRect)frame urlStr:(NSString *)urlStr font:(UIFont *)font linespacing:(CGFloat)linespacing{ 54 | frame.origin.y += (font.pointSize/16 * 2.8); 55 | 56 | self = [self initWithFrame:frame]; 57 | self.urlStr = urlStr; 58 | self.linespacing = linespacing; 59 | return self; 60 | } 61 | 62 | /* 63 | -(void)setIsReplaceUrl:(BOOL)isReplaceUrl{ 64 | _isReplaceUrl = isReplaceUrl; 65 | //如果替换,就显示为有颜色的,固体的 66 | if (isReplaceUrl) { 67 | self.backgroundColor = UIColorFromRGB(0X389ae5); 68 | [self addSubview:self.titleLabel]; 69 | }else{ 70 | self.backgroundColor = [UIColor clearColor]; 71 | } 72 | } 73 | */ 74 | 75 | -(void)setTitle:(NSString *)title{ 76 | _title = title; 77 | if (self.titleLabel) { 78 | self.titleLabel.text = title; 79 | } 80 | } 81 | 82 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 83 | // [super touchesBegan:touches withEvent:event]; 84 | [self setHighlighted:YES]; 85 | } 86 | 87 | -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 88 | [super touchesMoved:touches withEvent:event]; 89 | } 90 | 91 | -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ 92 | [super touchesCancelled:touches withEvent:event]; 93 | [self setHighlighted:NO]; 94 | } 95 | 96 | -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 97 | [super touchesEnded:touches withEvent:event]; 98 | [self setHighlighted:NO]; 99 | } 100 | 101 | -(void)setHighlighted:(BOOL)highlighted{ 102 | _highlighted = highlighted; 103 | // NSLog(@"cao nim "); 104 | if (highlighted) { 105 | self.alpha = 0.55; 106 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didBeginHighlightedWithUrlStr:)]) { 107 | [self.delegate RxTextLinkTapView:self didBeginHighlightedWithUrlStr:self.urlStr]; 108 | } 109 | }else{ 110 | self.alpha = 1; 111 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didEndHighlightedWithUrlStr:)]) { 112 | [self.delegate RxTextLinkTapView:self didEndHighlightedWithUrlStr:self.urlStr]; 113 | } 114 | } 115 | } 116 | 117 | /* 118 | -(void)setHighlighted:(BOOL)highlighted{ 119 | _highlighted = highlighted; 120 | // NSLog(@"cao nim "); 121 | if (!self.isReplaceUrl) { 122 | if (highlighted) { 123 | self.backgroundColor = self.tapColor; 124 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didBeginHighlightedWithUrlStr:)]) { 125 | [self.delegate RxTextLinkTapView:self didBeginHighlightedWithUrlStr:self.urlStr]; 126 | } 127 | }else{ 128 | self.backgroundColor = [UIColor clearColor]; 129 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didEndHighlightedWithUrlStr:)]) { 130 | [self.delegate RxTextLinkTapView:self didEndHighlightedWithUrlStr:self.urlStr]; 131 | } 132 | } 133 | }else{ 134 | if (highlighted) { 135 | // self.titleLabel.alpha = 0.6; 136 | // self.backgroundColor = UIColorFromRGB(0X2a7dbe); 137 | self.alpha = 0.55; 138 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didBeginHighlightedWithUrlStr:)]) { 139 | [self.delegate RxTextLinkTapView:self didBeginHighlightedWithUrlStr:self.urlStr]; 140 | } 141 | }else{ 142 | // self.titleLabel.alpha = 1; 143 | // self.backgroundColor = UIColorFromRGB(0X389ae5); 144 | // self.backgroundColor = self.tapColor; 145 | self.alpha = 1; 146 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didEndHighlightedWithUrlStr:)]) { 147 | [self.delegate RxTextLinkTapView:self didEndHighlightedWithUrlStr:self.urlStr]; 148 | } 149 | } 150 | } 151 | } 152 | */ 153 | 154 | -(void)handleTap:(UITapGestureRecognizer*)sender{ 155 | [self setHighlighted:YES]; 156 | [self performSelector:@selector(backToNormal) withObject:nil afterDelay:0.25]; 157 | if ([self.delegate respondsToSelector:@selector(RxTextLinkTapView:didDetectTapWithUrlStr:)]) { 158 | [self.delegate RxTextLinkTapView:self didDetectTapWithUrlStr:self.urlStr]; 159 | } 160 | } 161 | 162 | -(void)backToNormal{ 163 | [self setHighlighted:NO]; 164 | } 165 | @end 166 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "RxLabel.h" 11 | #import "RxWebViewController.h" 12 | #import "myNormalViewController.h" 13 | 14 | #define UIColorFromHexRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 15 | 16 | @interface ViewController () 17 | 18 | @property (strong, nonatomic) IBOutlet RxLabel *label; 19 | 20 | - (IBAction)navigationStyleSegmentChanged:(id)sender; 21 | - (IBAction)addItemClicked:(id)sender; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | self.title = @"RxWebViewController"; 31 | 32 | self.label.delegate = self; 33 | self.label.text = @"长者,指年纪大、辈分高、德高望重的人。一般多用于对别人的尊称,也可用于自称。能被称为长者的人往往具有丰富的人生经验,可以帮助年轻人提高姿势水平 http://github.com/roxasora"; 34 | self.label.customUrlArray = @[ 35 | @{ 36 | @"scheme":@"baidu", 37 | @"color":@0X459df5, 38 | @"title":@"百度" 39 | }, 40 | @{ 41 | @"scheme":@"github", 42 | @"color":@0X333333, 43 | @"title":@"Github" 44 | } 45 | ]; 46 | 47 | self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; 48 | self.navigationController.navigationBar.barTintColor = UIColorFromHexRGB(0X151515); 49 | [self.navigationController.navigationBar setTitleTextAttributes:@{ 50 | NSForegroundColorAttributeName:[UIColor whiteColor] 51 | }]; 52 | self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil]; 53 | 54 | //set custom back button image 55 | // [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationController class]]] setBackButtonBackgroundImage:[UIImage imageNamed:@"icon-nav-backButton-bg"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; 56 | } 57 | 58 | -(void)RxLabel:(RxLabel *)label didDetectedTapLinkWithUrlStr:(NSString *)urlStr{ 59 | RxWebViewController* webViewController = [[RxWebViewController alloc] initWithUrl:[NSURL URLWithString:urlStr]]; 60 | [self.navigationController pushViewController:webViewController animated:YES]; 61 | } 62 | 63 | - (void)didReceiveMemoryWarning { 64 | [super didReceiveMemoryWarning]; 65 | // Dispose of any resources that can be recreated. 66 | } 67 | 68 | - (IBAction)navigationStyleSegmentChanged:(id)sender { 69 | UISegmentedControl* seg = (UISegmentedControl*)sender; 70 | 71 | UIColor* tintColor; 72 | UIColor* barTintColor; 73 | switch (seg.selectedSegmentIndex) { 74 | case 0: 75 | { 76 | tintColor = [UIColor whiteColor]; 77 | barTintColor = UIColorFromHexRGB(0X151515); 78 | } 79 | break; 80 | case 1: 81 | { 82 | tintColor = [UIColor redColor]; 83 | barTintColor = [UIColor blueColor]; 84 | } 85 | break; 86 | case 2: 87 | { 88 | tintColor = [UIColor whiteColor]; 89 | barTintColor = UIColorFromHexRGB(0X4BAFF3); 90 | } 91 | break; 92 | 93 | default: 94 | break; 95 | } 96 | 97 | self.navigationController.navigationBar.tintColor = tintColor; 98 | self.navigationController.navigationBar.barTintColor = barTintColor; 99 | [self.navigationController.navigationBar setTitleTextAttributes:@{ 100 | NSForegroundColorAttributeName:tintColor 101 | }]; 102 | } 103 | 104 | - (IBAction)addItemClicked:(id)sender { 105 | myNormalViewController* vc = [[myNormalViewController alloc] init]; 106 | [self.navigationController pushViewController:vc animated:YES]; 107 | } 108 | 109 | //- (void) 110 | 111 | @end 112 | 113 | 114 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest/demo.gif -------------------------------------------------------------------------------- /RxWebViewControllerTest/icon-140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roxasora/RxWebViewController/12a53909b7a98d7d11dcf825b2a07a463f05dc8c/RxWebViewControllerTest/icon-140.png -------------------------------------------------------------------------------- /RxWebViewControllerTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RxWebViewController 4 | // 5 | // Created by roxasora on 15/10/23. 6 | // Copyright © 2015年 roxasora. 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 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/myNavigationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // myNavigationViewController.h 3 | // RxWebViewController 4 | // 5 | // Created by 范斌 on 15/12/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "RxWebViewNavigationViewController.h" 10 | 11 | @interface myNavigationViewController : RxWebViewNavigationViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/myNavigationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // myNavigationViewController.m 3 | // RxWebViewController 4 | // 5 | // Created by 范斌 on 15/12/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "myNavigationViewController.h" 10 | 11 | @interface myNavigationViewController () 12 | 13 | @end 14 | 15 | @implementation myNavigationViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/myNormalViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // myNormalViewController.h 3 | // RxWebViewController 4 | // 5 | // Created by 范斌 on 15/12/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface myNormalViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/myNormalViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // myNormalViewController.m 3 | // RxWebViewController 4 | // 5 | // Created by 范斌 on 15/12/9. 6 | // Copyright © 2015年 roxasora. All rights reserved. 7 | // 8 | 9 | #import "myNormalViewController.h" 10 | 11 | @interface myNormalViewController () 12 | - (IBAction)btnClicked:(id)sender; 13 | - (IBAction)popBtnClicked:(id)sender; 14 | 15 | @end 16 | 17 | @implementation myNormalViewController 18 | 19 | - (void)viewDidLoad { 20 | self.view.backgroundColor = [UIColor whiteColor]; 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view from its nib. 23 | } 24 | 25 | - (void)didReceiveMemoryWarning { 26 | [super didReceiveMemoryWarning]; 27 | // Dispose of any resources that can be recreated. 28 | } 29 | 30 | /* 31 | #pragma mark - Navigation 32 | 33 | // In a storyboard-based application, you will often want to do a little preparation before navigation 34 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 35 | // Get the new view controller using [segue destinationViewController]. 36 | // Pass the selected object to the new view controller. 37 | } 38 | */ 39 | 40 | - (IBAction)btnClicked:(id)sender { 41 | myNormalViewController* vc = [[myNormalViewController alloc] init]; 42 | [self.navigationController pushViewController:vc animated:YES]; 43 | 44 | } 45 | 46 | - (IBAction)popBtnClicked:(id)sender { 47 | [self.navigationController popViewControllerAnimated:YES]; 48 | } 49 | @end 50 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/myNormalViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------