├── .gitignore ├── README.md ├── RxWebViewController ├── 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 │ ├── yang.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── RxWebViewControllerTest.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 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 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 | -------------------------------------------------------------------------------- /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 | 11 | #define boundsWidth self.view.bounds.size.width 12 | #define boundsHeight self.view.bounds.size.height 13 | @interface RxWebViewController () 14 | 15 | //@property (strong, nonatomic) UIBarButtonItem* customBackBarItem; 16 | @property (strong, nonatomic) UIBarButtonItem* closeButtonItem; 17 | @property (strong, nonatomic) UILabel *hostInfoLabel; 18 | 19 | @property (strong, nonatomic) UIProgressView *progressView; 20 | @property (strong, nonatomic) NSTimer *timer; 21 | @property (nonatomic) BOOL loading; 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 | _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 | if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) { 75 | self.edgesForExtendedLayout = UIRectEdgeNone; 76 | } 77 | 78 | self.title = @""; 79 | 80 | //config navigation item 81 | self.navigationItem.leftItemsSupplementBackButton = YES; 82 | 83 | [self.view addSubview:self.webView]; 84 | [self.webView insertSubview:self.hostInfoLabel belowSubview:self.webView.scrollView]; 85 | [self.view addSubview:self.progressView]; 86 | 87 | [self.webView loadRequest:[NSURLRequest requestWithURL:self.url]]; 88 | } 89 | 90 | -(void)viewDidDisappear:(BOOL)animated{ 91 | [super viewDidDisappear:animated]; 92 | self.webView.delegate = nil; 93 | if (self.timer) { 94 | [self.timer invalidate]; 95 | } 96 | } 97 | 98 | 99 | #pragma mark - public funcs 100 | -(void)reloadWebView{ 101 | [self.webView reload]; 102 | } 103 | 104 | #pragma mark - logic of push and pop snap shot views 105 | -(void)pushCurrentSnapshotViewWithRequest:(NSURLRequest*)request{ 106 | NSLog(@"push with request %@",request); 107 | NSURLRequest* lastRequest = (NSURLRequest*)[[self.snapShotsArray lastObject] objectForKey:@"request"]; 108 | 109 | //如果url是很奇怪的就不push 110 | if ([request.URL.absoluteString isEqualToString:@"about:blank"]) { 111 | // NSLog(@"about blank!! return"); 112 | return; 113 | } 114 | //如果url一样就不进行push 115 | if ([lastRequest.URL.absoluteString isEqualToString:request.URL.absoluteString]) { 116 | return; 117 | } 118 | 119 | UIView* currentSnapShotView = [self.webView snapshotViewAfterScreenUpdates:YES]; 120 | [self.snapShotsArray addObject: 121 | @{ 122 | @"request":request, 123 | @"snapShotView":currentSnapShotView 124 | } 125 | ]; 126 | // NSLog(@"now array count %d",self.snapShotsArray.count); 127 | } 128 | 129 | -(void)startPopSnapshotView{ 130 | if (self.isSwipingBack) { 131 | return; 132 | } 133 | if (!self.webView.canGoBack) { 134 | return; 135 | } 136 | self.isSwipingBack = YES; 137 | //create a center of scrren 138 | CGPoint center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2); 139 | 140 | self.currentSnapShotView = [self.webView snapshotViewAfterScreenUpdates:YES]; 141 | 142 | //add shadows just like UINavigationController 143 | self.currentSnapShotView.layer.shadowColor = [UIColor blackColor].CGColor; 144 | self.currentSnapShotView.layer.shadowOffset = CGSizeMake(3, 3); 145 | self.currentSnapShotView.layer.shadowRadius = 5; 146 | self.currentSnapShotView.layer.shadowOpacity = 0.75; 147 | 148 | //move to center of screen 149 | self.currentSnapShotView.center = center; 150 | 151 | self.prevSnapShotView = (UIView*)[[self.snapShotsArray lastObject] objectForKey:@"snapShotView"]; 152 | center.x -= 60; 153 | self.prevSnapShotView.center = center; 154 | self.prevSnapShotView.alpha = 1; 155 | self.view.backgroundColor = [UIColor blackColor]; 156 | 157 | [self.webView addSubview:self.prevSnapShotView]; 158 | [self.webView addSubview:self.swipingBackgoundView]; 159 | [self.webView addSubview:self.currentSnapShotView]; 160 | } 161 | 162 | -(void)popSnapShotViewWithPanGestureDistance:(CGFloat)distance{ 163 | if (!self.isSwipingBack) { 164 | return; 165 | } 166 | 167 | if (distance <= 0) { 168 | return; 169 | } 170 | 171 | CGPoint currentSnapshotViewCenter = CGPointMake(boundsWidth/2, boundsHeight/2); 172 | currentSnapshotViewCenter.x += distance; 173 | CGPoint prevSnapshotViewCenter = CGPointMake(boundsWidth/2, boundsHeight/2); 174 | prevSnapshotViewCenter.x -= (boundsWidth - distance)*60/boundsWidth; 175 | // NSLog(@"prev center x%f",prevSnapshotViewCenter.x); 176 | 177 | self.currentSnapShotView.center = currentSnapshotViewCenter; 178 | self.prevSnapShotView.center = prevSnapshotViewCenter; 179 | self.swipingBackgoundView.alpha = (boundsWidth - distance)/boundsWidth; 180 | } 181 | 182 | -(void)endPopSnapShotView{ 183 | if (!self.isSwipingBack) { 184 | return; 185 | } 186 | 187 | //prevent the user touch for now 188 | self.view.userInteractionEnabled = NO; 189 | 190 | if (self.currentSnapShotView.center.x >= boundsWidth) { 191 | // pop success 192 | [UIView animateWithDuration:0.2 animations:^{ 193 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 194 | 195 | self.currentSnapShotView.center = CGPointMake(boundsWidth*3/2, boundsHeight/2); 196 | self.prevSnapShotView.center = CGPointMake(boundsWidth/2, boundsHeight/2); 197 | self.swipingBackgoundView.alpha = 0; 198 | }completion:^(BOOL finished) { 199 | 200 | [self.webView goBack]; 201 | [self.snapShotsArray removeLastObject]; 202 | [self.currentSnapShotView removeFromSuperview]; 203 | // [self.prevSnapShotView removeFromSuperview]; 204 | [self.swipingBackgoundView removeFromSuperview]; 205 | 206 | self.view.userInteractionEnabled = YES; 207 | self.isSwipingBack = NO; 208 | }]; 209 | }else{ 210 | //pop fail 211 | [UIView animateWithDuration:0.2 animations:^{ 212 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 213 | 214 | self.currentSnapShotView.center = CGPointMake(boundsWidth/2, boundsHeight/2); 215 | self.prevSnapShotView.center = CGPointMake(boundsWidth/2-60, boundsHeight/2); 216 | self.prevSnapShotView.alpha = 1; 217 | }completion:^(BOOL finished) { 218 | [self.prevSnapShotView removeFromSuperview]; 219 | [self.swipingBackgoundView removeFromSuperview]; 220 | [self.currentSnapShotView removeFromSuperview]; 221 | self.view.userInteractionEnabled = YES; 222 | 223 | self.isSwipingBack = NO; 224 | }]; 225 | } 226 | } 227 | 228 | #pragma mark - update nav items 229 | 230 | -(void)updateNavigationItems{ 231 | if (self.webView.canGoBack) { 232 | 233 | self.navigationController.interactivePopGestureRecognizer.enabled = NO; 234 | [self.navigationItem setLeftBarButtonItems:@[self.closeButtonItem] animated:NO]; 235 | 236 | //弃用customBackBarItem,使用原生backButtonItem 237 | // UIBarButtonItem *spaceButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; 238 | // spaceButtonItem.width = -6.5; 239 | // [self.navigationItem setLeftBarButtonItems:@[spaceButtonItem,self.customBackBarItem,self.closeButtonItem] animated:NO]; 240 | 241 | }else{ 242 | self.navigationController.interactivePopGestureRecognizer.enabled = YES; 243 | [self.navigationItem setLeftBarButtonItems:nil]; 244 | } 245 | } 246 | 247 | #pragma mark - events handler 248 | -(void)swipePanGestureHandler:(UIPanGestureRecognizer*)panGesture{ 249 | CGPoint translation = [panGesture translationInView:self.webView]; 250 | CGPoint location = [panGesture locationInView:self.webView]; 251 | // NSLog(@"pan x %f,pan y %f",translation.x,translation.y); 252 | 253 | if (panGesture.state == UIGestureRecognizerStateBegan) { 254 | if (location.x <= 50 && translation.x >= 0) { //开始动画 255 | [self startPopSnapshotView]; 256 | } 257 | }else if (panGesture.state == UIGestureRecognizerStateCancelled || panGesture.state == UIGestureRecognizerStateEnded){ 258 | [self endPopSnapShotView]; 259 | 260 | }else if (panGesture.state == UIGestureRecognizerStateChanged){ 261 | [self popSnapShotViewWithPanGestureDistance:translation.x]; 262 | } 263 | } 264 | 265 | -(void)customBackItemClicked{ 266 | [self.webView goBack]; 267 | } 268 | 269 | -(void)closeItemClicked{ 270 | [self.navigationController popViewControllerAnimated:YES]; 271 | } 272 | 273 | - (void)timerCallback { 274 | if (!self.loading) { 275 | if (self.progressView.progress >= 1) { 276 | self.progressView.hidden = true; 277 | [self.timer invalidate]; 278 | } 279 | else { 280 | self.progressView.progress += 0.5; 281 | } 282 | } 283 | else { 284 | self.progressView.progress += 0.05; 285 | if (self.progressView.progress >= 0.9) { 286 | self.progressView.progress = 0.9; 287 | } 288 | } 289 | } 290 | 291 | - (void)updateHostLabelWithRequest:(NSURLRequest *)request { 292 | NSString *host = [request.URL host]; 293 | if (host) { 294 | self.hostInfoLabel.text = [NSString stringWithFormat:@"网页由 %@ 提供", host]; 295 | } 296 | } 297 | 298 | #pragma mark - webView delegate 299 | - (void)webViewDidStartLoad:(UIWebView *)webView { 300 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 301 | 302 | self.progressView.progress = 0; 303 | self.progressView.hidden = false; 304 | self.loading = YES; 305 | if (!self.timer) { 306 | self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerCallback) userInfo:nil repeats:YES]; 307 | } 308 | } 309 | 310 | -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 311 | // NSLog(@"navigation type %ld",(long)navigationType); 312 | NSLog(@"url: %@",request.URL); 313 | [self updateHostLabelWithRequest:request]; 314 | 315 | switch (navigationType) { 316 | case UIWebViewNavigationTypeLinkClicked: { 317 | [self pushCurrentSnapshotViewWithRequest:request]; 318 | break; 319 | } 320 | case UIWebViewNavigationTypeFormSubmitted: { 321 | [self pushCurrentSnapshotViewWithRequest:request]; 322 | break; 323 | } 324 | case UIWebViewNavigationTypeBackForward: { 325 | break; 326 | } 327 | case UIWebViewNavigationTypeReload: { 328 | break; 329 | } 330 | case UIWebViewNavigationTypeFormResubmitted: { 331 | break; 332 | } 333 | case UIWebViewNavigationTypeOther: { 334 | [self pushCurrentSnapshotViewWithRequest:request]; 335 | break; 336 | } 337 | default: { 338 | break; 339 | } 340 | } 341 | // [self updateNavigationItems]; 342 | return YES; 343 | } 344 | 345 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 346 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 347 | [self updateNavigationItems]; 348 | NSString *theTitle=[webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 349 | if (theTitle.length > 10) { 350 | theTitle = [[theTitle substringToIndex:9] stringByAppendingString:@"…"]; 351 | } 352 | self.title = theTitle; 353 | 354 | self.loading = NO; 355 | if (self.prevSnapShotView.superview) { 356 | [self.prevSnapShotView removeFromSuperview]; 357 | } 358 | } 359 | 360 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 361 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 362 | } 363 | 364 | #pragma mark - setters and getters 365 | 366 | -(UIWebView*)webView{ 367 | if (!_webView) { 368 | _webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; 369 | _webView.delegate = (id)self; 370 | _webView.scalesPageToFit = YES; 371 | _webView.contentMode = UIViewContentModeScaleAspectFit; 372 | _webView.autoresizingMask=(UIViewAutoresizingFlexibleHeight |UIViewAutoresizingFlexibleWidth); 373 | // _webView.backgroundColor = [UIColor whiteColor]; 374 | [_webView addGestureRecognizer:self.swipePanGesture]; 375 | } 376 | return _webView; 377 | } 378 | 379 | //-(UIBarButtonItem*)customBackBarItem{ 380 | // if (!_customBackBarItem) { 381 | // UIImage* backItemImage = [[UIImage imageNamed:@"backItemImage"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 382 | // UIImage* backItemHlImage = [[UIImage imageNamed:@"backItemImage-hl"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 383 | // 384 | // UIButton* backButton = [[UIButton alloc] init]; 385 | // [backButton setTitle:@"返回" forState:UIControlStateNormal]; 386 | // [backButton setTitleColor:self.navigationController.navigationBar.tintColor forState:UIControlStateNormal]; 387 | // [backButton setTitleColor:[self.navigationController.navigationBar.tintColor colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 388 | // [backButton.titleLabel setFont:[UIFont systemFontOfSize:17]]; 389 | // [backButton setImage:backItemImage forState:UIControlStateNormal]; 390 | // [backButton setImage:backItemHlImage forState:UIControlStateHighlighted]; 391 | // [backButton sizeToFit]; 392 | // 393 | // [backButton addTarget:self action:@selector(customBackItemClicked) forControlEvents:UIControlEventTouchUpInside]; 394 | // _customBackBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; 395 | // } 396 | // return _customBackBarItem; 397 | //} 398 | 399 | -(UIBarButtonItem*)closeButtonItem{ 400 | if (!_closeButtonItem) { 401 | _closeButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(closeItemClicked)]; 402 | } 403 | return _closeButtonItem; 404 | } 405 | 406 | -(UIView*)swipingBackgoundView{ 407 | if (!_swipingBackgoundView) { 408 | _swipingBackgoundView = [[UIView alloc] initWithFrame:self.view.bounds]; 409 | _swipingBackgoundView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]; 410 | } 411 | return _swipingBackgoundView; 412 | } 413 | 414 | -(NSMutableArray*)snapShotsArray{ 415 | if (!_snapShotsArray) { 416 | _snapShotsArray = [NSMutableArray array]; 417 | } 418 | return _snapShotsArray; 419 | } 420 | 421 | -(BOOL)isSwipingBack{ 422 | if (!_isSwipingBack) { 423 | _isSwipingBack = NO; 424 | } 425 | return _isSwipingBack; 426 | } 427 | 428 | -(UIPanGestureRecognizer*)swipePanGesture{ 429 | if (!_swipePanGesture) { 430 | _swipePanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipePanGestureHandler:)]; 431 | } 432 | return _swipePanGesture; 433 | } 434 | 435 | - (UIProgressView *)progressView { 436 | if (!_progressView) { 437 | CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, 2.0); 438 | _progressView = [[UIProgressView alloc] initWithFrame:frame]; 439 | _progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth ; 440 | _progressView.tintColor = self.progressViewColor; 441 | _progressView.trackTintColor = [UIColor clearColor]; 442 | } 443 | 444 | return _progressView; 445 | } 446 | 447 | - (UILabel *)hostInfoLabel { 448 | if (!_hostInfoLabel) { 449 | _hostInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, self.view.frame.size.width, 20)]; 450 | _hostInfoLabel.textColor = [UIColor grayColor]; 451 | _hostInfoLabel.font = [UIFont systemFontOfSize:14]; 452 | _hostInfoLabel.textAlignment = NSTextAlignmentCenter; 453 | } 454 | 455 | return _hostInfoLabel; 456 | } 457 | 458 | @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/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/RxWebViewController/backItemImage@2x.png -------------------------------------------------------------------------------- /RxWebViewController/backItemImage_hl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/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 | 2FC924BD1C853BC900064799 /* RxWebViewNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FC924BC1C853BC900064799 /* RxWebViewNavigationViewController.m */; }; 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 | F59CDEA81C1FBFB9008B5358 /* RxLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */; }; 20 | F59CDEA91C1FBFB9008B5358 /* RxTextLinkTapView.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */; }; 21 | F59CDEB01C1FBFB9008B5358 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDE9E1C1FBFB9008B5358 /* ViewController.m */; }; 22 | F59CDEC51C1FC6AE008B5358 /* backItemImage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */; }; 23 | F59CDEC61C1FC6AE008B5358 /* backItemImage_hl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */; }; 24 | F59CDEC91C1FC6AE008B5358 /* RxWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 2FC924BB1C853BC900064799 /* RxWebViewNavigationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxWebViewNavigationViewController.h; sourceTree = ""; }; 29 | 2FC924BC1C853BC900064799 /* RxWebViewNavigationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxWebViewNavigationViewController.m; sourceTree = ""; }; 30 | F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxWebViewControllerTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | F59CDE7F1C1FBFB9008B5358 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | F59CDE801C1FBFB9008B5358 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | F59CDE811C1FBFB9008B5358 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | F59CDE831C1FBFB9008B5358 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | F59CDE851C1FBFB9008B5358 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | F59CDE861C1FBFB9008B5358 /* demo.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = demo.gif; sourceTree = ""; }; 37 | F59CDE871C1FBFB9008B5358 /* icon-140.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-140.png"; sourceTree = ""; }; 38 | F59CDE881C1FBFB9008B5358 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | F59CDE891C1FBFB9008B5358 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 40 | F59CDE8D1C1FBFB9008B5358 /* RxLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxLabel.h; sourceTree = ""; }; 41 | F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLabel.m; sourceTree = ""; }; 42 | F59CDE8F1C1FBFB9008B5358 /* RxTextLinkTapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxTextLinkTapView.h; sourceTree = ""; }; 43 | F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxTextLinkTapView.m; sourceTree = ""; }; 44 | F59CDE9D1C1FBFB9008B5358 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 45 | F59CDE9E1C1FBFB9008B5358 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 46 | F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backItemImage@2x.png"; sourceTree = ""; }; 47 | F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backItemImage_hl@2x.png"; sourceTree = ""; }; 48 | F59CDEC11C1FC6AE008B5358 /* RxWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxWebViewController.h; sourceTree = ""; }; 49 | F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxWebViewController.m; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | F53D31961BDA4FCA002322E1 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | F53D31901BDA4FCA002322E1 = { 64 | isa = PBXGroup; 65 | children = ( 66 | F59CDEB91C1FC6AE008B5358 /* RxWebViewController */, 67 | F59CDE7E1C1FBFB9008B5358 /* RxWebViewControllerTest */, 68 | F53D319A1BDA4FCA002322E1 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | F53D319A1BDA4FCA002322E1 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | F59CDE7E1C1FBFB9008B5358 /* RxWebViewControllerTest */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | F59CDE7F1C1FBFB9008B5358 /* AppDelegate.h */, 84 | F59CDE801C1FBFB9008B5358 /* AppDelegate.m */, 85 | F59CDE9D1C1FBFB9008B5358 /* ViewController.h */, 86 | F59CDE9E1C1FBFB9008B5358 /* ViewController.m */, 87 | F59CDE811C1FBFB9008B5358 /* Assets.xcassets */, 88 | F59CDE821C1FBFB9008B5358 /* LaunchScreen.storyboard */, 89 | F59CDE841C1FBFB9008B5358 /* Main.storyboard */, 90 | F59CDE861C1FBFB9008B5358 /* demo.gif */, 91 | F59CDE871C1FBFB9008B5358 /* icon-140.png */, 92 | F59CDE881C1FBFB9008B5358 /* Info.plist */, 93 | F59CDE891C1FBFB9008B5358 /* main.m */, 94 | F59CDE8C1C1FBFB9008B5358 /* RxLabel */, 95 | ); 96 | path = RxWebViewControllerTest; 97 | sourceTree = ""; 98 | }; 99 | F59CDE8C1C1FBFB9008B5358 /* RxLabel */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | F59CDE8D1C1FBFB9008B5358 /* RxLabel.h */, 103 | F59CDE8E1C1FBFB9008B5358 /* RxLabel.m */, 104 | F59CDE8F1C1FBFB9008B5358 /* RxTextLinkTapView.h */, 105 | F59CDE901C1FBFB9008B5358 /* RxTextLinkTapView.m */, 106 | ); 107 | path = RxLabel; 108 | sourceTree = ""; 109 | }; 110 | F59CDEB91C1FC6AE008B5358 /* RxWebViewController */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | F59CDEBA1C1FC6AE008B5358 /* backItemImage@2x.png */, 114 | F59CDEBB1C1FC6AE008B5358 /* backItemImage_hl@2x.png */, 115 | 2FC924BB1C853BC900064799 /* RxWebViewNavigationViewController.h */, 116 | 2FC924BC1C853BC900064799 /* RxWebViewNavigationViewController.m */, 117 | F59CDEC11C1FC6AE008B5358 /* RxWebViewController.h */, 118 | F59CDEC21C1FC6AE008B5358 /* RxWebViewController.m */, 119 | ); 120 | path = RxWebViewController; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | F53D31981BDA4FCA002322E1 /* RxWebViewControllerTest */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = F53D31C61BDA4FCB002322E1 /* Build configuration list for PBXNativeTarget "RxWebViewControllerTest" */; 129 | buildPhases = ( 130 | F53D31951BDA4FCA002322E1 /* Sources */, 131 | F53D31961BDA4FCA002322E1 /* Frameworks */, 132 | F53D31971BDA4FCA002322E1 /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = RxWebViewControllerTest; 139 | productName = RxWebViewController; 140 | productReference = F53D31991BDA4FCA002322E1 /* RxWebViewControllerTest.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | F53D31911BDA4FCA002322E1 /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 0700; 150 | ORGANIZATIONNAME = roxasora; 151 | TargetAttributes = { 152 | F53D31981BDA4FCA002322E1 = { 153 | CreatedOnToolsVersion = 7.0.1; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = F53D31941BDA4FCA002322E1 /* Build configuration list for PBXProject "RxWebViewControllerTest" */; 158 | compatibilityVersion = "Xcode 3.2"; 159 | developmentRegion = English; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | Base, 164 | ); 165 | mainGroup = F53D31901BDA4FCA002322E1; 166 | productRefGroup = F53D319A1BDA4FCA002322E1 /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | F53D31981BDA4FCA002322E1 /* RxWebViewControllerTest */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | F53D31971BDA4FCA002322E1 /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | F59CDEA11C1FBFB9008B5358 /* LaunchScreen.storyboard in Resources */, 181 | F59CDEA01C1FBFB9008B5358 /* Assets.xcassets in Resources */, 182 | F59CDEA41C1FBFB9008B5358 /* icon-140.png in Resources */, 183 | F59CDEA31C1FBFB9008B5358 /* demo.gif in Resources */, 184 | F59CDEC51C1FC6AE008B5358 /* backItemImage@2x.png in Resources */, 185 | F59CDEC61C1FC6AE008B5358 /* backItemImage_hl@2x.png in Resources */, 186 | F59CDEA21C1FBFB9008B5358 /* Main.storyboard in Resources */, 187 | F59CDEA51C1FBFB9008B5358 /* Info.plist in Resources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXSourcesBuildPhase section */ 194 | F53D31951BDA4FCA002322E1 /* Sources */ = { 195 | isa = PBXSourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | F59CDEA61C1FBFB9008B5358 /* main.m in Sources */, 199 | 2FC924BD1C853BC900064799 /* RxWebViewNavigationViewController.m in Sources */, 200 | F59CDE9F1C1FBFB9008B5358 /* AppDelegate.m in Sources */, 201 | F59CDEA81C1FBFB9008B5358 /* RxLabel.m in Sources */, 202 | F59CDEB01C1FBFB9008B5358 /* ViewController.m in Sources */, 203 | F59CDEA91C1FBFB9008B5358 /* RxTextLinkTapView.m in Sources */, 204 | F59CDEC91C1FC6AE008B5358 /* RxWebViewController.m in Sources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXSourcesBuildPhase section */ 209 | 210 | /* Begin PBXVariantGroup section */ 211 | F59CDE821C1FBFB9008B5358 /* LaunchScreen.storyboard */ = { 212 | isa = PBXVariantGroup; 213 | children = ( 214 | F59CDE831C1FBFB9008B5358 /* Base */, 215 | ); 216 | name = LaunchScreen.storyboard; 217 | sourceTree = ""; 218 | }; 219 | F59CDE841C1FBFB9008B5358 /* Main.storyboard */ = { 220 | isa = PBXVariantGroup; 221 | children = ( 222 | F59CDE851C1FBFB9008B5358 /* Base */, 223 | ); 224 | name = Main.storyboard; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXVariantGroup section */ 228 | 229 | /* Begin XCBuildConfiguration section */ 230 | F53D31C41BDA4FCB002322E1 /* Debug */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 248 | COPY_PHASE_STRIP = NO; 249 | DEBUG_INFORMATION_FORMAT = dwarf; 250 | ENABLE_STRICT_OBJC_MSGSEND = YES; 251 | ENABLE_TESTABILITY = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu99; 253 | GCC_DYNAMIC_NO_PIC = NO; 254 | GCC_NO_COMMON_BLOCKS = YES; 255 | GCC_OPTIMIZATION_LEVEL = 0; 256 | GCC_PREPROCESSOR_DEFINITIONS = ( 257 | "DEBUG=1", 258 | "$(inherited)", 259 | ); 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 267 | MTL_ENABLE_DEBUG_INFO = YES; 268 | ONLY_ACTIVE_ARCH = YES; 269 | SDKROOT = iphoneos; 270 | TARGETED_DEVICE_FAMILY = "1,2"; 271 | }; 272 | name = Debug; 273 | }; 274 | F53D31C51BDA4FCB002322E1 /* Release */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | ALWAYS_SEARCH_USER_PATHS = NO; 278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 279 | CLANG_CXX_LIBRARY = "libc++"; 280 | CLANG_ENABLE_MODULES = YES; 281 | CLANG_ENABLE_OBJC_ARC = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 285 | CLANG_WARN_EMPTY_BODY = YES; 286 | CLANG_WARN_ENUM_CONVERSION = YES; 287 | CLANG_WARN_INT_CONVERSION = YES; 288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 294 | ENABLE_NS_ASSERTIONS = NO; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 300 | GCC_WARN_UNDECLARED_SELECTOR = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 302 | GCC_WARN_UNUSED_FUNCTION = YES; 303 | GCC_WARN_UNUSED_VARIABLE = YES; 304 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 305 | MTL_ENABLE_DEBUG_INFO = NO; 306 | SDKROOT = iphoneos; 307 | TARGETED_DEVICE_FAMILY = "1,2"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Release; 311 | }; 312 | F53D31C71BDA4FCB002322E1 /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 316 | ENABLE_BITCODE = NO; 317 | FRAMEWORK_SEARCH_PATHS = ( 318 | "$(inherited)", 319 | "$(PROJECT_DIR)", 320 | ); 321 | INFOPLIST_FILE = "$(SRCROOT)/RxWebViewControllerTest/Info.plist"; 322 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 323 | OTHER_LDFLAGS = ""; 324 | PRODUCT_BUNDLE_IDENTIFIER = com.roxasora.RxWebViewController; 325 | PRODUCT_NAME = RxWebViewControllerTest; 326 | }; 327 | name = Debug; 328 | }; 329 | F53D31C81BDA4FCB002322E1 /* Release */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 333 | ENABLE_BITCODE = NO; 334 | FRAMEWORK_SEARCH_PATHS = ( 335 | "$(inherited)", 336 | "$(PROJECT_DIR)", 337 | ); 338 | INFOPLIST_FILE = "$(SRCROOT)/RxWebViewControllerTest/Info.plist"; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 340 | OTHER_LDFLAGS = ""; 341 | PRODUCT_BUNDLE_IDENTIFIER = com.roxasora.RxWebViewController; 342 | PRODUCT_NAME = RxWebViewControllerTest; 343 | }; 344 | name = Release; 345 | }; 346 | /* End XCBuildConfiguration section */ 347 | 348 | /* Begin XCConfigurationList section */ 349 | F53D31941BDA4FCA002322E1 /* Build configuration list for PBXProject "RxWebViewControllerTest" */ = { 350 | isa = XCConfigurationList; 351 | buildConfigurations = ( 352 | F53D31C41BDA4FCB002322E1 /* Debug */, 353 | F53D31C51BDA4FCB002322E1 /* Release */, 354 | ); 355 | defaultConfigurationIsVisible = 0; 356 | defaultConfigurationName = Release; 357 | }; 358 | F53D31C61BDA4FCB002322E1 /* Build configuration list for PBXNativeTarget "RxWebViewControllerTest" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | F53D31C71BDA4FCB002322E1 /* Debug */, 362 | F53D31C81BDA4FCB002322E1 /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | /* End XCConfigurationList section */ 368 | }; 369 | rootObject = F53D31911BDA4FCA002322E1 /* Project object */; 370 | } 371 | -------------------------------------------------------------------------------- /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/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/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/yang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /RxWebViewControllerTest.xcodeproj/xcuserdata/yang.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/yang.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.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 | "idiom" : "ipad", 102 | "size" : "83.5x83.5", 103 | "scale" : "2x" 104 | } 105 | ], 106 | "info" : { 107 | "version" : 1, 108 | "author" : "xcode" 109 | } 110 | } -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-120.png -------------------------------------------------------------------------------- /RxWebViewControllerTest/Assets.xcassets/AppIcon.appiconset/icon-121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/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/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 13 | #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] 14 | 15 | @interface ViewController () 16 | 17 | @property (strong, nonatomic) IBOutlet RxLabel *label; 18 | 19 | - (IBAction)navigationStyleSegmentChanged:(id)sender; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | 28 | if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) { 29 | self.edgesForExtendedLayout = UIRectEdgeNone; 30 | } 31 | 32 | self.title = @"RxWebViewController"; 33 | 34 | self.label.delegate = self; 35 | 36 | self.label.text = @"长者,指年纪大、辈分高、德高望重的人。一般多用于对别人的尊称,也可用于自称。能被称为长者的人往往具有丰富的人生经验,可以帮助年轻人提高姿势水平 https://github.com/roxasora http://www.baidu.com"; 37 | self.label.customUrlArray = @[ 38 | @{ 39 | @"scheme":@"baidu", 40 | @"color":@0X459df5, 41 | @"title":@"百度" 42 | }, 43 | @{ 44 | @"scheme":@"github", 45 | @"color":@0X333333, 46 | @"title":@"Github" 47 | } 48 | ]; 49 | 50 | self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; 51 | self.navigationController.navigationBar.barTintColor = UIColorFromHexRGB(0X151515); 52 | [self.navigationController.navigationBar setTitleTextAttributes:@{ 53 | NSForegroundColorAttributeName:[UIColor whiteColor] 54 | }]; 55 | // self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil]; 56 | 57 | //set custom back button image 58 | // [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationController class]]] setBackButtonBackgroundImage:[UIImage imageNamed:@"icon-nav-backButton-bg"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; 59 | } 60 | 61 | -(void)RxLabel:(RxLabel *)label didDetectedTapLinkWithUrlStr:(NSString *)urlStr{ 62 | RxWebViewController* webViewController = [[RxWebViewController alloc] initWithUrl:[NSURL URLWithString:urlStr]]; 63 | [self.navigationController pushViewController:webViewController animated:YES]; 64 | } 65 | 66 | - (void)didReceiveMemoryWarning { 67 | [super didReceiveMemoryWarning]; 68 | // Dispose of any resources that can be recreated. 69 | } 70 | 71 | - (IBAction)navigationStyleSegmentChanged:(id)sender { 72 | UISegmentedControl* seg = (UISegmentedControl*)sender; 73 | 74 | UIColor* tintColor; 75 | UIColor* barTintColor; 76 | switch (seg.selectedSegmentIndex) { 77 | case 0: 78 | { 79 | tintColor = [UIColor whiteColor]; 80 | barTintColor = UIColorFromHexRGB(0X151515); 81 | } 82 | break; 83 | case 1: 84 | { 85 | tintColor = [UIColor redColor]; 86 | barTintColor = [UIColor blueColor]; 87 | } 88 | break; 89 | case 2: 90 | { 91 | tintColor = [UIColor whiteColor]; 92 | barTintColor = UIColorFromHexRGB(0X4BAFF3); 93 | } 94 | break; 95 | 96 | default: 97 | break; 98 | } 99 | 100 | self.navigationController.navigationBar.tintColor = tintColor; 101 | self.navigationController.navigationBar.barTintColor = barTintColor; 102 | [self.navigationController.navigationBar setTitleTextAttributes:@{ 103 | NSForegroundColorAttributeName:tintColor 104 | }]; 105 | } 106 | 107 | @end 108 | 109 | 110 | -------------------------------------------------------------------------------- /RxWebViewControllerTest/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/RxWebViewControllerTest/demo.gif -------------------------------------------------------------------------------- /RxWebViewControllerTest/icon-140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yang0013/RxWebViewController/4e2491f72d33764748e73feda5169f0943fccbae/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 | --------------------------------------------------------------------------------