├── HXTechnologyBlogCrawler.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── huangxuan518.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── HXTechnologyBlogCrawler.xcscheme │ └── xcschememanagement.plist ├── HXTechnologyBlogCrawler.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── huangxuan518.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── HXTechnologyBlogCrawler ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── Default-568h@2x.png │ │ └── Default-667h@2x.png │ └── logo.imageset │ │ ├── Contents.json │ │ ├── logo.png │ │ ├── logo@2x.png │ │ └── logo@3x.png ├── CoreData │ └── DatasourceModel.xcdatamodeld │ │ └── DatasourceModel.xcdatamodel │ │ └── contents ├── Hpple │ ├── TFHpple.h │ ├── TFHpple.m │ ├── TFHppleElement.h │ ├── TFHppleElement.m │ ├── XPathQuery.h │ └── XPathQuery.m ├── Info.plist ├── Main │ ├── Model │ │ ├── HXArticle.h │ │ └── HXArticle.m │ ├── View │ │ ├── HXArticleListCell.h │ │ ├── HXArticleListCell.m │ │ ├── HXArticleListCell.xib │ │ ├── HXHistoryCell.h │ │ ├── HXHistoryCell.m │ │ ├── HXHistoryCell.xib │ │ ├── HXHistoryLastCell.h │ │ ├── HXHistoryLastCell.m │ │ └── HXHistoryLastCell.xib │ └── ViewController │ │ ├── Detail │ │ ├── HXDetailWebViewController.h │ │ ├── HXDetailWebViewController.m │ │ └── HXDetailWebViewController.xib │ │ ├── Home │ │ ├── HXHomeViewController.h │ │ ├── HXHomeViewController.m │ │ └── HXHomeViewController.xib │ │ └── List │ │ ├── HXArticleListViewController.h │ │ ├── HXArticleListViewController.m │ │ ├── HXHistoryListViewController.h │ │ ├── HXHistoryListViewController.m │ │ ├── HXSearchResultViewController.h │ │ └── HXSearchResultViewController.m ├── Request │ ├── HXRequestManager.h │ └── HXRequestManager.m ├── main.m └── xiaoguo.gif ├── Podfile ├── Podfile.lock ├── Pods ├── Headers │ ├── Private │ │ └── MJRefresh │ │ │ ├── MJRefresh.h │ │ │ ├── MJRefreshAutoFooter.h │ │ │ ├── MJRefreshAutoGifFooter.h │ │ │ ├── MJRefreshAutoNormalFooter.h │ │ │ ├── MJRefreshAutoStateFooter.h │ │ │ ├── MJRefreshBackFooter.h │ │ │ ├── MJRefreshBackGifFooter.h │ │ │ ├── MJRefreshBackNormalFooter.h │ │ │ ├── MJRefreshBackStateFooter.h │ │ │ ├── MJRefreshComponent.h │ │ │ ├── MJRefreshConst.h │ │ │ ├── MJRefreshFooter.h │ │ │ ├── MJRefreshGifHeader.h │ │ │ ├── MJRefreshHeader.h │ │ │ ├── MJRefreshNormalHeader.h │ │ │ ├── MJRefreshStateHeader.h │ │ │ ├── UIScrollView+MJExtension.h │ │ │ ├── UIScrollView+MJRefresh.h │ │ │ └── UIView+MJExtension.h │ └── Public │ │ └── MJRefresh │ │ ├── MJRefresh.h │ │ ├── MJRefreshAutoFooter.h │ │ ├── MJRefreshAutoGifFooter.h │ │ ├── MJRefreshAutoNormalFooter.h │ │ ├── MJRefreshAutoStateFooter.h │ │ ├── MJRefreshBackFooter.h │ │ ├── MJRefreshBackGifFooter.h │ │ ├── MJRefreshBackNormalFooter.h │ │ ├── MJRefreshBackStateFooter.h │ │ ├── MJRefreshComponent.h │ │ ├── MJRefreshConst.h │ │ ├── MJRefreshFooter.h │ │ ├── MJRefreshGifHeader.h │ │ ├── MJRefreshHeader.h │ │ ├── MJRefreshNormalHeader.h │ │ ├── MJRefreshStateHeader.h │ │ ├── UIScrollView+MJExtension.h │ │ ├── UIScrollView+MJRefresh.h │ │ └── UIView+MJExtension.h ├── MJRefresh │ ├── LICENSE │ ├── MJRefresh │ │ ├── Base │ │ │ ├── MJRefreshAutoFooter.h │ │ │ ├── MJRefreshAutoFooter.m │ │ │ ├── MJRefreshBackFooter.h │ │ │ ├── MJRefreshBackFooter.m │ │ │ ├── MJRefreshComponent.h │ │ │ ├── MJRefreshComponent.m │ │ │ ├── MJRefreshFooter.h │ │ │ ├── MJRefreshFooter.m │ │ │ ├── MJRefreshHeader.h │ │ │ └── MJRefreshHeader.m │ │ ├── Custom │ │ │ ├── Footer │ │ │ │ ├── Auto │ │ │ │ │ ├── MJRefreshAutoGifFooter.h │ │ │ │ │ ├── MJRefreshAutoGifFooter.m │ │ │ │ │ ├── MJRefreshAutoNormalFooter.h │ │ │ │ │ ├── MJRefreshAutoNormalFooter.m │ │ │ │ │ ├── MJRefreshAutoStateFooter.h │ │ │ │ │ └── MJRefreshAutoStateFooter.m │ │ │ │ └── Back │ │ │ │ │ ├── MJRefreshBackGifFooter.h │ │ │ │ │ ├── MJRefreshBackGifFooter.m │ │ │ │ │ ├── MJRefreshBackNormalFooter.h │ │ │ │ │ ├── MJRefreshBackNormalFooter.m │ │ │ │ │ ├── MJRefreshBackStateFooter.h │ │ │ │ │ └── MJRefreshBackStateFooter.m │ │ │ └── Header │ │ │ │ ├── MJRefreshGifHeader.h │ │ │ │ ├── MJRefreshGifHeader.m │ │ │ │ ├── MJRefreshNormalHeader.h │ │ │ │ ├── MJRefreshNormalHeader.m │ │ │ │ ├── MJRefreshStateHeader.h │ │ │ │ └── MJRefreshStateHeader.m │ │ ├── MJRefresh.bundle │ │ │ └── arrow@2x.png │ │ ├── MJRefresh.h │ │ ├── MJRefreshConst.h │ │ ├── MJRefreshConst.m │ │ ├── UIScrollView+MJExtension.h │ │ ├── UIScrollView+MJExtension.m │ │ ├── UIScrollView+MJRefresh.h │ │ ├── UIScrollView+MJRefresh.m │ │ ├── UIView+MJExtension.h │ │ └── UIView+MJExtension.m │ └── README.md ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── huangxuan518.xcuserdatad │ │ └── xcschemes │ │ ├── MJRefresh.xcscheme │ │ ├── Pods-HXTechnologyBlogCrawler.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ ├── MJRefresh │ ├── MJRefresh-dummy.m │ ├── MJRefresh-prefix.pch │ └── MJRefresh.xcconfig │ └── Pods-HXTechnologyBlogCrawler │ ├── Pods-HXTechnologyBlogCrawler-acknowledgements.markdown │ ├── Pods-HXTechnologyBlogCrawler-acknowledgements.plist │ ├── Pods-HXTechnologyBlogCrawler-dummy.m │ ├── Pods-HXTechnologyBlogCrawler-frameworks.sh │ ├── Pods-HXTechnologyBlogCrawler-resources.sh │ ├── Pods-HXTechnologyBlogCrawler.debug.xcconfig │ └── Pods-HXTechnologyBlogCrawler.release.xcconfig └── README.md /HXTechnologyBlogCrawler.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcschemes/HXTechnologyBlogCrawler.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 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | HXTechnologyBlogCrawler.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | E49D47361D65670C004FE1E6 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcworkspace/xcuserdata/huangxuan518.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler.xcworkspace/xcuserdata/huangxuan518.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler.xcworkspace/xcuserdata/huangxuan518.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HXHtmlParser 4 | // 5 | // Created by 黄轩 on 16/8/18. 6 | // Copyright © 2016年 黄轩. 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 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HXHtmlParser 4 | // 5 | // Created by 黄轩 on 16/8/18. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "HXHomeViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | 24 | HXHomeViewController *vc = [HXHomeViewController new]; 25 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; 26 | self.window.rootViewController = nav; 27 | 28 | [self.window makeKeyAndVisible]; 29 | 30 | return YES; 31 | } 32 | 33 | - (void)applicationWillResignActive:(UIApplication *)application { 34 | // 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. 35 | // 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. 36 | } 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application { 39 | // 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. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | - (void)applicationWillEnterForeground:(UIApplication *)application { 44 | // 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. 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application { 48 | // 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. 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "extent" : "full-screen", 21 | "idiom" : "iphone", 22 | "subtype" : "667h", 23 | "filename" : "Default-667h@2x.png", 24 | "minimum-system-version" : "8.0", 25 | "orientation" : "portrait", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "orientation" : "portrait", 30 | "idiom" : "iphone", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "extent" : "full-screen", 37 | "idiom" : "iphone", 38 | "subtype" : "retina4", 39 | "filename" : "Default-568h@2x.png", 40 | "minimum-system-version" : "7.0", 41 | "orientation" : "portrait", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "portrait", 46 | "idiom" : "iphone", 47 | "extent" : "full-screen", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "orientation" : "portrait", 52 | "idiom" : "iphone", 53 | "extent" : "full-screen", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "iphone", 59 | "extent" : "full-screen", 60 | "subtype" : "retina4", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo.png -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo@2x.png -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/Assets.xcassets/logo.imageset/logo@3x.png -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/CoreData/DatasourceModel.xcdatamodeld/DatasourceModel.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/TFHpple.h: -------------------------------------------------------------------------------- 1 | // 2 | // TFHpple.h 3 | // Hpple 4 | // 5 | // Created by Geoffrey Grosenbach on 1/31/09. 6 | // 7 | // Copyright (c) 2009 Topfunky Corporation, http://topfunky.com 8 | // 9 | // MIT LICENSE 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | 31 | #import 32 | 33 | #import "TFHppleElement.h" 34 | 35 | @interface TFHpple : NSObject 36 | 37 | - (id) initWithData:(NSData *)theData encoding:(NSString *)encoding isXML:(BOOL)isDataXML; 38 | - (id) initWithData:(NSData *)theData isXML:(BOOL)isDataXML; 39 | - (id) initWithXMLData:(NSData *)theData encoding:(NSString *)encoding; 40 | - (id) initWithXMLData:(NSData *)theData; 41 | - (id) initWithHTMLData:(NSData *)theData encoding:(NSString *)encoding; 42 | - (id) initWithHTMLData:(NSData *)theData; 43 | 44 | + (TFHpple *) hppleWithData:(NSData *)theData encoding:(NSString *)encoding isXML:(BOOL)isDataXML; 45 | + (TFHpple *) hppleWithData:(NSData *)theData isXML:(BOOL)isDataXML; 46 | + (TFHpple *) hppleWithXMLData:(NSData *)theData encoding:(NSString *)encoding; 47 | + (TFHpple *) hppleWithXMLData:(NSData *)theData; 48 | + (TFHpple *) hppleWithHTMLData:(NSData *)theData encoding:(NSString *)encoding; 49 | + (TFHpple *) hppleWithHTMLData:(NSData *)theData; 50 | 51 | - (NSArray *) searchWithXPathQuery:(NSString *)xPathOrCSS; 52 | - (TFHppleElement *) peekAtSearchWithXPathQuery:(NSString *)xPathOrCSS; 53 | 54 | @property (nonatomic, readonly) NSData * data; 55 | @property (nonatomic, readonly) NSString * encoding; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/TFHpple.m: -------------------------------------------------------------------------------- 1 | // 2 | // TFHpple.m 3 | // Hpple 4 | // 5 | // Created by Geoffrey Grosenbach on 1/31/09. 6 | // 7 | // Copyright (c) 2009 Topfunky Corporation, http://topfunky.com 8 | // 9 | // MIT LICENSE 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | #import "TFHpple.h" 31 | #import "XPathQuery.h" 32 | 33 | @interface TFHpple () 34 | { 35 | NSData * data; 36 | NSString * encoding; 37 | BOOL isXML; 38 | } 39 | 40 | @end 41 | 42 | 43 | @implementation TFHpple 44 | 45 | @synthesize data; 46 | @synthesize encoding; 47 | 48 | 49 | - (id) initWithData:(NSData *)theData encoding:(NSString *)theEncoding isXML:(BOOL)isDataXML 50 | { 51 | if (!(self = [super init])) { 52 | return nil; 53 | } 54 | 55 | data = theData; 56 | encoding = theEncoding; 57 | isXML = isDataXML; 58 | 59 | return self; 60 | } 61 | 62 | - (id) initWithData:(NSData *)theData isXML:(BOOL)isDataXML 63 | { 64 | return [self initWithData:theData encoding:nil isXML:isDataXML]; 65 | } 66 | 67 | - (id) initWithXMLData:(NSData *)theData encoding:(NSString *)theEncoding 68 | { 69 | return [self initWithData:theData encoding:theEncoding isXML:YES]; 70 | } 71 | 72 | - (id) initWithXMLData:(NSData *)theData 73 | { 74 | return [self initWithData:theData encoding:nil isXML:YES]; 75 | } 76 | 77 | - (id) initWithHTMLData:(NSData *)theData encoding:(NSString *)theEncoding 78 | { 79 | return [self initWithData:theData encoding:theEncoding isXML:NO]; 80 | } 81 | 82 | - (id) initWithHTMLData:(NSData *)theData 83 | { 84 | return [self initWithData:theData encoding:nil isXML:NO]; 85 | } 86 | 87 | + (TFHpple *) hppleWithData:(NSData *)theData encoding:(NSString *)theEncoding isXML:(BOOL)isDataXML { 88 | return [[[self class] alloc] initWithData:theData encoding:theEncoding isXML:isDataXML]; 89 | } 90 | 91 | + (TFHpple *) hppleWithData:(NSData *)theData isXML:(BOOL)isDataXML { 92 | return [[self class] hppleWithData:theData encoding:nil isXML:isDataXML]; 93 | } 94 | 95 | + (TFHpple *) hppleWithHTMLData:(NSData *)theData encoding:(NSString *)theEncoding { 96 | return [[self class] hppleWithData:theData encoding:theEncoding isXML:NO]; 97 | } 98 | 99 | + (TFHpple *) hppleWithHTMLData:(NSData *)theData { 100 | return [[self class] hppleWithData:theData encoding:nil isXML:NO]; 101 | } 102 | 103 | + (TFHpple *) hppleWithXMLData:(NSData *)theData encoding:(NSString *)theEncoding { 104 | return [[self class] hppleWithData:theData encoding:theEncoding isXML:YES]; 105 | } 106 | 107 | + (TFHpple *) hppleWithXMLData:(NSData *)theData { 108 | return [[self class] hppleWithData:theData encoding:nil isXML:YES]; 109 | } 110 | 111 | #pragma mark - 112 | 113 | // Returns all elements at xPath. 114 | - (NSArray *) searchWithXPathQuery:(NSString *)xPathOrCSS 115 | { 116 | NSArray * detailNodes = nil; 117 | if (isXML) { 118 | detailNodes = PerformXMLXPathQueryWithEncoding(data, xPathOrCSS, encoding); 119 | } else { 120 | detailNodes = PerformHTMLXPathQueryWithEncoding(data, xPathOrCSS, encoding); 121 | } 122 | 123 | NSMutableArray * hppleElements = [NSMutableArray array]; 124 | for (id node in detailNodes) { 125 | [hppleElements addObject:[TFHppleElement hppleElementWithNode:node isXML:isXML withEncoding:encoding]]; 126 | } 127 | return hppleElements; 128 | } 129 | 130 | // Returns first element at xPath 131 | - (TFHppleElement *) peekAtSearchWithXPathQuery:(NSString *)xPathOrCSS 132 | { 133 | NSArray * elements = [self searchWithXPathQuery:xPathOrCSS]; 134 | if ([elements count] >= 1) { 135 | return [elements objectAtIndex:0]; 136 | } 137 | 138 | return nil; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/TFHppleElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // TFHppleElement.h 3 | // Hpple 4 | // 5 | // Created by Geoffrey Grosenbach on 1/31/09. 6 | // 7 | // Copyright (c) 2009 Topfunky Corporation, http://topfunky.com 8 | // 9 | // MIT LICENSE 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | #import 31 | 32 | 33 | @interface TFHppleElement : NSObject 34 | 35 | - (id) initWithNode:(NSDictionary *) theNode isXML:(BOOL)isDataXML withEncoding:(NSString *)theEncoding; 36 | 37 | + (TFHppleElement *) hppleElementWithNode:(NSDictionary *) theNode isXML:(BOOL)isDataXML withEncoding:(NSString *)theEncoding; 38 | 39 | @property (nonatomic, copy, readonly) NSString *raw; 40 | // Returns this tag's innerHTML content. 41 | @property (nonatomic, copy, readonly) NSString *content; 42 | 43 | // Returns the name of the current tag, such as "h3". 44 | @property (nonatomic, copy, readonly) NSString *tagName; 45 | 46 | // Returns tag attributes with name as key and content as value. 47 | // href = 'http://peepcode.com' 48 | // class = 'highlight' 49 | @property (nonatomic, strong, readonly) NSDictionary *attributes; 50 | 51 | // Returns the children of a given node 52 | @property (nonatomic, strong, readonly) NSArray *children; 53 | 54 | // Returns the first child of a given node 55 | @property (nonatomic, strong, readonly) TFHppleElement *firstChild; 56 | 57 | // the parent of a node 58 | @property (nonatomic, unsafe_unretained, readonly) TFHppleElement *parent; 59 | 60 | // Returns YES if the node has any child 61 | // This is more efficient than using the children property since no NSArray is constructed 62 | - (BOOL)hasChildren; 63 | 64 | // Returns YES if this is a text node 65 | - (BOOL)isTextNode; 66 | 67 | // Provides easy access to the content of a specific attribute, 68 | // such as 'href' or 'class'. 69 | - (NSString *) objectForKey:(NSString *) theKey; 70 | 71 | // Returns the children whose tag name equals the given string 72 | // (comparison is performed with NSString's isEqualToString) 73 | // Returns an empty array if no matching child is found 74 | - (NSArray *) childrenWithTagName:(NSString *)tagName; 75 | 76 | // Returns the first child node whose tag name equals the given string 77 | // (comparison is performed with NSString's isEqualToString) 78 | // Returns nil if no matching child is found 79 | - (TFHppleElement *) firstChildWithTagName:(NSString *)tagName; 80 | 81 | // Returns the children whose class equals the given string 82 | // (comparison is performed with NSString's isEqualToString) 83 | // Returns an empty array if no matching child is found 84 | - (NSArray *) childrenWithClassName:(NSString *)className; 85 | 86 | // Returns the first child whose class requals the given string 87 | // (comparison is performed with NSString's isEqualToString) 88 | // Returns nil if no matching child is found 89 | - (TFHppleElement *) firstChildWithClassName:(NSString*)className; 90 | 91 | // Returns the first text node from this element's children 92 | // Returns nil if there is no text node among the children 93 | - (TFHppleElement *) firstTextChild; 94 | 95 | // Returns the string contained by the first text node from this element's children 96 | // Convenience method which can be used instead of firstTextChild.content 97 | - (NSString *) text; 98 | 99 | // Returns elements searched with xpath 100 | - (NSArray *) searchWithXPathQuery:(NSString *)xPathOrCSS; 101 | 102 | // Custom keyed subscripting 103 | - (id)objectForKeyedSubscript:(id)key; 104 | 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/TFHppleElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // TFHppleElement.m 3 | // Hpple 4 | // 5 | // Created by Geoffrey Grosenbach on 1/31/09. 6 | // 7 | // Copyright (c) 2009 Topfunky Corporation, http://topfunky.com 8 | // 9 | // MIT LICENSE 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | 31 | #import "TFHppleElement.h" 32 | #import "XPathQuery.h" 33 | 34 | static NSString * const TFHppleNodeContentKey = @"nodeContent"; 35 | static NSString * const TFHppleNodeNameKey = @"nodeName"; 36 | static NSString * const TFHppleNodeChildrenKey = @"nodeChildArray"; 37 | static NSString * const TFHppleNodeAttributeArrayKey = @"nodeAttributeArray"; 38 | static NSString * const TFHppleNodeAttributeNameKey = @"attributeName"; 39 | 40 | static NSString * const TFHppleTextNodeName = @"text"; 41 | 42 | @interface TFHppleElement () 43 | { 44 | NSDictionary * node; 45 | BOOL isXML; 46 | NSString *encoding; 47 | __unsafe_unretained TFHppleElement *parent; 48 | } 49 | 50 | @property (nonatomic, unsafe_unretained, readwrite) TFHppleElement *parent; 51 | 52 | @end 53 | 54 | @implementation TFHppleElement 55 | @synthesize parent; 56 | 57 | 58 | - (id) initWithNode:(NSDictionary *) theNode isXML:(BOOL)isDataXML withEncoding:(NSString *)theEncoding 59 | { 60 | if (!(self = [super init])) 61 | return nil; 62 | 63 | isXML = isDataXML; 64 | node = theNode; 65 | encoding = theEncoding; 66 | 67 | return self; 68 | } 69 | 70 | + (TFHppleElement *) hppleElementWithNode:(NSDictionary *) theNode isXML:(BOOL)isDataXML withEncoding:(NSString *)theEncoding 71 | { 72 | return [[[self class] alloc] initWithNode:theNode isXML:isDataXML withEncoding:theEncoding]; 73 | } 74 | 75 | #pragma mark - 76 | 77 | - (NSString *)raw 78 | { 79 | return [node objectForKey:@"raw"]; 80 | } 81 | 82 | - (NSString *) content 83 | { 84 | return [node objectForKey:TFHppleNodeContentKey]; 85 | } 86 | 87 | 88 | - (NSString *) tagName 89 | { 90 | return [node objectForKey:TFHppleNodeNameKey]; 91 | } 92 | 93 | - (NSArray *) children 94 | { 95 | NSMutableArray *children = [NSMutableArray array]; 96 | for (NSDictionary *child in [node objectForKey:TFHppleNodeChildrenKey]) { 97 | TFHppleElement *element = [TFHppleElement hppleElementWithNode:child isXML:isXML withEncoding:encoding]; 98 | element.parent = self; 99 | [children addObject:element]; 100 | } 101 | return children; 102 | } 103 | 104 | - (TFHppleElement *) firstChild 105 | { 106 | NSArray * children = self.children; 107 | if (children.count) 108 | return [children objectAtIndex:0]; 109 | return nil; 110 | } 111 | 112 | 113 | - (NSDictionary *) attributes 114 | { 115 | NSMutableDictionary * translatedAttributes = [NSMutableDictionary dictionary]; 116 | for (NSDictionary * attributeDict in [node objectForKey:TFHppleNodeAttributeArrayKey]) { 117 | if ([attributeDict objectForKey:TFHppleNodeContentKey] && [attributeDict objectForKey:TFHppleNodeAttributeNameKey]) { 118 | [translatedAttributes setObject:[attributeDict objectForKey:TFHppleNodeContentKey] 119 | forKey:[attributeDict objectForKey:TFHppleNodeAttributeNameKey]]; 120 | } 121 | } 122 | return translatedAttributes; 123 | } 124 | 125 | - (NSString *) objectForKey:(NSString *) theKey 126 | { 127 | return [[self attributes] objectForKey:theKey]; 128 | } 129 | 130 | - (id) description 131 | { 132 | return [node description]; 133 | } 134 | 135 | - (BOOL)hasChildren 136 | { 137 | if ([node objectForKey:TFHppleNodeChildrenKey]) 138 | return YES; 139 | else 140 | return NO; 141 | } 142 | 143 | - (BOOL)isTextNode 144 | { 145 | // we must distinguish between real text nodes and standard nodes with tha name "text" () 146 | // real text nodes must have content 147 | if ([self.tagName isEqualToString:TFHppleTextNodeName] && (self.content)) 148 | return YES; 149 | else 150 | return NO; 151 | } 152 | 153 | - (NSArray*) childrenWithTagName:(NSString*)tagName 154 | { 155 | NSMutableArray* matches = [NSMutableArray array]; 156 | 157 | for (TFHppleElement* child in self.children) 158 | { 159 | if ([child.tagName isEqualToString:tagName]) 160 | [matches addObject:child]; 161 | } 162 | 163 | return matches; 164 | } 165 | 166 | - (TFHppleElement *) firstChildWithTagName:(NSString*)tagName 167 | { 168 | for (TFHppleElement* child in self.children) 169 | { 170 | if ([child.tagName isEqualToString:tagName]) 171 | return child; 172 | } 173 | 174 | return nil; 175 | } 176 | 177 | - (NSArray*) childrenWithClassName:(NSString*)className 178 | { 179 | NSMutableArray* matches = [NSMutableArray array]; 180 | 181 | for (TFHppleElement* child in self.children) 182 | { 183 | if ([[child objectForKey:@"class"] isEqualToString:className]) 184 | [matches addObject:child]; 185 | } 186 | 187 | return matches; 188 | } 189 | 190 | - (TFHppleElement *) firstChildWithClassName:(NSString*)className 191 | { 192 | for (TFHppleElement* child in self.children) 193 | { 194 | if ([[child objectForKey:@"class"] isEqualToString:className]) 195 | return child; 196 | } 197 | 198 | return nil; 199 | } 200 | 201 | - (TFHppleElement *) firstTextChild 202 | { 203 | for (TFHppleElement* child in self.children) 204 | { 205 | if ([child isTextNode]) 206 | return child; 207 | } 208 | 209 | return [self firstChildWithTagName:TFHppleTextNodeName]; 210 | } 211 | 212 | - (NSString *) text 213 | { 214 | return self.firstTextChild.content; 215 | } 216 | 217 | // Returns all elements at xPath. 218 | - (NSArray *) searchWithXPathQuery:(NSString *)xPathOrCSS 219 | { 220 | 221 | NSData *data = [self.raw dataUsingEncoding:NSUTF8StringEncoding]; 222 | 223 | NSArray * detailNodes = nil; 224 | if (isXML) { 225 | detailNodes = PerformXMLXPathQueryWithEncoding(data, xPathOrCSS, encoding); 226 | } else { 227 | detailNodes = PerformHTMLXPathQueryWithEncoding(data, xPathOrCSS, encoding); 228 | } 229 | 230 | NSMutableArray * hppleElements = [NSMutableArray array]; 231 | for (id newNode in detailNodes) { 232 | [hppleElements addObject:[TFHppleElement hppleElementWithNode:newNode isXML:isXML withEncoding:encoding]]; 233 | } 234 | return hppleElements; 235 | } 236 | 237 | // Custom keyed subscripting 238 | - (id)objectForKeyedSubscript:(id)key 239 | { 240 | return [self objectForKey:key]; 241 | } 242 | 243 | @end 244 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/XPathQuery.h: -------------------------------------------------------------------------------- 1 | // 2 | // XPathQuery.h 3 | // FuelFinder 4 | // 5 | // Created by Matt Gallagher on 4/08/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NSArray *PerformHTMLXPathQuery(NSData *document, NSString *query); 12 | NSArray *PerformHTMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding); 13 | NSArray *PerformXMLXPathQuery(NSData *document, NSString *query); 14 | NSArray *PerformXMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding); 15 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Hpple/XPathQuery.m: -------------------------------------------------------------------------------- 1 | // 2 | // XPathQuery.m 3 | // FuelFinder 4 | // 5 | // Created by Matt Gallagher on 4/08/08. 6 | // Copyright 2008 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "XPathQuery.h" 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult,BOOL parentContent); 18 | NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query); 19 | 20 | NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult,BOOL parentContent) 21 | { 22 | NSMutableDictionary *resultForNode = [NSMutableDictionary dictionary]; 23 | if (currentNode->name) { 24 | NSString *currentNodeContent = [NSString stringWithCString:(const char *)currentNode->name 25 | encoding:NSUTF8StringEncoding]; 26 | resultForNode[@"nodeName"] = currentNodeContent; 27 | } 28 | 29 | xmlChar *nodeContent = xmlNodeGetContent(currentNode); 30 | if (nodeContent != NULL) { 31 | NSString *currentNodeContent = [NSString stringWithCString:(const char *)nodeContent 32 | encoding:NSUTF8StringEncoding]; 33 | if ([resultForNode[@"nodeName"] isEqual:@"text"] && parentResult) { 34 | if (parentContent) { 35 | NSCharacterSet *charactersToTrim = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 36 | parentResult[@"nodeContent"] = [currentNodeContent stringByTrimmingCharactersInSet:charactersToTrim]; 37 | return nil; 38 | } 39 | if (currentNodeContent != nil) { 40 | resultForNode[@"nodeContent"] = currentNodeContent; 41 | } 42 | return resultForNode; 43 | } else { 44 | resultForNode[@"nodeContent"] = currentNodeContent; 45 | } 46 | xmlFree(nodeContent); 47 | } 48 | 49 | xmlAttr *attribute = currentNode->properties; 50 | if (attribute) { 51 | NSMutableArray *attributeArray = [NSMutableArray array]; 52 | while (attribute) { 53 | NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary]; 54 | NSString *attributeName = [NSString stringWithCString:(const char *)attribute->name 55 | encoding:NSUTF8StringEncoding]; 56 | if (attributeName) { 57 | attributeDictionary[@"attributeName"] = attributeName; 58 | } 59 | 60 | if (attribute->children) { 61 | NSDictionary *childDictionary = DictionaryForNode(attribute->children, attributeDictionary, true); 62 | if (childDictionary) { 63 | attributeDictionary[@"attributeContent"] = childDictionary; 64 | } 65 | } 66 | 67 | if ([attributeDictionary count] > 0) { 68 | [attributeArray addObject:attributeDictionary]; 69 | } 70 | attribute = attribute->next; 71 | } 72 | 73 | if ([attributeArray count] > 0) { 74 | resultForNode[@"nodeAttributeArray"] = attributeArray; 75 | } 76 | } 77 | 78 | xmlNodePtr childNode = currentNode->children; 79 | if (childNode) { 80 | NSMutableArray *childContentArray = [NSMutableArray array]; 81 | while (childNode) { 82 | NSDictionary *childDictionary = DictionaryForNode(childNode, resultForNode,false); 83 | if (childDictionary) { 84 | [childContentArray addObject:childDictionary]; 85 | } 86 | childNode = childNode->next; 87 | } 88 | if ([childContentArray count] > 0) { 89 | resultForNode[@"nodeChildArray"] = childContentArray; 90 | } 91 | } 92 | 93 | xmlBufferPtr buffer = xmlBufferCreate(); 94 | xmlNodeDump(buffer, currentNode->doc, currentNode, 0, 0); 95 | NSString *rawContent = [NSString stringWithCString:(const char *)buffer->content encoding:NSUTF8StringEncoding]; 96 | if (rawContent != nil) { 97 | resultForNode[@"raw"] = rawContent; 98 | } 99 | xmlBufferFree(buffer); 100 | return resultForNode; 101 | } 102 | 103 | NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query) 104 | { 105 | xmlXPathContextPtr xpathCtx; 106 | xmlXPathObjectPtr xpathObj; 107 | 108 | /* Make sure that passed query is non-nil and is NSString object */ 109 | if (query == nil || ![query isKindOfClass:[NSString class]]) { 110 | return nil; 111 | } 112 | 113 | /* Create xpath evaluation context */ 114 | xpathCtx = xmlXPathNewContext(doc); 115 | if(xpathCtx == NULL) { 116 | NSLog(@"Unable to create XPath context."); 117 | return nil; 118 | } 119 | 120 | /* Evaluate xpath expression */ 121 | xpathObj = xmlXPathEvalExpression((xmlChar *)[query cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx); 122 | if(xpathObj == NULL) { 123 | NSLog(@"Unable to evaluate XPath."); 124 | xmlXPathFreeContext(xpathCtx); 125 | return nil; 126 | } 127 | 128 | xmlNodeSetPtr nodes = xpathObj->nodesetval; 129 | if (!nodes) { 130 | NSLog(@"Nodes was nil."); 131 | xmlXPathFreeObject(xpathObj); 132 | xmlXPathFreeContext(xpathCtx); 133 | return nil; 134 | } 135 | 136 | NSMutableArray *resultNodes = [NSMutableArray array]; 137 | for (NSInteger i = 0; i < nodes->nodeNr; i++) { 138 | NSDictionary *nodeDictionary = DictionaryForNode(nodes->nodeTab[i], nil,false); 139 | if (nodeDictionary) { 140 | [resultNodes addObject:nodeDictionary]; 141 | } 142 | } 143 | 144 | /* Cleanup */ 145 | xmlXPathFreeObject(xpathObj); 146 | xmlXPathFreeContext(xpathCtx); 147 | 148 | return resultNodes; 149 | } 150 | 151 | NSArray *PerformHTMLXPathQuery(NSData *document, NSString *query) { 152 | return PerformHTMLXPathQueryWithEncoding(document, query, nil); 153 | } 154 | 155 | NSArray *PerformHTMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding) 156 | { 157 | xmlDocPtr doc; 158 | 159 | /* Load XML document */ 160 | const char *encoded = encoding ? [encoding cStringUsingEncoding:NSUTF8StringEncoding] : NULL; 161 | 162 | doc = htmlReadMemory([document bytes], (int)[document length], "", encoded, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); 163 | if (doc == NULL) { 164 | NSLog(@"Unable to parse."); 165 | return nil; 166 | } 167 | 168 | NSArray *result = PerformXPathQuery(doc, query); 169 | xmlFreeDoc(doc); 170 | 171 | return result; 172 | } 173 | 174 | NSArray *PerformXMLXPathQuery(NSData *document, NSString *query) { 175 | return PerformXMLXPathQueryWithEncoding(document, query, nil); 176 | } 177 | 178 | NSArray *PerformXMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding) 179 | { 180 | xmlDocPtr doc; 181 | 182 | /* Load XML document */ 183 | const char *encoded = encoding ? [encoding cStringUsingEncoding:NSUTF8StringEncoding] : NULL; 184 | 185 | doc = xmlReadMemory([document bytes], (int)[document length], "", encoded, XML_PARSE_RECOVER); 186 | 187 | if (doc == NULL) { 188 | NSLog(@"Unable to parse."); 189 | return nil; 190 | } 191 | 192 | NSArray *result = PerformXPathQuery(doc, query); 193 | xmlFreeDoc(doc); 194 | 195 | return result; 196 | } 197 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UIRequiredDeviceCapabilities 31 | 32 | armv7 33 | 34 | UISupportedInterfaceOrientations 35 | 36 | UIInterfaceOrientationPortrait 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/Model/HXArticle.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticle.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/22. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXArticle : NSObject 12 | 13 | @property (nonatomic,copy) NSString *title; 14 | @property (nonatomic,copy) NSString *sketch; 15 | @property (nonatomic,copy) NSString *url; 16 | @property (nonatomic,copy) NSString *time; 17 | @property (nonatomic,copy) NSString *recommendcount; 18 | @property (nonatomic,copy) NSString *commentcount; 19 | @property (nonatomic,copy) NSString *readcount; 20 | @property (nonatomic,copy) NSString *homepage; 21 | @property (nonatomic,copy) NSString *name; 22 | @property (nonatomic,copy) NSString *findCount; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/Model/HXArticle.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticle.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/22. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXArticle.h" 10 | 11 | @implementation HXArticle 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXArticleListCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticleListCell.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/20. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HXArticle.h" 11 | 12 | @class HXArticleListCell; 13 | 14 | @protocol HXArticleListCellDelegate 15 | 16 | - (void)articleListCell:(HXArticleListCell *)cell titleButtonAction:(UIButton *)sender article:(HXArticle *)article; 17 | - (void)articleListCell:(HXArticleListCell *)cell nameButtonAction:(UIButton *)sender article:(HXArticle *)article; 18 | 19 | @end 20 | 21 | @interface HXArticleListCell : UITableViewCell 22 | 23 | @property (nonatomic,weak) id delegate; 24 | @property (weak, nonatomic) IBOutlet UIButton *titleButton; 25 | @property (weak, nonatomic) IBOutlet UILabel *sketchLabel; 26 | @property (weak, nonatomic) IBOutlet UIButton *nameButton; 27 | @property (weak, nonatomic) IBOutlet UILabel *timeLabel; 28 | @property (weak, nonatomic) IBOutlet UILabel *countLabel; 29 | 30 | - (void)setData:(id)data key:(NSString *)key delegate:(id)delegate; 31 | 32 | + (float)getCellFrame:(id)msg width:(float)width; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXArticleListCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticleListCell.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/20. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXArticleListCell.h" 10 | 11 | @interface HXArticleListCell () 12 | 13 | @property (nonatomic,strong) HXArticle *article; 14 | 15 | @end 16 | 17 | @implementation HXArticleListCell 18 | 19 | - (void)awakeFromNib { 20 | // Initialization code 21 | [super awakeFromNib]; 22 | } 23 | 24 | - (void)setData:(id)data key:(NSString *)key delegate:(id)delegate { 25 | if ([data isKindOfClass:[HXArticle class]]) { 26 | _delegate = delegate; 27 | _article = (HXArticle *)data; 28 | //标题 29 | [self.titleButton setAttributedTitle:[self getTitleAttributedString:_article.title key:key] forState:UIControlStateNormal]; 30 | //简述 31 | self.sketchLabel.attributedText = [self getContentAttributedString:_article.sketch key:key]; 32 | //作者 33 | [self.nameButton setTitle:_article.name forState:UIControlStateNormal]; 34 | //时间 35 | self.timeLabel.text = [NSString stringWithFormat:@"发布于 %@",_article.time]; 36 | 37 | NSMutableString *str = [NSMutableString stringWithString:@""]; 38 | 39 | //推荐数 40 | if (_article.recommendcount.integerValue > 0) { 41 | [str appendFormat:@"推荐(%@)",_article.recommendcount]; 42 | } 43 | 44 | //评论数 45 | if (_article.commentcount.integerValue > 0) { 46 | if (_article.recommendcount.integerValue > 0) { 47 | [str appendString:@" "]; 48 | } 49 | [str appendFormat:@"评论(%@)",_article.commentcount]; 50 | } 51 | 52 | //阅读数 53 | if (_article.readcount.integerValue > 0) { 54 | if (_article.commentcount.integerValue > 0) { 55 | [str appendString:@" "]; 56 | } 57 | [str appendFormat:@"阅读(%@)",_article.readcount]; 58 | } 59 | 60 | self.countLabel.text = str; 61 | } 62 | } 63 | 64 | #pragma mark - 文字样式处理 65 | 66 | //获取内容样式 关键字标亮 67 | - (NSMutableAttributedString *)getContentAttributedString:(NSString *)content key:(NSString *)key { 68 | 69 | NSMutableAttributedString *titleStr = [[NSMutableAttributedString alloc] initWithString:content]; 70 | 71 | NSString *copyStr = content; 72 | 73 | NSMutableString *xxstr = [NSMutableString new]; 74 | for (int i = 0; i < key.length; i++) { 75 | [xxstr appendString:@"*"]; 76 | } 77 | 78 | while ([copyStr rangeOfString:key options:NSCaseInsensitiveSearch].location != NSNotFound) { 79 | 80 | NSRange range = [copyStr rangeOfString:key options:NSCaseInsensitiveSearch]; 81 | 82 | //关键字颜色 83 | UIColor *keyColor = [UIColor redColor]; 84 | 85 | [titleStr addAttribute:NSForegroundColorAttributeName value:keyColor range:range]; 86 | 87 | copyStr = [copyStr stringByReplacingCharactersInRange:NSMakeRange(range.location, range.length) withString:xxstr]; 88 | } 89 | return titleStr; 90 | } 91 | 92 | //获取标题样式 标题下划线 关键字标亮 93 | - (NSMutableAttributedString *)getTitleAttributedString:(NSString *)title key:(NSString *)key { 94 | //标题样式 95 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:title]; 96 | 97 | //下划线颜色 98 | UIColor *undlineColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:204/255.0 alpha:1.0]; 99 | //下划线颜色和位置 100 | [attributedString addAttribute:NSForegroundColorAttributeName value:undlineColor range:NSMakeRange(0,title.length)]; 101 | //下划线样式 102 | [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0,title.length)]; 103 | 104 | NSString *copyStr = title; 105 | 106 | NSMutableString *xxstr = [NSMutableString new]; 107 | for (int i = 0; i < key.length; i++) { 108 | [xxstr appendString:@"*"]; 109 | } 110 | 111 | while ([copyStr rangeOfString:key options:NSCaseInsensitiveSearch].location != NSNotFound) { 112 | 113 | NSRange range = [copyStr rangeOfString:key options:NSCaseInsensitiveSearch]; 114 | //关键字颜色 115 | UIColor *keyColor = [UIColor redColor]; 116 | [attributedString addAttribute:NSForegroundColorAttributeName value:keyColor range:range]; 117 | 118 | copyStr = [copyStr stringByReplacingCharactersInRange:NSMakeRange(range.location, range.length) withString:xxstr]; 119 | } 120 | 121 | return attributedString; 122 | } 123 | 124 | - (IBAction)titleButtonAction:(UIButton *)sender { 125 | if (_delegate && [_delegate respondsToSelector:@selector(articleListCell:titleButtonAction:article:)]) { 126 | [_delegate articleListCell:self titleButtonAction:sender article:_article]; 127 | } 128 | } 129 | 130 | - (IBAction)nameButtonAction:(UIButton *)sender { 131 | if (_delegate && [_delegate respondsToSelector:@selector(articleListCell:nameButtonAction:article:)]) { 132 | [_delegate articleListCell:self nameButtonAction:sender article:_article]; 133 | } 134 | } 135 | 136 | + (float)getCellFrame:(id)msg width:(float)width { 137 | if ([msg isKindOfClass:[HXArticle class]]) { 138 | HXArticle *article = (HXArticle *)msg; 139 | NSString *str = article.sketch; 140 | CGRect frame = [str boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12]} context:nil]; 141 | return 77 + frame.size.height - 12; 142 | } 143 | return 77; 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryCell.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class HXHistoryCell; 12 | 13 | @protocol HXHistoryCellDelegate 14 | 15 | - (void)historyCell:(HXHistoryCell *)cell deleteButtonAction:(UIButton *)sender history:(NSString *)history; 16 | 17 | @end 18 | 19 | @interface HXHistoryCell : UITableViewCell 20 | 21 | @property (nonatomic,weak) id delegate; 22 | @property (weak, nonatomic) IBOutlet UILabel *historyLabel; 23 | 24 | - (void)setData:(id)data delegate:(id)delegate; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryCell.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXHistoryCell.h" 10 | 11 | @interface HXHistoryCell () 12 | 13 | @property (nonatomic,strong) NSString *history; 14 | 15 | @end 16 | 17 | @implementation HXHistoryCell 18 | 19 | - (void)awakeFromNib { 20 | // Initialization code 21 | [super awakeFromNib]; 22 | } 23 | 24 | - (void)setData:(id)data delegate:(id)delegate { 25 | if ([data isKindOfClass:[NSString class]]) { 26 | _delegate = delegate; 27 | _history = (NSString *)data; 28 | 29 | self.historyLabel.text = _history; 30 | } 31 | } 32 | 33 | - (IBAction)deleteButtonAction:(UIButton *)sender { 34 | if (_delegate && [_delegate respondsToSelector:@selector(historyCell:deleteButtonAction:history:)]) { 35 | [_delegate historyCell:self deleteButtonAction:sender history:_history]; 36 | } 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryLastCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryLastCell.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class HXHistoryLastCell; 12 | 13 | @protocol HXHistoryLastCellDelegate 14 | 15 | - (void)historyLastCell:(HXHistoryLastCell *)cell deleteButtonAction:(UIButton *)sender; 16 | - (void)historyLastCell:(HXHistoryLastCell *)cell moreButtonAction:(UIButton *)sender; 17 | 18 | @end 19 | 20 | @interface HXHistoryLastCell : UITableViewCell 21 | 22 | @property (nonatomic,weak) id delegate; 23 | 24 | - (void)setData:(id)data delegate:(id)delegate; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryLastCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryLastCell.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXHistoryLastCell.h" 10 | 11 | @interface HXHistoryLastCell () 12 | 13 | @property (nonatomic,strong) NSString *history; 14 | 15 | @end 16 | 17 | @implementation HXHistoryLastCell 18 | 19 | - (void)awakeFromNib { 20 | // Initialization code 21 | [super awakeFromNib]; 22 | } 23 | 24 | - (void)setData:(id)data delegate:(id)delegate { 25 | _delegate = delegate; 26 | } 27 | 28 | - (IBAction)deleteButtonAction:(UIButton *)sender { 29 | if (_delegate && [_delegate respondsToSelector:@selector(historyLastCell:deleteButtonAction:)]) { 30 | [_delegate historyLastCell:self deleteButtonAction:sender]; 31 | } 32 | } 33 | 34 | - (IBAction)moreButtonAction:(UIButton *)sender { 35 | if (_delegate && [_delegate respondsToSelector:@selector(historyLastCell:deleteButtonAction:)]) { 36 | [_delegate historyLastCell:self moreButtonAction:sender]; 37 | } 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/View/HXHistoryLastCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 31 | 45 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/Detail/HXDetailWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXDetailWebViewController.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/20. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HXArticle.h" 11 | 12 | @interface HXDetailWebViewController : UIViewController 13 | 14 | @property (strong, nonatomic) HXArticle *article; 15 | @property (copy, nonatomic) NSString *type; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/Detail/HXDetailWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXDetailWebViewController.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/20. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXDetailWebViewController.h" 10 | 11 | @interface HXDetailWebViewController () 12 | 13 | @property (weak, nonatomic) IBOutlet UIWebView *webView; 14 | 15 | @end 16 | 17 | @implementation HXDetailWebViewController 18 | 19 | 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view from its nib. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | if ([_type isEqualToString:@"home"]) { 27 | self.title = _article.name; 28 | 29 | //1.创建并加载远程网页 30 | [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:_article.homepage]]]; 31 | } else if ([_type isEqualToString:@"detail"]) { 32 | self.title = _article.title; 33 | 34 | //1.创建并加载远程网页 35 | [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:_article.url]]]; 36 | } 37 | } 38 | 39 | - (void)didReceiveMemoryWarning { 40 | [super didReceiveMemoryWarning]; 41 | // Dispose of any resources that can be recreated. 42 | 43 | 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/Detail/HXDetailWebViewController.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/Home/HXHomeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXHomeViewController.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/22. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXHomeViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/Home/HXHomeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXHomeViewController.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/22. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXHomeViewController.h" 10 | #import "HXSearchResultViewController.h" 11 | #import "HXHistoryListViewController.h" 12 | #import "HXHistoryCell.h" 13 | #import "HXHistoryLastCell.h" 14 | 15 | @interface HXHomeViewController () 16 | 17 | @property (weak, nonatomic) IBOutlet UITextField *keyTextField; 18 | @property (weak, nonatomic) IBOutlet UIView *keyView; 19 | @property (weak, nonatomic) IBOutlet UIButton *searchButton; 20 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 21 | @property (nonatomic,strong) NSMutableArray *tagAry; 22 | @property (nonatomic,assign) float maxHeight; 23 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewHeight; 24 | 25 | @end 26 | 27 | @implementation HXHomeViewController 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | // Do any additional setup after loading the view from its nib. 32 | self.view.backgroundColor = [UIColor whiteColor]; 33 | 34 | _keyView.layer.borderWidth = 1.0; 35 | _keyView.layer.borderColor = [UIColor colorWithRed:204/255.0 green:204/255.0 blue:204/255.0 alpha:1.0].CGColor; 36 | _tableView.layer.borderWidth = 1.0; 37 | _tableView.layer.borderColor = [UIColor colorWithRed:204/255.0 green:204/255.0 blue:204/255.0 alpha:1.0].CGColor; 38 | [_tableView registerNib:[UINib nibWithNibName:@"HXHistoryCell" bundle:nil] forCellReuseIdentifier:@"HXHistoryCell"]; 39 | [_tableView registerNib:[UINib nibWithNibName:@"HXHistoryLastCell" bundle:nil] forCellReuseIdentifier:@"HXHistoryLastCell"]; 40 | 41 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 42 | NSArray *ary = [userDefaults objectForKey:@"tagAry"]; 43 | _tagAry = [NSMutableArray arrayWithArray:ary]; 44 | 45 | _maxHeight = [UIScreen mainScreen].bounds.size.height - _tableView.frame.origin.y; 46 | 47 | [self changeTableViewHeight]; 48 | 49 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; 50 | 51 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil]; 52 | } 53 | 54 | - (void)viewWillAppear:(BOOL)animated { 55 | [super viewWillAppear:animated]; 56 | self.navigationController.navigationBarHidden = YES; 57 | } 58 | 59 | - (void)viewWillDisappear:(BOOL)animated { 60 | [super viewWillDisappear:animated]; 61 | self.navigationController.navigationBarHidden = NO; 62 | } 63 | 64 | #pragma mark - data 65 | 66 | - (NSArray *)dataSource { 67 | return _tagAry; 68 | } 69 | 70 | #pragma mark UITableViewDataSource/UITableViewDelegate 71 | 72 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 73 | if (self.dataSource.count > 9) { 74 | return 9 + 1; 75 | } 76 | return self.dataSource.count + 1; 77 | } 78 | 79 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 80 | 81 | if (indexPath.row == self.dataSource.count || indexPath.row == 9) { 82 | //最后一行 83 | HXHistoryLastCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HXHistoryLastCell"]; 84 | if (!cell) { 85 | cell = [[HXHistoryLastCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"HXHistoryLastCell"]; 86 | } 87 | [cell setData:nil delegate:self]; 88 | return cell; 89 | } else { 90 | HXHistoryCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HXHistoryCell"]; 91 | if (!cell) { 92 | cell = [[HXHistoryCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"HXHistoryCell"]; 93 | } 94 | 95 | NSString *str = _tagAry[self.dataSource.count - indexPath.row - 1]; 96 | [cell setData:str delegate:self]; 97 | 98 | return cell; 99 | } 100 | } 101 | 102 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 103 | return 20; 104 | } 105 | 106 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 107 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 108 | 109 | _tableView.hidden = YES; 110 | 111 | NSString *str = _tagAry[self.dataSource.count - indexPath.row - 1]; 112 | 113 | if (str.length > 0) { 114 | 115 | [self gotoSearchResultViewController:str]; 116 | } 117 | } 118 | 119 | #pragma mark - HXHistoryCellDelegate 120 | 121 | - (void)historyCell:(HXHistoryCell *)cell deleteButtonAction:(UIButton *)sender history:(NSString *)history { 122 | [_tagAry removeObject:history]; 123 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 124 | [userDefaults setObject:_tagAry forKey:@"tagAry"]; 125 | [userDefaults synchronize]; 126 | [self changeTableViewHeight]; 127 | [_tableView reloadData]; 128 | } 129 | 130 | #pragma mark - HXHistoryLastCellDelegate 131 | 132 | - (void)historyLastCell:(HXHistoryLastCell *)cell deleteButtonAction:(UIButton *)sender { 133 | [_tagAry removeAllObjects]; 134 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 135 | [userDefaults setObject:_tagAry forKey:@"tagAry"]; 136 | [userDefaults synchronize]; 137 | _tableView.hidden = YES; 138 | } 139 | 140 | - (void)historyLastCell:(HXHistoryLastCell *)cell moreButtonAction:(UIButton *)sender { 141 | [self gotoHistoryListViewController]; 142 | } 143 | 144 | #pragma mark - Text 145 | 146 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { 147 | if (_tagAry.count > 0) { 148 | _tableView.hidden = NO; 149 | } 150 | return YES; 151 | } 152 | 153 | - (IBAction)tapButtonAction:(UITapGestureRecognizer *)sender { 154 | _tableView.hidden = YES; 155 | [self.view endEditing:YES]; 156 | } 157 | 158 | 159 | - (void)keyboardWasShown:(NSNotification *)notif 160 | { 161 | NSDictionary *info = [notif userInfo]; 162 | NSValue *value = [info objectForKey:UIKeyboardFrameBeginUserInfoKey]; 163 | CGSize keyboardSize = [value CGRectValue].size; 164 | NSLog(@"keyBoard:%f", keyboardSize.height); //216 165 | 166 | _maxHeight = [value CGRectValue].origin.y - _tableView.frame.origin.y; 167 | [self changeTableViewHeight]; 168 | } 169 | 170 | - (void)keyboardWasHidden:(NSNotification *)notif 171 | { 172 | NSDictionary *info = [notif userInfo]; 173 | 174 | NSValue *value = [info objectForKey:UIKeyboardFrameBeginUserInfoKey]; 175 | CGSize keyboardSize = [value CGRectValue].size; 176 | NSLog(@"keyboardWasHidden keyBoard:%f", keyboardSize.height); 177 | 178 | _maxHeight = [value CGRectValue].origin.y - _tableView.frame.origin.y; 179 | [self changeTableViewHeight]; 180 | } 181 | 182 | - (void)changeTableViewHeight { 183 | if (_tagAry.count > 0 && _keyTextField.isFirstResponder) { 184 | _tableView.hidden = NO; 185 | } 186 | 187 | if (_tagAry.count >= 9) { 188 | if (_maxHeight > 20*10) { 189 | _tableViewHeight.constant = 200; 190 | } else { 191 | _tableViewHeight.constant = _maxHeight; 192 | } 193 | } else { 194 | if (_maxHeight > 20*_tagAry.count) { 195 | _tableViewHeight.constant = 20*(_tagAry.count + 1); 196 | } else { 197 | _tableViewHeight.constant = _maxHeight; 198 | } 199 | } 200 | } 201 | 202 | #pragma mark - Button Action 203 | 204 | - (IBAction)searchButtonAction:(UIButton *)sender { 205 | if (_keyTextField.text.length > 0) { 206 | if (![_tagAry containsObject:_keyTextField.text]) { 207 | [_tagAry addObject:_keyTextField.text]; 208 | 209 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 210 | [userDefaults setObject:_tagAry forKey:@"tagAry"]; 211 | [userDefaults synchronize]; 212 | 213 | [self changeTableViewHeight]; 214 | 215 | [_tableView reloadData]; 216 | } 217 | [self gotoSearchResultViewController:_keyTextField.text]; 218 | } 219 | } 220 | 221 | #pragma mark - goto 222 | 223 | - (void)gotoSearchResultViewController:(NSString *)key { 224 | HXSearchResultViewController *vc = [HXSearchResultViewController new]; 225 | vc.key = key; 226 | [self.navigationController pushViewController:vc animated:YES]; 227 | } 228 | 229 | /** 230 | * 去历史记录页面 231 | */ 232 | - (void)gotoHistoryListViewController { 233 | HXHistoryListViewController *vc = [HXHistoryListViewController new]; 234 | [self.navigationController pushViewController:vc animated:YES]; 235 | } 236 | 237 | - (void)didReceiveMemoryWarning { 238 | [super didReceiveMemoryWarning]; 239 | // Dispose of any resources that can be recreated. 240 | } 241 | 242 | - (void)dealloc { 243 | [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:UIKeyboardDidShowNotification]; 244 | [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:UIKeyboardDidHideNotification]; 245 | } 246 | 247 | @end 248 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXArticleListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticleListViewController.h 3 | // https://github.com/huangxuan518/HXInternationalizationDemo 4 | // 5 | // Created by 黄轩 on 16/7/29. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXArticleListViewController : UIViewController 12 | 13 | @property (nonatomic,copy) NSString *key; //搜索关键字 14 | @property (nonatomic,copy) NSString *blogType; //博客类型 1.@"csdn" CSDN博客 2.@"cnblogs" 博客园 3.@"51cto" 51CTO博客 4.@"oschina" 开源中国博客 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXArticleListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXArticleListViewController.m 3 | // https://github.com/huangxuan518/HXInternationalizationDemo 4 | // 5 | // Created by 黄轩 on 16/7/29. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXArticleListViewController.h" 10 | #import "HXDetailWebViewController.h" 11 | #import "HXArticleListCell.h" 12 | #import "HXArticle.h" 13 | #import "HXRequestManager.h" 14 | 15 | #import 16 | 17 | @interface HXArticleListViewController () 18 | 19 | @property (nonatomic,strong) UITableView *tableView; 20 | @property (nonatomic,copy) NSString *searchText;//搜索词 21 | @property (nonatomic,strong) UILabel *findCountLabel; 22 | @property (nonatomic,strong) NSMutableArray *dataAry; 23 | @property (nonatomic,strong) HXRequestManager *requestManager; 24 | 25 | @property (nonatomic,assign) NSInteger page; 26 | 27 | @end 28 | 29 | @implementation HXArticleListViewController 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | // Do any additional setup after loading the view. 34 | self.view.backgroundColor = [UIColor clearColor]; 35 | 36 | _page = 1; 37 | _dataAry = [NSMutableArray new]; 38 | 39 | [self.view addSubview:self.tableView]; 40 | 41 | __weak __typeof(self)weakSelf = self; 42 | self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ 43 | __strong __typeof(self)self = weakSelf; 44 | //下拉刷新 45 | _page = 1; 46 | [self request:_page]; 47 | }]; 48 | 49 | self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ 50 | __strong __typeof(self)self = weakSelf; 51 | //加载更多 52 | _page++; 53 | [self request:_page]; 54 | }]; 55 | 56 | [self.tableView.mj_header beginRefreshing]; 57 | } 58 | 59 | - (UILabel *)findCountLabel { 60 | if (!_findCountLabel) { 61 | _findCountLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 30, self.view.frame.size.width, 30)]; 62 | _findCountLabel.backgroundColor = [UIColor grayColor]; 63 | _findCountLabel.textAlignment = NSTextAlignmentCenter; 64 | _findCountLabel.font = [UIFont systemFontOfSize:12]; 65 | _findCountLabel.textColor = [UIColor whiteColor]; 66 | [self.view addSubview:_findCountLabel]; 67 | } 68 | _findCountLabel.frame = CGRectMake(0, self.view.frame.size.height - 30, self.view.frame.size.width, 30); 69 | return _findCountLabel; 70 | } 71 | 72 | - (void)request:(NSInteger)page { 73 | __weak __typeof(self)weakSelf = self; 74 | [self.requestManager requestWithBlogType:_blogType key:_key page:page complete:^(NSArray *dataAry,NSString *findCount) { 75 | __strong __typeof(self)self = weakSelf; 76 | 77 | [self.tableView.mj_header endRefreshing]; 78 | [self.tableView.mj_footer endRefreshing]; 79 | 80 | if (page == 1) { 81 | self.findCountLabel.text = [NSString stringWithFormat:@"为您找到相关结果约%@个",findCount]; 82 | [_dataAry removeAllObjects]; 83 | } 84 | 85 | [_dataAry addObjectsFromArray:dataAry]; 86 | [self.tableView reloadData]; 87 | }]; 88 | } 89 | 90 | #pragma mark - data 91 | 92 | - (NSArray *)dataSource { 93 | return _dataAry; 94 | } 95 | 96 | #pragma mark UITableViewDataSource/UITableViewDelegate 97 | 98 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 99 | return self.dataSource.count; 100 | } 101 | 102 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 103 | 104 | HXArticleListCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HXArticleListCell"]; 105 | if (!cell) { 106 | cell = [[HXArticleListCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"HXArticleListCell"]; 107 | } 108 | 109 | [cell setData:self.dataSource[indexPath.row] key:_key delegate:self]; 110 | 111 | return cell; 112 | } 113 | 114 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 115 | HXArticle *article = self.dataSource[indexPath.row]; 116 | return [HXArticleListCell getCellFrame:article width:tableView.frame.size.width]; 117 | } 118 | 119 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 120 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 121 | } 122 | 123 | #pragma HXArticleListCellDelegate 124 | 125 | - (void)articleListCell:(HXArticleListCell *)cell titleButtonAction:(UIButton *)sender article:(HXArticle *)article { 126 | [self gotoDetailWebViewController:article type:@"detail"]; 127 | } 128 | 129 | - (void)articleListCell:(HXArticleListCell *)cell nameButtonAction:(UIButton *)sender article:(HXArticle *)article { 130 | [self gotoDetailWebViewController:article type:@"home"]; 131 | } 132 | 133 | #pragma mark - goto 134 | 135 | /** 136 | * 去文章详情界面 137 | */ 138 | - (void)gotoDetailWebViewController:(HXArticle *)article type:(NSString *)type { 139 | HXDetailWebViewController *vc = [HXDetailWebViewController new]; 140 | vc.article = article; 141 | vc.type = type; 142 | [self.navigationController pushViewController:vc animated:YES]; 143 | } 144 | 145 | #pragma mark - 懒加载 146 | 147 | - (UITableView *)tableView { 148 | if (!_tableView) { 149 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - 30) style:UITableViewStylePlain]; 150 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 151 | _tableView.dataSource = self; 152 | _tableView.delegate = self; 153 | _tableView.backgroundColor = [UIColor clearColor]; 154 | [_tableView registerNib:[UINib nibWithNibName:@"HXArticleListCell" bundle:nil] forCellReuseIdentifier:@"HXArticleListCell"]; 155 | } 156 | return _tableView; 157 | } 158 | 159 | - (HXRequestManager *)requestManager { 160 | if (!_requestManager) { 161 | _requestManager = [HXRequestManager new]; 162 | } 163 | return _requestManager; 164 | } 165 | 166 | - (void)didReceiveMemoryWarning { 167 | [super didReceiveMemoryWarning]; 168 | // Dispose of any resources that can be recreated. 169 | } 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXHistoryListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryListViewController.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXHistoryListViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXHistoryListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXHistoryListViewController.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/25. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXHistoryListViewController.h" 10 | #import "HXSearchResultViewController.h" 11 | 12 | @interface HXHistoryListViewController () 13 | 14 | @property (nonatomic,strong) UITableView *tableView; 15 | @property (nonatomic,strong) NSMutableArray *dataAry; 16 | 17 | @end 18 | 19 | @implementation HXHistoryListViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | self.title = @"搜索历史"; 27 | 28 | [self.view addSubview:self.tableView]; 29 | } 30 | 31 | #pragma mark - data 32 | 33 | - (NSArray *)dataSource { 34 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 35 | NSArray *ary = [userDefaults objectForKey:@"tagAry"]; 36 | return ary; 37 | } 38 | 39 | #pragma mark UITableViewDataSource/UITableViewDelegate 40 | 41 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 42 | return self.dataSource.count; 43 | } 44 | 45 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 46 | 47 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellID"]; 48 | if (!cell) { 49 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"CellID"]; 50 | } 51 | 52 | NSString *str = self.dataSource[self.dataSource.count - indexPath.row - 1]; 53 | 54 | cell.textLabel.text = str; 55 | 56 | return cell; 57 | } 58 | 59 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 60 | return 44; 61 | } 62 | 63 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 64 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 65 | 66 | NSString *str = self.dataSource[self.dataSource.count - indexPath.row - 1]; 67 | [self gotoSearchResultViewController:str]; 68 | } 69 | 70 | #pragma mark - goto 71 | 72 | - (void)gotoSearchResultViewController:(NSString *)key { 73 | HXSearchResultViewController *vc = [HXSearchResultViewController new]; 74 | vc.key = key; 75 | [self.navigationController pushViewController:vc animated:YES]; 76 | } 77 | 78 | #pragma mark - 懒加载 79 | 80 | - (UITableView *)tableView { 81 | if (!_tableView) { 82 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - 30) style:UITableViewStylePlain]; 83 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 84 | _tableView.dataSource = self; 85 | _tableView.delegate = self; 86 | _tableView.backgroundColor = [UIColor clearColor]; 87 | [_tableView registerNib:[UINib nibWithNibName:@"HXArticleListCell" bundle:nil] forCellReuseIdentifier:@"HXArticleListCell"]; 88 | } 89 | return _tableView; 90 | } 91 | 92 | - (void)didReceiveMemoryWarning { 93 | [super didReceiveMemoryWarning]; 94 | // Dispose of any resources that can be recreated. 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXSearchResultViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXSearchResultViewController.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/24. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXSearchResultViewController : UIViewController 12 | 13 | @property (nonatomic,copy) NSString *key; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Main/ViewController/List/HXSearchResultViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXSearchResultViewController.m 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/24. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import "HXSearchResultViewController.h" 10 | #import "HXArticleListViewController.h" 11 | 12 | @interface HXSearchResultViewController () 13 | 14 | @property (nonatomic,strong) HXArticleListViewController *csdnVc; //CSDN 15 | @property (nonatomic,strong) HXArticleListViewController *cnblogsVc; //博客园 16 | @property (nonatomic,strong) HXArticleListViewController *ctoVc; //51CTO 17 | @property (nonatomic,strong) HXArticleListViewController *oschinaVc; //开源中国 18 | 19 | @property (nonatomic ,strong) UIViewController *currentVC; //当前Vc 20 | 21 | @end 22 | 23 | @implementation HXSearchResultViewController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | // Do any additional setup after loading the view. 28 | self.view.backgroundColor = [UIColor colorWithRed:245/255.0 green:245/255.0 blue:245/255.0 alpha:1.0]; 29 | 30 | self.title = [NSString stringWithFormat:@"搜索\"%@\"相关结果",_key]; 31 | 32 | [self initSegmentedControl]; 33 | [self addSubControllers]; 34 | } 35 | 36 | #pragma mark - 37 | 38 | - (void)initSegmentedControl 39 | { 40 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 50)]; 41 | view.backgroundColor = [UIColor whiteColor]; 42 | 43 | UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"CSDN",@"博客园",@"51CTO",@"开源中国"]]; 44 | segmentedControl.frame = CGRectMake(10, 10,self.view.frame.size.width - 20, 30.0); 45 | segmentedControl.selectedSegmentIndex = 0; 46 | //设置分段控件点击相应事件 47 | [segmentedControl addTarget:self action:@selector(segmentedControlButtonAction:)forControlEvents:UIControlEventValueChanged]; 48 | 49 | [view addSubview:segmentedControl]; 50 | 51 | [self.view addSubview:view]; 52 | } 53 | 54 | - (void)segmentedControlButtonAction:(UISegmentedControl *)sender 55 | { 56 | NSInteger index = sender.selectedSegmentIndex; 57 | 58 | // 点击处于当前页面的按钮,直接跳出 59 | if ((self.currentVC == _csdnVc && index == 0) || (self.currentVC == _cnblogsVc && index == 1) || (self.currentVC == _ctoVc && index == 2) || (self.currentVC == _oschinaVc && index == 3)) { 60 | return; 61 | } else { 62 | 63 | switch (index) 64 | { 65 | case 0: 66 | [self replaceController:self.currentVC newController:_csdnVc]; 67 | break; 68 | 69 | case 1: 70 | [self replaceController:self.currentVC newController:_cnblogsVc]; 71 | break; 72 | 73 | case 2: 74 | [self replaceController:self.currentVC newController:_ctoVc]; 75 | break; 76 | 77 | case 3: 78 | [self replaceController:self.currentVC newController:_oschinaVc]; 79 | break; 80 | 81 | default: 82 | break; 83 | } 84 | } 85 | } 86 | 87 | #pragma mark - privatemethods 88 | 89 | - (void)addSubControllers { 90 | _csdnVc = [HXArticleListViewController new]; 91 | _csdnVc.view.frame = CGRectMake(0, 114, self.view.frame.size.width, self.view.frame.size.height - 114); 92 | _csdnVc.key = _key; 93 | _csdnVc.blogType = @"csdn"; 94 | [self addChildViewController:_csdnVc]; 95 | 96 | 97 | _cnblogsVc = [HXArticleListViewController new]; 98 | _cnblogsVc.view.frame = CGRectMake(0, 114, self.view.frame.size.width, self.view.frame.size.height - 114); 99 | _cnblogsVc.key = _key; 100 | _cnblogsVc.blogType = @"cnblogs"; 101 | [self addChildViewController:_cnblogsVc]; 102 | 103 | _ctoVc = [HXArticleListViewController new]; 104 | _ctoVc.view.frame = CGRectMake(0, 114, self.view.frame.size.width, self.view.frame.size.height - 114); 105 | _ctoVc.key = _key; 106 | _ctoVc.blogType = @"51cto"; 107 | [self addChildViewController:_ctoVc]; 108 | 109 | _oschinaVc = [HXArticleListViewController new]; 110 | _oschinaVc.view.frame = CGRectMake(0, 114, self.view.frame.size.width, self.view.frame.size.height - 114); 111 | _oschinaVc.key = _key; 112 | _oschinaVc.blogType = @"oschina"; 113 | [self addChildViewController:_oschinaVc]; 114 | 115 | // 默认,第一个视图(你会发现,全程就这一个用了addSubview) 116 | [self.view addSubview:_csdnVc.view]; 117 | 118 | self.currentVC = _csdnVc; 119 | } 120 | 121 | // 切换各个标签内容 122 | - (void)replaceController:(UIViewController *)oldController newController:(UIViewController *)newController 123 | { 124 | /** 125 | * 着重介绍一下它 126 | * transitionFromViewController:toViewController:duration:options:animations:completion: 127 | * fromViewController 当前显示在父视图控制器中的子视图控制器 128 | * toViewController 将要显示的姿势图控制器 129 | * duration 动画时间(这个属性,old friend 了 O(∩_∩)O) 130 | * options 动画效果(渐变,从下往上等等,具体查看API) 131 | * animations 转换过程中得动画 132 | * completion 转换完成 133 | */ 134 | [self transitionFromViewController:oldController 135 | toViewController:newController 136 | duration:0 137 | options:UIViewAnimationOptionTransitionCrossDissolve 138 | animations:^{ 139 | // NOPS; 140 | } 141 | completion:^(BOOL finished) { 142 | if (finished) { 143 | self.currentVC = newController; 144 | } else { 145 | self.currentVC = oldController; 146 | } 147 | }]; 148 | } 149 | 150 | - (void)didReceiveMemoryWarning { 151 | [super didReceiveMemoryWarning]; 152 | // Dispose of any resources that can be recreated. 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/Request/HXRequestManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXRequestManager.h 3 | // HXTechnologyBlogCrawler 4 | // 5 | // Created by 黄轩 on 16/8/20. 6 | // Copyright © 2016年 黄轩. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HXRequestManager : NSObject 12 | 13 | - (void)requestWithBlogType:(NSString *)blogType key:(NSString *)key page:(NSInteger)page complete:(void(^)(NSArray *dataAry,NSString *findCount))complete; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HXHtmlParser 4 | // 5 | // Created by 黄轩 on 16/8/18. 6 | // Copyright © 2016年 黄轩. 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 | -------------------------------------------------------------------------------- /HXTechnologyBlogCrawler/xiaoguo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/HXTechnologyBlogCrawler/xiaoguo.gif -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, ‘8.0’ 2 | 3 | target 'HXTechnologyBlogCrawler' do 4 | 5 | pod 'MJRefresh' 6 | 7 | end 8 | 9 | 10 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MJRefresh (3.1.0) 3 | 4 | DEPENDENCIES: 5 | - MJRefresh 6 | 7 | SPEC CHECKSUMS: 8 | MJRefresh: 743e6404967d1c2c688472ea3ecfde247d872db4 9 | 10 | PODFILE CHECKSUM: 3e97bbe32e7bf5574669b64ec6a6420fb7521e7f 11 | 12 | COCOAPODS: 1.0.0 13 | -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefresh.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/MJRefresh.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshComponent.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshConst.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/MJRefreshConst.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h -------------------------------------------------------------------------------- /Pods/Headers/Private/MJRefresh/UIView+MJExtension.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIView+MJExtension.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefresh.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/MJRefresh.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshComponent.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshConst.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/MJRefreshConst.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshFooter.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h -------------------------------------------------------------------------------- /Pods/Headers/Public/MJRefresh/UIView+MJExtension.h: -------------------------------------------------------------------------------- 1 | ../../../MJRefresh/MJRefresh/UIView+MJExtension.h -------------------------------------------------------------------------------- /Pods/MJRefresh/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshFooter.h" 10 | 11 | @interface MJRefreshAutoFooter : MJRefreshFooter 12 | /** 是否自动刷新(默认为YES) */ 13 | @property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; 14 | 15 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ 16 | @property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); 17 | 18 | /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ 19 | @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; 20 | @end 21 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoFooter.h" 10 | 11 | @interface MJRefreshAutoFooter() 12 | @end 13 | 14 | @implementation MJRefreshAutoFooter 15 | 16 | #pragma mark - 初始化 17 | - (void)willMoveToSuperview:(UIView *)newSuperview 18 | { 19 | [super willMoveToSuperview:newSuperview]; 20 | 21 | if (newSuperview) { // 新的父控件 22 | if (self.hidden == NO) { 23 | self.scrollView.mj_insetB += self.mj_h; 24 | } 25 | 26 | // 设置位置 27 | self.mj_y = _scrollView.mj_contentH; 28 | } else { // 被移除了 29 | if (self.hidden == NO) { 30 | self.scrollView.mj_insetB -= self.mj_h; 31 | } 32 | } 33 | } 34 | 35 | #pragma mark - 过期方法 36 | - (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh 37 | { 38 | self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh; 39 | } 40 | 41 | - (CGFloat)appearencePercentTriggerAutoRefresh 42 | { 43 | return self.triggerAutomaticallyRefreshPercent; 44 | } 45 | 46 | #pragma mark - 实现父类的方法 47 | - (void)prepare 48 | { 49 | [super prepare]; 50 | 51 | // 默认底部控件100%出现时才会自动刷新 52 | self.triggerAutomaticallyRefreshPercent = 1.0; 53 | 54 | // 设置为默认状态 55 | self.automaticallyRefresh = YES; 56 | } 57 | 58 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change 59 | { 60 | [super scrollViewContentSizeDidChange:change]; 61 | 62 | // 设置位置 63 | self.mj_y = self.scrollView.mj_contentH; 64 | } 65 | 66 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change 67 | { 68 | [super scrollViewContentOffsetDidChange:change]; 69 | 70 | if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return; 71 | 72 | if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕 73 | // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理 74 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) { 75 | // 防止手松开时连续调用 76 | CGPoint old = [change[@"old"] CGPointValue]; 77 | CGPoint new = [change[@"new"] CGPointValue]; 78 | if (new.y <= old.y) return; 79 | 80 | // 当底部刷新控件完全出现时,才刷新 81 | [self beginRefreshing]; 82 | } 83 | } 84 | } 85 | 86 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change 87 | { 88 | [super scrollViewPanStateDidChange:change]; 89 | 90 | if (self.state != MJRefreshStateIdle) return; 91 | 92 | if (_scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded) {// 手松开 93 | if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕 94 | if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽 95 | [self beginRefreshing]; 96 | } 97 | } else { // 超出一个屏幕 98 | if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) { 99 | [self beginRefreshing]; 100 | } 101 | } 102 | } 103 | } 104 | 105 | - (void)setState:(MJRefreshState)state 106 | { 107 | MJRefreshCheckState 108 | 109 | if (state == MJRefreshStateRefreshing) { 110 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 111 | [self executeRefreshingCallback]; 112 | }); 113 | } 114 | } 115 | 116 | - (void)setHidden:(BOOL)hidden 117 | { 118 | BOOL lastHidden = self.isHidden; 119 | 120 | [super setHidden:hidden]; 121 | 122 | if (!lastHidden && hidden) { 123 | self.state = MJRefreshStateIdle; 124 | 125 | self.scrollView.mj_insetB -= self.mj_h; 126 | } else if (lastHidden && !hidden) { 127 | self.scrollView.mj_insetB += self.mj_h; 128 | 129 | // 设置位置 130 | self.mj_y = _scrollView.mj_contentH; 131 | } 132 | } 133 | @end 134 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshFooter.h" 10 | 11 | @interface MJRefreshBackFooter : MJRefreshFooter 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackFooter.h" 10 | 11 | @interface MJRefreshBackFooter() 12 | @property (assign, nonatomic) NSInteger lastRefreshCount; 13 | @property (assign, nonatomic) CGFloat lastBottomDelta; 14 | @end 15 | 16 | @implementation MJRefreshBackFooter 17 | 18 | #pragma mark - 初始化 19 | - (void)willMoveToSuperview:(UIView *)newSuperview 20 | { 21 | [super willMoveToSuperview:newSuperview]; 22 | 23 | [self scrollViewContentSizeDidChange:nil]; 24 | } 25 | 26 | #pragma mark - 实现父类的方法 27 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change 28 | { 29 | [super scrollViewContentOffsetDidChange:change]; 30 | 31 | // 如果正在刷新,直接返回 32 | if (self.state == MJRefreshStateRefreshing) return; 33 | 34 | _scrollViewOriginalInset = self.scrollView.contentInset; 35 | 36 | // 当前的contentOffset 37 | CGFloat currentOffsetY = self.scrollView.mj_offsetY; 38 | // 尾部控件刚好出现的offsetY 39 | CGFloat happenOffsetY = [self happenOffsetY]; 40 | // 如果是向下滚动到看不见尾部控件,直接返回 41 | if (currentOffsetY <= happenOffsetY) return; 42 | 43 | CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h; 44 | 45 | // 如果已全部加载,仅设置pullingPercent,然后返回 46 | if (self.state == MJRefreshStateNoMoreData) { 47 | self.pullingPercent = pullingPercent; 48 | return; 49 | } 50 | 51 | if (self.scrollView.isDragging) { 52 | self.pullingPercent = pullingPercent; 53 | // 普通 和 即将刷新 的临界点 54 | CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h; 55 | 56 | if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) { 57 | // 转为即将刷新状态 58 | self.state = MJRefreshStatePulling; 59 | } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) { 60 | // 转为普通状态 61 | self.state = MJRefreshStateIdle; 62 | } 63 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 64 | // 开始刷新 65 | [self beginRefreshing]; 66 | } else if (pullingPercent < 1) { 67 | self.pullingPercent = pullingPercent; 68 | } 69 | } 70 | 71 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change 72 | { 73 | [super scrollViewContentSizeDidChange:change]; 74 | 75 | // 内容的高度 76 | CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom; 77 | // 表格的高度 78 | CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom; 79 | // 设置位置和尺寸 80 | self.mj_y = MAX(contentHeight, scrollHeight); 81 | } 82 | 83 | - (void)setState:(MJRefreshState)state 84 | { 85 | MJRefreshCheckState 86 | 87 | // 根据状态来设置属性 88 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { 89 | // 刷新完毕 90 | if (MJRefreshStateRefreshing == oldState) { 91 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ 92 | self.scrollView.mj_insetB -= self.lastBottomDelta; 93 | 94 | // 自动调整透明度 95 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; 96 | } completion:^(BOOL finished) { 97 | self.pullingPercent = 0.0; 98 | }]; 99 | } 100 | 101 | CGFloat deltaH = [self heightForContentBreakView]; 102 | // 刚刷新完毕 103 | if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) { 104 | self.scrollView.mj_offsetY = self.scrollView.mj_offsetY; 105 | } 106 | } else if (state == MJRefreshStateRefreshing) { 107 | // 记录刷新前的数量 108 | self.lastRefreshCount = self.scrollView.mj_totalDataCount; 109 | 110 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 111 | CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom; 112 | CGFloat deltaH = [self heightForContentBreakView]; 113 | if (deltaH < 0) { // 如果内容高度小于view的高度 114 | bottom -= deltaH; 115 | } 116 | self.lastBottomDelta = bottom - self.scrollView.mj_insetB; 117 | self.scrollView.mj_insetB = bottom; 118 | self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h; 119 | } completion:^(BOOL finished) { 120 | [self executeRefreshingCallback]; 121 | }]; 122 | } 123 | } 124 | 125 | #pragma mark - 公共方法 126 | - (void)endRefreshing 127 | { 128 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) { 129 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 130 | [super endRefreshing]; 131 | }); 132 | } else { 133 | [super endRefreshing]; 134 | } 135 | } 136 | 137 | - (void)noticeNoMoreData 138 | { 139 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) { 140 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 141 | [super noticeNoMoreData]; 142 | }); 143 | } else { 144 | [super noticeNoMoreData]; 145 | } 146 | } 147 | 148 | #pragma mark - 私有方法 149 | #pragma mark 获得scrollView的内容 超出 view 的高度 150 | - (CGFloat)heightForContentBreakView 151 | { 152 | CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top; 153 | return self.scrollView.contentSize.height - h; 154 | } 155 | 156 | #pragma mark 刚好看到上拉刷新控件时的contentOffset.y 157 | - (CGFloat)happenOffsetY 158 | { 159 | CGFloat deltaH = [self heightForContentBreakView]; 160 | if (deltaH > 0) { 161 | return deltaH - self.scrollViewOriginalInset.top; 162 | } else { 163 | return - self.scrollViewOriginalInset.top; 164 | } 165 | } 166 | @end 167 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshComponent.h 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 刷新控件的基类 9 | 10 | #import 11 | #import "MJRefreshConst.h" 12 | #import "UIView+MJExtension.h" 13 | #import "UIScrollView+MJExtension.h" 14 | #import "UIScrollView+MJRefresh.h" 15 | 16 | /** 刷新控件的状态 */ 17 | typedef NS_ENUM(NSInteger, MJRefreshState) { 18 | /** 普通闲置状态 */ 19 | MJRefreshStateIdle = 1, 20 | /** 松开就可以进行刷新的状态 */ 21 | MJRefreshStatePulling, 22 | /** 正在刷新中的状态 */ 23 | MJRefreshStateRefreshing, 24 | /** 即将刷新的状态 */ 25 | MJRefreshStateWillRefresh, 26 | /** 所有数据加载完毕,没有更多的数据了 */ 27 | MJRefreshStateNoMoreData 28 | }; 29 | 30 | /** 进入刷新状态的回调 */ 31 | typedef void (^MJRefreshComponentRefreshingBlock)(); 32 | 33 | /** 刷新控件的基类 */ 34 | @interface MJRefreshComponent : UIView 35 | { 36 | /** 记录scrollView刚开始的inset */ 37 | UIEdgeInsets _scrollViewOriginalInset; 38 | /** 父控件 */ 39 | __weak UIScrollView *_scrollView; 40 | } 41 | #pragma mark - 刷新回调 42 | /** 正在刷新的回调 */ 43 | @property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock; 44 | /** 设置回调对象和回调方法 */ 45 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action; 46 | /** 回调对象 */ 47 | @property (weak, nonatomic) id refreshingTarget; 48 | /** 回调方法 */ 49 | @property (assign, nonatomic) SEL refreshingAction; 50 | /** 触发回调(交给子类去调用) */ 51 | - (void)executeRefreshingCallback; 52 | 53 | #pragma mark - 刷新状态控制 54 | /** 进入刷新状态 */ 55 | - (void)beginRefreshing; 56 | /** 结束刷新状态 */ 57 | - (void)endRefreshing; 58 | /** 是否正在刷新 */ 59 | - (BOOL)isRefreshing; 60 | /** 刷新状态 一般交给子类内部实现 */ 61 | @property (assign, nonatomic) MJRefreshState state; 62 | 63 | #pragma mark - 交给子类去访问 64 | /** 记录scrollView刚开始的inset */ 65 | @property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset; 66 | /** 父控件 */ 67 | @property (weak, nonatomic, readonly) UIScrollView *scrollView; 68 | 69 | #pragma mark - 交给子类们去实现 70 | /** 初始化 */ 71 | - (void)prepare NS_REQUIRES_SUPER; 72 | /** 摆放子控件frame */ 73 | - (void)placeSubviews NS_REQUIRES_SUPER; 74 | /** 当scrollView的contentOffset发生改变的时候调用 */ 75 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; 76 | /** 当scrollView的contentSize发生改变的时候调用 */ 77 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; 78 | /** 当scrollView的拖拽状态发生改变的时候调用 */ 79 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER; 80 | 81 | 82 | #pragma mark - 其他 83 | /** 拉拽的百分比(交给子类重写) */ 84 | @property (assign, nonatomic) CGFloat pullingPercent; 85 | /** 根据拖拽比例自动切换透明度 */ 86 | @property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); 87 | /** 根据拖拽比例自动切换透明度 */ 88 | @property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; 89 | @end 90 | 91 | @interface UILabel(MJRefresh) 92 | + (instancetype)label; 93 | @end 94 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshComponent.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "MJRefreshComponent.h" 11 | #import "MJRefreshConst.h" 12 | #import "UIView+MJExtension.h" 13 | #import "UIScrollView+MJRefresh.h" 14 | 15 | @interface MJRefreshComponent() 16 | @property (strong, nonatomic) UIPanGestureRecognizer *pan; 17 | @end 18 | 19 | @implementation MJRefreshComponent 20 | #pragma mark - 初始化 21 | - (instancetype)initWithFrame:(CGRect)frame 22 | { 23 | if (self = [super initWithFrame:frame]) { 24 | // 准备工作 25 | [self prepare]; 26 | 27 | // 默认是普通状态 28 | self.state = MJRefreshStateIdle; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)prepare 34 | { 35 | // 基本属性 36 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth; 37 | self.backgroundColor = [UIColor clearColor]; 38 | } 39 | 40 | - (void)layoutSubviews 41 | { 42 | [super layoutSubviews]; 43 | 44 | [self placeSubviews]; 45 | } 46 | 47 | - (void)placeSubviews{} 48 | 49 | - (void)willMoveToSuperview:(UIView *)newSuperview 50 | { 51 | [super willMoveToSuperview:newSuperview]; 52 | 53 | // 如果不是UIScrollView,不做任何事情 54 | if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return; 55 | 56 | // 旧的父控件移除监听 57 | [self removeObservers]; 58 | 59 | if (newSuperview) { // 新的父控件 60 | // 设置宽度 61 | self.mj_w = newSuperview.mj_w; 62 | // 设置位置 63 | self.mj_x = 0; 64 | 65 | // 记录UIScrollView 66 | _scrollView = (UIScrollView *)newSuperview; 67 | // 设置永远支持垂直弹簧效果 68 | _scrollView.alwaysBounceVertical = YES; 69 | // 记录UIScrollView最开始的contentInset 70 | _scrollViewOriginalInset = _scrollView.contentInset; 71 | 72 | // 添加监听 73 | [self addObservers]; 74 | } 75 | } 76 | 77 | - (void)drawRect:(CGRect)rect 78 | { 79 | [super drawRect:rect]; 80 | 81 | if (self.state == MJRefreshStateWillRefresh) { 82 | // 预防view还没显示出来就调用了beginRefreshing 83 | self.state = MJRefreshStateRefreshing; 84 | } 85 | } 86 | 87 | #pragma mark - KVO监听 88 | - (void)addObservers 89 | { 90 | NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; 91 | [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil]; 92 | [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil]; 93 | self.pan = self.scrollView.panGestureRecognizer; 94 | [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil]; 95 | } 96 | 97 | - (void)removeObservers 98 | { 99 | [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset]; 100 | [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];; 101 | [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState]; 102 | self.pan = nil; 103 | } 104 | 105 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 106 | { 107 | // 遇到这些情况就直接返回 108 | if (!self.userInteractionEnabled) return; 109 | 110 | // 这个就算看不见也需要处理 111 | if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) { 112 | [self scrollViewContentSizeDidChange:change]; 113 | } 114 | 115 | // 看不见 116 | if (self.hidden) return; 117 | if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) { 118 | [self scrollViewContentOffsetDidChange:change]; 119 | } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) { 120 | [self scrollViewPanStateDidChange:change]; 121 | } 122 | } 123 | 124 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{} 125 | - (void)scrollViewContentSizeDidChange:(NSDictionary *)change{} 126 | - (void)scrollViewPanStateDidChange:(NSDictionary *)change{} 127 | 128 | #pragma mark - 公共方法 129 | #pragma mark 设置回调对象和回调方法 130 | - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action 131 | { 132 | self.refreshingTarget = target; 133 | self.refreshingAction = action; 134 | } 135 | 136 | #pragma mark 进入刷新状态 137 | - (void)beginRefreshing 138 | { 139 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 140 | self.alpha = 1.0; 141 | }]; 142 | self.pullingPercent = 1.0; 143 | // 只要正在刷新,就完全显示 144 | if (self.window) { 145 | self.state = MJRefreshStateRefreshing; 146 | } else { 147 | self.state = MJRefreshStateWillRefresh; 148 | // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下) 149 | [self setNeedsDisplay]; 150 | } 151 | } 152 | 153 | #pragma mark 结束刷新状态 154 | - (void)endRefreshing 155 | { 156 | self.state = MJRefreshStateIdle; 157 | } 158 | 159 | #pragma mark 是否正在刷新 160 | - (BOOL)isRefreshing 161 | { 162 | return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh; 163 | } 164 | 165 | #pragma mark 自动切换透明度 166 | - (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha 167 | { 168 | self.automaticallyChangeAlpha = autoChangeAlpha; 169 | } 170 | 171 | - (BOOL)isAutoChangeAlpha 172 | { 173 | return self.isAutomaticallyChangeAlpha; 174 | } 175 | 176 | - (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha 177 | { 178 | _automaticallyChangeAlpha = automaticallyChangeAlpha; 179 | 180 | if (self.isRefreshing) return; 181 | 182 | if (automaticallyChangeAlpha) { 183 | self.alpha = self.pullingPercent; 184 | } else { 185 | self.alpha = 1.0; 186 | } 187 | } 188 | 189 | #pragma mark 根据拖拽进度设置透明度 190 | - (void)setPullingPercent:(CGFloat)pullingPercent 191 | { 192 | _pullingPercent = pullingPercent; 193 | 194 | if (self.isRefreshing) return; 195 | 196 | if (self.isAutomaticallyChangeAlpha) { 197 | self.alpha = pullingPercent; 198 | } 199 | } 200 | 201 | #pragma mark - 内部方法 202 | - (void)executeRefreshingCallback 203 | { 204 | dispatch_async(dispatch_get_main_queue(), ^{ 205 | if (self.refreshingBlock) { 206 | self.refreshingBlock(); 207 | } 208 | if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) { 209 | MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self); 210 | } 211 | }); 212 | } 213 | @end 214 | 215 | @implementation UILabel(MJRefresh) 216 | + (instancetype)label 217 | { 218 | UILabel *label = [[self alloc] init]; 219 | label.font = MJRefreshLabelFont; 220 | label.textColor = MJRefreshLabelTextColor; 221 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth; 222 | label.textAlignment = NSTextAlignmentCenter; 223 | label.backgroundColor = [UIColor clearColor]; 224 | return label; 225 | } 226 | @end -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshFooter.h 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/5. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 上拉刷新控件 9 | 10 | #import "MJRefreshComponent.h" 11 | 12 | @interface MJRefreshFooter : MJRefreshComponent 13 | /** 创建footer */ 14 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; 15 | /** 创建footer */ 16 | + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; 17 | 18 | /** 提示没有更多的数据 */ 19 | - (void)endRefreshingWithNoMoreData; 20 | - (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData"); 21 | 22 | /** 重置没有更多的数据(消除没有更多数据的状态) */ 23 | - (void)resetNoMoreData; 24 | 25 | /** 忽略多少scrollView的contentInset的bottom */ 26 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; 27 | 28 | /** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */ 29 | @property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden; 30 | @end 31 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshFooter.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/5. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "MJRefreshFooter.h" 11 | 12 | @interface MJRefreshFooter() 13 | 14 | @end 15 | 16 | @implementation MJRefreshFooter 17 | #pragma mark - 构造方法 18 | + (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock 19 | { 20 | MJRefreshFooter *cmp = [[self alloc] init]; 21 | cmp.refreshingBlock = refreshingBlock; 22 | return cmp; 23 | } 24 | + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action 25 | { 26 | MJRefreshFooter *cmp = [[self alloc] init]; 27 | [cmp setRefreshingTarget:target refreshingAction:action]; 28 | return cmp; 29 | } 30 | 31 | #pragma mark - 重写父类的方法 32 | - (void)prepare 33 | { 34 | [super prepare]; 35 | 36 | // 设置自己的高度 37 | self.mj_h = MJRefreshFooterHeight; 38 | 39 | // 默认不会自动隐藏 40 | self.automaticallyHidden = NO; 41 | } 42 | 43 | - (void)willMoveToSuperview:(UIView *)newSuperview 44 | { 45 | [super willMoveToSuperview:newSuperview]; 46 | 47 | if (newSuperview) { 48 | // 监听scrollView数据的变化 49 | if ([self.scrollView isKindOfClass:[UITableView class]] || [self.scrollView isKindOfClass:[UICollectionView class]]) { 50 | [self.scrollView setMj_reloadDataBlock:^(NSInteger totalDataCount) { 51 | if (self.isAutomaticallyHidden) { 52 | self.hidden = (totalDataCount == 0); 53 | } 54 | }]; 55 | } 56 | } 57 | } 58 | 59 | #pragma mark - 公共方法 60 | - (void)endRefreshingWithNoMoreData 61 | { 62 | self.state = MJRefreshStateNoMoreData; 63 | } 64 | 65 | - (void)noticeNoMoreData 66 | { 67 | [self endRefreshingWithNoMoreData]; 68 | } 69 | 70 | - (void)resetNoMoreData 71 | { 72 | self.state = MJRefreshStateIdle; 73 | } 74 | @end 75 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshHeader.h 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 下拉刷新控件:负责监控用户下拉的状态 9 | 10 | #import "MJRefreshComponent.h" 11 | 12 | @interface MJRefreshHeader : MJRefreshComponent 13 | /** 创建header */ 14 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; 15 | /** 创建header */ 16 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; 17 | 18 | /** 这个key用来存储上一次下拉刷新成功的时间 */ 19 | @property (copy, nonatomic) NSString *lastUpdatedTimeKey; 20 | /** 上一次下拉刷新成功的时间 */ 21 | @property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; 22 | 23 | /** 忽略多少scrollView的contentInset的top */ 24 | @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; 25 | @end 26 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // MJRefreshHeader.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "MJRefreshHeader.h" 11 | 12 | @interface MJRefreshHeader() 13 | @property (assign, nonatomic) CGFloat insetTDelta; 14 | @end 15 | 16 | @implementation MJRefreshHeader 17 | #pragma mark - 构造方法 18 | + (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock 19 | { 20 | MJRefreshHeader *cmp = [[self alloc] init]; 21 | cmp.refreshingBlock = refreshingBlock; 22 | return cmp; 23 | } 24 | + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action 25 | { 26 | MJRefreshHeader *cmp = [[self alloc] init]; 27 | [cmp setRefreshingTarget:target refreshingAction:action]; 28 | return cmp; 29 | } 30 | 31 | #pragma mark - 覆盖父类的方法 32 | - (void)prepare 33 | { 34 | [super prepare]; 35 | 36 | // 设置key 37 | self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey; 38 | 39 | // 设置高度 40 | self.mj_h = MJRefreshHeaderHeight; 41 | } 42 | 43 | - (void)placeSubviews 44 | { 45 | [super placeSubviews]; 46 | 47 | // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值) 48 | self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop; 49 | } 50 | 51 | - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change 52 | { 53 | [super scrollViewContentOffsetDidChange:change]; 54 | 55 | // 在刷新的refreshing状态 56 | if (self.state == MJRefreshStateRefreshing) { 57 | if (self.window == nil) return; 58 | 59 | // sectionheader停留解决 60 | CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top; 61 | insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT; 62 | self.scrollView.mj_insetT = insetT; 63 | 64 | self.insetTDelta = _scrollViewOriginalInset.top - insetT; 65 | return; 66 | } 67 | 68 | // 跳转到下一个控制器时,contentInset可能会变 69 | _scrollViewOriginalInset = self.scrollView.contentInset; 70 | 71 | // 当前的contentOffset 72 | CGFloat offsetY = self.scrollView.mj_offsetY; 73 | // 头部控件刚好出现的offsetY 74 | CGFloat happenOffsetY = - self.scrollViewOriginalInset.top; 75 | 76 | // 如果是向上滚动到看不见头部控件,直接返回 77 | // >= -> > 78 | if (offsetY > happenOffsetY) return; 79 | 80 | // 普通 和 即将刷新 的临界点 81 | CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h; 82 | CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h; 83 | 84 | if (self.scrollView.isDragging) { // 如果正在拖拽 85 | self.pullingPercent = pullingPercent; 86 | if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) { 87 | // 转为即将刷新状态 88 | self.state = MJRefreshStatePulling; 89 | } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) { 90 | // 转为普通状态 91 | self.state = MJRefreshStateIdle; 92 | } 93 | } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 94 | // 开始刷新 95 | [self beginRefreshing]; 96 | } else if (pullingPercent < 1) { 97 | self.pullingPercent = pullingPercent; 98 | } 99 | } 100 | 101 | - (void)setState:(MJRefreshState)state 102 | { 103 | MJRefreshCheckState 104 | 105 | // 根据状态做事情 106 | if (state == MJRefreshStateIdle) { 107 | if (oldState != MJRefreshStateRefreshing) return; 108 | 109 | // 保存刷新时间 110 | [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey]; 111 | [[NSUserDefaults standardUserDefaults] synchronize]; 112 | 113 | // 恢复inset和offset 114 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ 115 | self.scrollView.mj_insetT += self.insetTDelta; 116 | 117 | // 自动调整透明度 118 | if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; 119 | } completion:^(BOOL finished) { 120 | self.pullingPercent = 0.0; 121 | }]; 122 | } else if (state == MJRefreshStateRefreshing) { 123 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 124 | // 增加滚动区域 125 | CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; 126 | self.scrollView.mj_insetT = top; 127 | 128 | // 设置滚动位置 129 | self.scrollView.mj_offsetY = - top; 130 | } completion:^(BOOL finished) { 131 | [self executeRefreshingCallback]; 132 | }]; 133 | } 134 | } 135 | 136 | - (void)drawRect:(CGRect)rect 137 | { 138 | [super drawRect:rect]; 139 | 140 | 141 | } 142 | 143 | #pragma mark - 公共方法 144 | - (void)endRefreshing 145 | { 146 | if ([self.scrollView isKindOfClass:[UICollectionView class]]) { 147 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 148 | [super endRefreshing]; 149 | }); 150 | } else { 151 | [super endRefreshing]; 152 | } 153 | } 154 | 155 | - (NSDate *)lastUpdatedTime 156 | { 157 | return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey]; 158 | } 159 | @end 160 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoGifFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoStateFooter.h" 10 | 11 | @interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter 12 | @property (weak, nonatomic, readonly) UIImageView *gifView; 13 | 14 | /** 设置state状态下的动画图片images 动画持续时间duration*/ 15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; 16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; 17 | @end 18 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoGifFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoGifFooter.h" 10 | 11 | @interface MJRefreshAutoGifFooter() 12 | { 13 | __unsafe_unretained UIImageView *_gifView; 14 | } 15 | /** 所有状态对应的动画图片 */ 16 | @property (strong, nonatomic) NSMutableDictionary *stateImages; 17 | /** 所有状态对应的动画时间 */ 18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations; 19 | @end 20 | 21 | @implementation MJRefreshAutoGifFooter 22 | #pragma mark - 懒加载 23 | - (UIImageView *)gifView 24 | { 25 | if (!_gifView) { 26 | UIImageView *gifView = [[UIImageView alloc] init]; 27 | [self addSubview:_gifView = gifView]; 28 | } 29 | return _gifView; 30 | } 31 | 32 | - (NSMutableDictionary *)stateImages 33 | { 34 | if (!_stateImages) { 35 | self.stateImages = [NSMutableDictionary dictionary]; 36 | } 37 | return _stateImages; 38 | } 39 | 40 | - (NSMutableDictionary *)stateDurations 41 | { 42 | if (!_stateDurations) { 43 | self.stateDurations = [NSMutableDictionary dictionary]; 44 | } 45 | return _stateDurations; 46 | } 47 | 48 | #pragma mark - 公共方法 49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state 50 | { 51 | if (images == nil) return; 52 | 53 | self.stateImages[@(state)] = images; 54 | self.stateDurations[@(state)] = @(duration); 55 | 56 | /* 根据图片设置控件的高度 */ 57 | UIImage *image = [images firstObject]; 58 | if (image.size.height > self.mj_h) { 59 | self.mj_h = image.size.height; 60 | } 61 | } 62 | 63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state 64 | { 65 | [self setImages:images duration:images.count * 0.1 forState:state]; 66 | } 67 | 68 | #pragma mark - 实现父类的方法 69 | - (void)placeSubviews 70 | { 71 | [super placeSubviews]; 72 | 73 | if (self.gifView.constraints.count) return; 74 | 75 | self.gifView.frame = self.bounds; 76 | if (self.isRefreshingTitleHidden) { 77 | self.gifView.contentMode = UIViewContentModeCenter; 78 | } else { 79 | self.gifView.contentMode = UIViewContentModeRight; 80 | self.gifView.mj_w = self.mj_w * 0.5 - 90; 81 | } 82 | } 83 | 84 | - (void)setState:(MJRefreshState)state 85 | { 86 | MJRefreshCheckState 87 | 88 | // 根据状态做事情 89 | if (state == MJRefreshStateRefreshing) { 90 | NSArray *images = self.stateImages[@(state)]; 91 | if (images.count == 0) return; 92 | [self.gifView stopAnimating]; 93 | 94 | self.gifView.hidden = NO; 95 | if (images.count == 1) { // 单张图片 96 | self.gifView.image = [images lastObject]; 97 | } else { // 多张图片 98 | self.gifView.animationImages = images; 99 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; 100 | [self.gifView startAnimating]; 101 | } 102 | } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { 103 | [self.gifView stopAnimating]; 104 | self.gifView.hidden = YES; 105 | } 106 | } 107 | @end 108 | 109 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoNormalFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoStateFooter.h" 10 | 11 | @interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter 12 | /** 菊花的样式 */ 13 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; 14 | @end 15 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoNormalFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoNormalFooter.h" 10 | 11 | @interface MJRefreshAutoNormalFooter() 12 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView; 13 | @end 14 | 15 | @implementation MJRefreshAutoNormalFooter 16 | #pragma mark - 懒加载子控件 17 | - (UIActivityIndicatorView *)loadingView 18 | { 19 | if (!_loadingView) { 20 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; 21 | loadingView.hidesWhenStopped = YES; 22 | [self addSubview:_loadingView = loadingView]; 23 | } 24 | return _loadingView; 25 | } 26 | 27 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle 28 | { 29 | _activityIndicatorViewStyle = activityIndicatorViewStyle; 30 | 31 | self.loadingView = nil; 32 | [self setNeedsLayout]; 33 | } 34 | #pragma makr - 重写父类的方法 35 | - (void)prepare 36 | { 37 | [super prepare]; 38 | 39 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; 40 | } 41 | 42 | - (void)placeSubviews 43 | { 44 | [super placeSubviews]; 45 | 46 | if (self.loadingView.constraints.count) return; 47 | 48 | // 圈圈 49 | CGFloat loadingCenterX = self.mj_w * 0.5; 50 | if (!self.isRefreshingTitleHidden) { 51 | loadingCenterX -= 100; 52 | } 53 | CGFloat loadingCenterY = self.mj_h * 0.5; 54 | self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY); 55 | } 56 | 57 | - (void)setState:(MJRefreshState)state 58 | { 59 | MJRefreshCheckState 60 | 61 | // 根据状态做事情 62 | if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { 63 | [self.loadingView stopAnimating]; 64 | } else if (state == MJRefreshStateRefreshing) { 65 | [self.loadingView startAnimating]; 66 | } 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoStateFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/6/13. 6 | // Copyright © 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoFooter.h" 10 | 11 | @interface MJRefreshAutoStateFooter : MJRefreshAutoFooter 12 | /** 显示刷新状态的label */ 13 | @property (weak, nonatomic, readonly) UILabel *stateLabel; 14 | 15 | /** 设置state状态下的文字 */ 16 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; 17 | 18 | /** 隐藏刷新状态的文字 */ 19 | @property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden; 20 | @end 21 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshAutoStateFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/6/13. 6 | // Copyright © 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshAutoStateFooter.h" 10 | 11 | @interface MJRefreshAutoStateFooter() 12 | { 13 | /** 显示刷新状态的label */ 14 | __unsafe_unretained UILabel *_stateLabel; 15 | } 16 | /** 所有状态对应的文字 */ 17 | @property (strong, nonatomic) NSMutableDictionary *stateTitles; 18 | @end 19 | 20 | @implementation MJRefreshAutoStateFooter 21 | #pragma mark - 懒加载 22 | - (NSMutableDictionary *)stateTitles 23 | { 24 | if (!_stateTitles) { 25 | self.stateTitles = [NSMutableDictionary dictionary]; 26 | } 27 | return _stateTitles; 28 | } 29 | 30 | - (UILabel *)stateLabel 31 | { 32 | if (!_stateLabel) { 33 | [self addSubview:_stateLabel = [UILabel label]]; 34 | } 35 | return _stateLabel; 36 | } 37 | 38 | #pragma mark - 公共方法 39 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state 40 | { 41 | if (title == nil) return; 42 | self.stateTitles[@(state)] = title; 43 | self.stateLabel.text = self.stateTitles[@(self.state)]; 44 | } 45 | 46 | #pragma mark - 私有方法 47 | - (void)stateLabelClick 48 | { 49 | if (self.state == MJRefreshStateIdle) { 50 | [self beginRefreshing]; 51 | } 52 | } 53 | 54 | #pragma mark - 重写父类的方法 55 | - (void)prepare 56 | { 57 | [super prepare]; 58 | 59 | // 初始化文字 60 | [self setTitle:MJRefreshAutoFooterIdleText forState:MJRefreshStateIdle]; 61 | [self setTitle:MJRefreshAutoFooterRefreshingText forState:MJRefreshStateRefreshing]; 62 | [self setTitle:MJRefreshAutoFooterNoMoreDataText forState:MJRefreshStateNoMoreData]; 63 | 64 | // 监听label 65 | self.stateLabel.userInteractionEnabled = YES; 66 | [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]]; 67 | } 68 | 69 | - (void)placeSubviews 70 | { 71 | [super placeSubviews]; 72 | 73 | if (self.stateLabel.constraints.count) return; 74 | 75 | // 状态标签 76 | self.stateLabel.frame = self.bounds; 77 | } 78 | 79 | - (void)setState:(MJRefreshState)state 80 | { 81 | MJRefreshCheckState 82 | 83 | if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) { 84 | self.stateLabel.text = nil; 85 | } else { 86 | self.stateLabel.text = self.stateTitles[@(state)]; 87 | } 88 | } 89 | @end -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackGifFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackStateFooter.h" 10 | 11 | @interface MJRefreshBackGifFooter : MJRefreshBackStateFooter 12 | @property (weak, nonatomic, readonly) UIImageView *gifView; 13 | 14 | /** 设置state状态下的动画图片images 动画持续时间duration*/ 15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; 16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; 17 | @end 18 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackGifFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackGifFooter.h" 10 | 11 | @interface MJRefreshBackGifFooter() 12 | { 13 | __unsafe_unretained UIImageView *_gifView; 14 | } 15 | /** 所有状态对应的动画图片 */ 16 | @property (strong, nonatomic) NSMutableDictionary *stateImages; 17 | /** 所有状态对应的动画时间 */ 18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations; 19 | @end 20 | 21 | @implementation MJRefreshBackGifFooter 22 | #pragma mark - 懒加载 23 | - (UIImageView *)gifView 24 | { 25 | if (!_gifView) { 26 | UIImageView *gifView = [[UIImageView alloc] init]; 27 | [self addSubview:_gifView = gifView]; 28 | } 29 | return _gifView; 30 | } 31 | 32 | - (NSMutableDictionary *)stateImages 33 | { 34 | if (!_stateImages) { 35 | self.stateImages = [NSMutableDictionary dictionary]; 36 | } 37 | return _stateImages; 38 | } 39 | 40 | - (NSMutableDictionary *)stateDurations 41 | { 42 | if (!_stateDurations) { 43 | self.stateDurations = [NSMutableDictionary dictionary]; 44 | } 45 | return _stateDurations; 46 | } 47 | 48 | #pragma mark - 公共方法 49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state 50 | { 51 | if (images == nil) return; 52 | 53 | self.stateImages[@(state)] = images; 54 | self.stateDurations[@(state)] = @(duration); 55 | 56 | /* 根据图片设置控件的高度 */ 57 | UIImage *image = [images firstObject]; 58 | if (image.size.height > self.mj_h) { 59 | self.mj_h = image.size.height; 60 | } 61 | } 62 | 63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state 64 | { 65 | [self setImages:images duration:images.count * 0.1 forState:state]; 66 | } 67 | 68 | #pragma mark - 实现父类的方法 69 | - (void)setPullingPercent:(CGFloat)pullingPercent 70 | { 71 | [super setPullingPercent:pullingPercent]; 72 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; 73 | if (self.state != MJRefreshStateIdle || images.count == 0) return; 74 | [self.gifView stopAnimating]; 75 | NSUInteger index = images.count * pullingPercent; 76 | if (index >= images.count) index = images.count - 1; 77 | self.gifView.image = images[index]; 78 | } 79 | 80 | - (void)placeSubviews 81 | { 82 | [super placeSubviews]; 83 | 84 | if (self.gifView.constraints.count) return; 85 | 86 | self.gifView.frame = self.bounds; 87 | if (self.stateLabel.hidden) { 88 | self.gifView.contentMode = UIViewContentModeCenter; 89 | } else { 90 | self.gifView.contentMode = UIViewContentModeRight; 91 | self.gifView.mj_w = self.mj_w * 0.5 - 90; 92 | } 93 | } 94 | 95 | - (void)setState:(MJRefreshState)state 96 | { 97 | MJRefreshCheckState 98 | 99 | // 根据状态做事情 100 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { 101 | NSArray *images = self.stateImages[@(state)]; 102 | if (images.count == 0) return; 103 | 104 | self.gifView.hidden = NO; 105 | [self.gifView stopAnimating]; 106 | if (images.count == 1) { // 单张图片 107 | self.gifView.image = [images lastObject]; 108 | } else { // 多张图片 109 | self.gifView.animationImages = images; 110 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; 111 | [self.gifView startAnimating]; 112 | } 113 | } else if (state == MJRefreshStateIdle) { 114 | self.gifView.hidden = NO; 115 | } else if (state == MJRefreshStateNoMoreData) { 116 | self.gifView.hidden = YES; 117 | } 118 | } 119 | @end 120 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackNormalFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackStateFooter.h" 10 | 11 | @interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter 12 | @property (weak, nonatomic, readonly) UIImageView *arrowView; 13 | /** 菊花的样式 */ 14 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; 15 | @end 16 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackNormalFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackNormalFooter.h" 10 | 11 | @interface MJRefreshBackNormalFooter() 12 | { 13 | __unsafe_unretained UIImageView *_arrowView; 14 | } 15 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView; 16 | @end 17 | 18 | @implementation MJRefreshBackNormalFooter 19 | #pragma mark - 懒加载子控件 20 | - (UIImageView *)arrowView 21 | { 22 | if (!_arrowView) { 23 | UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")]; 24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:image]; 25 | [self addSubview:_arrowView = arrowView]; 26 | } 27 | return _arrowView; 28 | } 29 | 30 | 31 | - (UIActivityIndicatorView *)loadingView 32 | { 33 | if (!_loadingView) { 34 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; 35 | loadingView.hidesWhenStopped = YES; 36 | [self addSubview:_loadingView = loadingView]; 37 | } 38 | return _loadingView; 39 | } 40 | 41 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle 42 | { 43 | _activityIndicatorViewStyle = activityIndicatorViewStyle; 44 | 45 | self.loadingView = nil; 46 | [self setNeedsLayout]; 47 | } 48 | #pragma makr - 重写父类的方法 49 | - (void)prepare 50 | { 51 | [super prepare]; 52 | 53 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; 54 | } 55 | 56 | - (void)placeSubviews 57 | { 58 | [super placeSubviews]; 59 | 60 | // 箭头的中心点 61 | CGFloat arrowCenterX = self.mj_w * 0.5; 62 | if (!self.stateLabel.hidden) { 63 | arrowCenterX -= 100; 64 | } 65 | CGFloat arrowCenterY = self.mj_h * 0.5; 66 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); 67 | 68 | // 箭头 69 | if (self.arrowView.constraints.count == 0) { 70 | self.arrowView.mj_size = self.arrowView.image.size; 71 | self.arrowView.center = arrowCenter; 72 | } 73 | 74 | // 圈圈 75 | if (self.loadingView.constraints.count == 0) { 76 | self.loadingView.center = arrowCenter; 77 | } 78 | } 79 | 80 | - (void)setState:(MJRefreshState)state 81 | { 82 | MJRefreshCheckState 83 | 84 | // 根据状态做事情 85 | if (state == MJRefreshStateIdle) { 86 | if (oldState == MJRefreshStateRefreshing) { 87 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); 88 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ 89 | self.loadingView.alpha = 0.0; 90 | } completion:^(BOOL finished) { 91 | self.loadingView.alpha = 1.0; 92 | [self.loadingView stopAnimating]; 93 | 94 | self.arrowView.hidden = NO; 95 | }]; 96 | } else { 97 | self.arrowView.hidden = NO; 98 | [self.loadingView stopAnimating]; 99 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 100 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); 101 | }]; 102 | } 103 | } else if (state == MJRefreshStatePulling) { 104 | self.arrowView.hidden = NO; 105 | [self.loadingView stopAnimating]; 106 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 107 | self.arrowView.transform = CGAffineTransformIdentity; 108 | }]; 109 | } else if (state == MJRefreshStateRefreshing) { 110 | self.arrowView.hidden = YES; 111 | [self.loadingView startAnimating]; 112 | } else if (state == MJRefreshStateNoMoreData) { 113 | self.arrowView.hidden = YES; 114 | [self.loadingView stopAnimating]; 115 | } 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackStateFooter.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/6/13. 6 | // Copyright © 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackFooter.h" 10 | 11 | @interface MJRefreshBackStateFooter : MJRefreshBackFooter 12 | /** 显示刷新状态的label */ 13 | @property (weak, nonatomic, readonly) UILabel *stateLabel; 14 | /** 设置state状态下的文字 */ 15 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; 16 | 17 | /** 获取state状态下的title */ 18 | - (NSString *)titleForState:(MJRefreshState)state; 19 | @end 20 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshBackStateFooter.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/6/13. 6 | // Copyright © 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshBackStateFooter.h" 10 | 11 | @interface MJRefreshBackStateFooter() 12 | { 13 | /** 显示刷新状态的label */ 14 | __unsafe_unretained UILabel *_stateLabel; 15 | } 16 | /** 所有状态对应的文字 */ 17 | @property (strong, nonatomic) NSMutableDictionary *stateTitles; 18 | @end 19 | 20 | @implementation MJRefreshBackStateFooter 21 | #pragma mark - 懒加载 22 | - (NSMutableDictionary *)stateTitles 23 | { 24 | if (!_stateTitles) { 25 | self.stateTitles = [NSMutableDictionary dictionary]; 26 | } 27 | return _stateTitles; 28 | } 29 | 30 | - (UILabel *)stateLabel 31 | { 32 | if (!_stateLabel) { 33 | [self addSubview:_stateLabel = [UILabel label]]; 34 | } 35 | return _stateLabel; 36 | } 37 | 38 | #pragma mark - 公共方法 39 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state 40 | { 41 | if (title == nil) return; 42 | self.stateTitles[@(state)] = title; 43 | self.stateLabel.text = self.stateTitles[@(self.state)]; 44 | } 45 | 46 | - (NSString *)titleForState:(MJRefreshState)state { 47 | return self.stateTitles[@(state)]; 48 | } 49 | 50 | #pragma mark - 重写父类的方法 51 | - (void)prepare 52 | { 53 | [super prepare]; 54 | 55 | // 初始化文字 56 | [self setTitle:MJRefreshBackFooterIdleText forState:MJRefreshStateIdle]; 57 | [self setTitle:MJRefreshBackFooterPullingText forState:MJRefreshStatePulling]; 58 | [self setTitle:MJRefreshBackFooterRefreshingText forState:MJRefreshStateRefreshing]; 59 | [self setTitle:MJRefreshBackFooterNoMoreDataText forState:MJRefreshStateNoMoreData]; 60 | } 61 | 62 | - (void)placeSubviews 63 | { 64 | [super placeSubviews]; 65 | 66 | if (self.stateLabel.constraints.count) return; 67 | 68 | // 状态标签 69 | self.stateLabel.frame = self.bounds; 70 | } 71 | 72 | - (void)setState:(MJRefreshState)state 73 | { 74 | MJRefreshCheckState 75 | 76 | // 设置状态文字 77 | self.stateLabel.text = self.stateTitles[@(state)]; 78 | } 79 | @end 80 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshGifHeader.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshStateHeader.h" 10 | 11 | @interface MJRefreshGifHeader : MJRefreshStateHeader 12 | @property (weak, nonatomic, readonly) UIImageView *gifView; 13 | 14 | /** 设置state状态下的动画图片images 动画持续时间duration*/ 15 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; 16 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state; 17 | @end 18 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshGifHeader.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshGifHeader.h" 10 | 11 | @interface MJRefreshGifHeader() 12 | { 13 | __unsafe_unretained UIImageView *_gifView; 14 | } 15 | /** 所有状态对应的动画图片 */ 16 | @property (strong, nonatomic) NSMutableDictionary *stateImages; 17 | /** 所有状态对应的动画时间 */ 18 | @property (strong, nonatomic) NSMutableDictionary *stateDurations; 19 | @end 20 | 21 | @implementation MJRefreshGifHeader 22 | #pragma mark - 懒加载 23 | - (UIImageView *)gifView 24 | { 25 | if (!_gifView) { 26 | UIImageView *gifView = [[UIImageView alloc] init]; 27 | [self addSubview:_gifView = gifView]; 28 | } 29 | return _gifView; 30 | } 31 | 32 | - (NSMutableDictionary *)stateImages 33 | { 34 | if (!_stateImages) { 35 | self.stateImages = [NSMutableDictionary dictionary]; 36 | } 37 | return _stateImages; 38 | } 39 | 40 | - (NSMutableDictionary *)stateDurations 41 | { 42 | if (!_stateDurations) { 43 | self.stateDurations = [NSMutableDictionary dictionary]; 44 | } 45 | return _stateDurations; 46 | } 47 | 48 | #pragma mark - 公共方法 49 | - (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state 50 | { 51 | if (images == nil) return; 52 | 53 | self.stateImages[@(state)] = images; 54 | self.stateDurations[@(state)] = @(duration); 55 | 56 | /* 根据图片设置控件的高度 */ 57 | UIImage *image = [images firstObject]; 58 | if (image.size.height > self.mj_h) { 59 | self.mj_h = image.size.height; 60 | } 61 | } 62 | 63 | - (void)setImages:(NSArray *)images forState:(MJRefreshState)state 64 | { 65 | [self setImages:images duration:images.count * 0.1 forState:state]; 66 | } 67 | 68 | #pragma mark - 实现父类的方法 69 | - (void)setPullingPercent:(CGFloat)pullingPercent 70 | { 71 | [super setPullingPercent:pullingPercent]; 72 | NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; 73 | if (self.state != MJRefreshStateIdle || images.count == 0) return; 74 | // 停止动画 75 | [self.gifView stopAnimating]; 76 | // 设置当前需要显示的图片 77 | NSUInteger index = images.count * pullingPercent; 78 | if (index >= images.count) index = images.count - 1; 79 | self.gifView.image = images[index]; 80 | } 81 | 82 | - (void)placeSubviews 83 | { 84 | [super placeSubviews]; 85 | 86 | if (self.gifView.constraints.count) return; 87 | 88 | self.gifView.frame = self.bounds; 89 | if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) { 90 | self.gifView.contentMode = UIViewContentModeCenter; 91 | } else { 92 | self.gifView.contentMode = UIViewContentModeRight; 93 | self.gifView.mj_w = self.mj_w * 0.5 - 90; 94 | } 95 | } 96 | 97 | - (void)setState:(MJRefreshState)state 98 | { 99 | MJRefreshCheckState 100 | 101 | // 根据状态做事情 102 | if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { 103 | NSArray *images = self.stateImages[@(state)]; 104 | if (images.count == 0) return; 105 | 106 | [self.gifView stopAnimating]; 107 | if (images.count == 1) { // 单张图片 108 | self.gifView.image = [images lastObject]; 109 | } else { // 多张图片 110 | self.gifView.animationImages = images; 111 | self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; 112 | [self.gifView startAnimating]; 113 | } 114 | } else if (state == MJRefreshStateIdle) { 115 | [self.gifView stopAnimating]; 116 | } 117 | } 118 | @end 119 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshNormalHeader.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshStateHeader.h" 10 | 11 | @interface MJRefreshNormalHeader : MJRefreshStateHeader 12 | @property (weak, nonatomic, readonly) UIImageView *arrowView; 13 | /** 菊花的样式 */ 14 | @property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; 15 | @end 16 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshNormalHeader.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshNormalHeader.h" 10 | 11 | @interface MJRefreshNormalHeader() 12 | { 13 | __unsafe_unretained UIImageView *_arrowView; 14 | } 15 | @property (weak, nonatomic) UIActivityIndicatorView *loadingView; 16 | @end 17 | 18 | @implementation MJRefreshNormalHeader 19 | #pragma mark - 懒加载子控件 20 | - (UIImageView *)arrowView 21 | { 22 | if (!_arrowView) { 23 | UIImage *image = [UIImage imageNamed:MJRefreshSrcName(@"arrow.png")] ?: [UIImage imageNamed:MJRefreshFrameworkSrcName(@"arrow.png")]; 24 | UIImageView *arrowView = [[UIImageView alloc] initWithImage:image]; 25 | [self addSubview:_arrowView = arrowView]; 26 | } 27 | return _arrowView; 28 | } 29 | 30 | - (UIActivityIndicatorView *)loadingView 31 | { 32 | if (!_loadingView) { 33 | UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:self.activityIndicatorViewStyle]; 34 | loadingView.hidesWhenStopped = YES; 35 | [self addSubview:_loadingView = loadingView]; 36 | } 37 | return _loadingView; 38 | } 39 | 40 | #pragma mark - 公共方法 41 | - (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle 42 | { 43 | _activityIndicatorViewStyle = activityIndicatorViewStyle; 44 | 45 | self.loadingView = nil; 46 | [self setNeedsLayout]; 47 | } 48 | 49 | #pragma makr - 重写父类的方法 50 | - (void)prepare 51 | { 52 | [super prepare]; 53 | 54 | self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; 55 | } 56 | 57 | - (void)placeSubviews 58 | { 59 | [super placeSubviews]; 60 | 61 | // 箭头的中心点 62 | CGFloat arrowCenterX = self.mj_w * 0.5; 63 | if (!self.stateLabel.hidden) { 64 | arrowCenterX -= 100; 65 | } 66 | CGFloat arrowCenterY = self.mj_h * 0.5; 67 | CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); 68 | 69 | // 箭头 70 | if (self.arrowView.constraints.count == 0) { 71 | self.arrowView.mj_size = self.arrowView.image.size; 72 | self.arrowView.center = arrowCenter; 73 | } 74 | 75 | // 圈圈 76 | if (self.loadingView.constraints.count == 0) { 77 | self.loadingView.center = arrowCenter; 78 | } 79 | } 80 | 81 | - (void)setState:(MJRefreshState)state 82 | { 83 | MJRefreshCheckState 84 | 85 | // 根据状态做事情 86 | if (state == MJRefreshStateIdle) { 87 | if (oldState == MJRefreshStateRefreshing) { 88 | self.arrowView.transform = CGAffineTransformIdentity; 89 | 90 | [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{ 91 | self.loadingView.alpha = 0.0; 92 | } completion:^(BOOL finished) { 93 | // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态 94 | if (self.state != MJRefreshStateIdle) return; 95 | 96 | self.loadingView.alpha = 1.0; 97 | [self.loadingView stopAnimating]; 98 | self.arrowView.hidden = NO; 99 | }]; 100 | } else { 101 | [self.loadingView stopAnimating]; 102 | self.arrowView.hidden = NO; 103 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 104 | self.arrowView.transform = CGAffineTransformIdentity; 105 | }]; 106 | } 107 | } else if (state == MJRefreshStatePulling) { 108 | [self.loadingView stopAnimating]; 109 | self.arrowView.hidden = NO; 110 | [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{ 111 | self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); 112 | }]; 113 | } else if (state == MJRefreshStateRefreshing) { 114 | self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行 115 | [self.loadingView startAnimating]; 116 | self.arrowView.hidden = YES; 117 | } 118 | } 119 | @end 120 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshStateHeader.h 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshHeader.h" 10 | 11 | @interface MJRefreshStateHeader : MJRefreshHeader 12 | #pragma mark - 刷新时间相关 13 | /** 利用这个block来决定显示的更新时间文字 */ 14 | @property (copy, nonatomic) NSString *(^lastUpdatedTimeText)(NSDate *lastUpdatedTime); 15 | /** 显示上一次刷新时间的label */ 16 | @property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel; 17 | 18 | #pragma mark - 状态相关 19 | /** 显示刷新状态的label */ 20 | @property (weak, nonatomic, readonly) UILabel *stateLabel; 21 | /** 设置state状态下的文字 */ 22 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state; 23 | @end 24 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJRefreshStateHeader.m 3 | // MJRefreshExample 4 | // 5 | // Created by MJ Lee on 15/4/24. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJRefreshStateHeader.h" 10 | 11 | @interface MJRefreshStateHeader() 12 | { 13 | /** 显示上一次刷新时间的label */ 14 | __unsafe_unretained UILabel *_lastUpdatedTimeLabel; 15 | /** 显示刷新状态的label */ 16 | __unsafe_unretained UILabel *_stateLabel; 17 | } 18 | /** 所有状态对应的文字 */ 19 | @property (strong, nonatomic) NSMutableDictionary *stateTitles; 20 | @end 21 | 22 | @implementation MJRefreshStateHeader 23 | #pragma mark - 懒加载 24 | - (NSMutableDictionary *)stateTitles 25 | { 26 | if (!_stateTitles) { 27 | self.stateTitles = [NSMutableDictionary dictionary]; 28 | } 29 | return _stateTitles; 30 | } 31 | 32 | - (UILabel *)stateLabel 33 | { 34 | if (!_stateLabel) { 35 | [self addSubview:_stateLabel = [UILabel label]]; 36 | } 37 | return _stateLabel; 38 | } 39 | 40 | - (UILabel *)lastUpdatedTimeLabel 41 | { 42 | if (!_lastUpdatedTimeLabel) { 43 | [self addSubview:_lastUpdatedTimeLabel = [UILabel label]]; 44 | } 45 | return _lastUpdatedTimeLabel; 46 | } 47 | 48 | #pragma mark - 公共方法 49 | - (void)setTitle:(NSString *)title forState:(MJRefreshState)state 50 | { 51 | if (title == nil) return; 52 | self.stateTitles[@(state)] = title; 53 | self.stateLabel.text = self.stateTitles[@(self.state)]; 54 | } 55 | 56 | #pragma mark - 日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。 57 | - (NSCalendar *)currentCalendar { 58 | if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) { 59 | return [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; 60 | } 61 | return [NSCalendar currentCalendar]; 62 | } 63 | 64 | #pragma mark key的处理 65 | - (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey 66 | { 67 | [super setLastUpdatedTimeKey:lastUpdatedTimeKey]; 68 | 69 | // 如果label隐藏了,就不用再处理 70 | if (self.lastUpdatedTimeLabel.hidden) return; 71 | 72 | NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey]; 73 | 74 | // 如果有block 75 | if (self.lastUpdatedTimeText) { 76 | self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime); 77 | return; 78 | } 79 | 80 | if (lastUpdatedTime) { 81 | // 1.获得年月日 82 | NSCalendar *calendar = [self currentCalendar]; 83 | NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour |NSCalendarUnitMinute; 84 | NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime]; 85 | NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]]; 86 | 87 | // 2.格式化日期 88 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 89 | if ([cmp1 day] == [cmp2 day]) { // 今天 90 | formatter.dateFormat = @"今天 HH:mm"; 91 | } else if ([cmp1 year] == [cmp2 year]) { // 今年 92 | formatter.dateFormat = @"MM-dd HH:mm"; 93 | } else { 94 | formatter.dateFormat = @"yyyy-MM-dd HH:mm"; 95 | } 96 | NSString *time = [formatter stringFromDate:lastUpdatedTime]; 97 | 98 | // 3.显示日期 99 | self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"最后更新:%@", time]; 100 | } else { 101 | self.lastUpdatedTimeLabel.text = @"最后更新:无记录"; 102 | } 103 | } 104 | 105 | #pragma mark - 覆盖父类的方法 106 | - (void)prepare 107 | { 108 | [super prepare]; 109 | 110 | // 初始化文字 111 | [self setTitle:MJRefreshHeaderIdleText forState:MJRefreshStateIdle]; 112 | [self setTitle:MJRefreshHeaderPullingText forState:MJRefreshStatePulling]; 113 | [self setTitle:MJRefreshHeaderRefreshingText forState:MJRefreshStateRefreshing]; 114 | } 115 | 116 | - (void)placeSubviews 117 | { 118 | [super placeSubviews]; 119 | 120 | if (self.stateLabel.hidden) return; 121 | 122 | BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; 123 | 124 | if (self.lastUpdatedTimeLabel.hidden) { 125 | // 状态 126 | if (noConstrainsOnStatusLabel) self.stateLabel.frame = self.bounds; 127 | } else { 128 | CGFloat stateLabelH = self.mj_h * 0.5; 129 | // 状态 130 | if (noConstrainsOnStatusLabel) { 131 | self.stateLabel.mj_x = 0; 132 | self.stateLabel.mj_y = 0; 133 | self.stateLabel.mj_w = self.mj_w; 134 | self.stateLabel.mj_h = stateLabelH; 135 | } 136 | 137 | // 更新时间 138 | if (self.lastUpdatedTimeLabel.constraints.count == 0) { 139 | self.lastUpdatedTimeLabel.mj_x = 0; 140 | self.lastUpdatedTimeLabel.mj_y = stateLabelH; 141 | self.lastUpdatedTimeLabel.mj_w = self.mj_w; 142 | self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y; 143 | } 144 | } 145 | } 146 | 147 | - (void)setState:(MJRefreshState)state 148 | { 149 | MJRefreshCheckState 150 | 151 | // 设置状态文字 152 | self.stateLabel.text = self.stateTitles[@(state)]; 153 | 154 | // 重新设置key(重新显示时间) 155 | self.lastUpdatedTimeKey = self.lastUpdatedTimeKey; 156 | } 157 | @end 158 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxuan518/HXTechnologyBlogCrawler/0a2b6b0224ac0117877ef724c8fca5ec515d0bff/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/MJRefresh.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | 4 | #import "UIScrollView+MJRefresh.h" 5 | #import "UIScrollView+MJExtension.h" 6 | #import "UIView+MJExtension.h" 7 | 8 | #import "MJRefreshNormalHeader.h" 9 | #import "MJRefreshGifHeader.h" 10 | 11 | #import "MJRefreshBackNormalFooter.h" 12 | #import "MJRefreshBackGifFooter.h" 13 | #import "MJRefreshAutoNormalFooter.h" 14 | #import "MJRefreshAutoGifFooter.h" -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/MJRefreshConst.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | #import 4 | #import 5 | 6 | // 弱引用 7 | #define MJWeakSelf __weak typeof(self) weakSelf = self; 8 | 9 | // 日志输出 10 | #ifdef DEBUG 11 | #define MJRefreshLog(...) NSLog(__VA_ARGS__) 12 | #else 13 | #define MJRefreshLog(...) 14 | #endif 15 | 16 | // 过期提醒 17 | #define MJRefreshDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 18 | 19 | // 运行时objc_msgSend 20 | #define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__) 21 | #define MJRefreshMsgTarget(target) (__bridge void *)(target) 22 | 23 | // RGB颜色 24 | #define MJRefreshColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] 25 | 26 | // 文字颜色 27 | #define MJRefreshLabelTextColor MJRefreshColor(90, 90, 90) 28 | 29 | // 字体大小 30 | #define MJRefreshLabelFont [UIFont boldSystemFontOfSize:14] 31 | 32 | // 图片路径 33 | #define MJRefreshSrcName(file) [@"MJRefresh.bundle" stringByAppendingPathComponent:file] 34 | #define MJRefreshFrameworkSrcName(file) [@"Frameworks/MJRefresh.framework/MJRefresh.bundle" stringByAppendingPathComponent:file] 35 | 36 | // 常量 37 | UIKIT_EXTERN const CGFloat MJRefreshHeaderHeight; 38 | UIKIT_EXTERN const CGFloat MJRefreshFooterHeight; 39 | UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration; 40 | UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration; 41 | 42 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset; 43 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize; 44 | UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset; 45 | UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState; 46 | 47 | UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey; 48 | 49 | UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText; 50 | UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText; 51 | UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText; 52 | 53 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText; 54 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText; 55 | UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText; 56 | 57 | UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText; 58 | UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText; 59 | UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText; 60 | UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText; 61 | 62 | // 状态检查 63 | #define MJRefreshCheckState \ 64 | MJRefreshState oldState = self.state; \ 65 | if (state == oldState) return; \ 66 | [super setState:state]; 67 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/MJRefreshConst.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | #import 4 | 5 | const CGFloat MJRefreshHeaderHeight = 54.0; 6 | const CGFloat MJRefreshFooterHeight = 44.0; 7 | const CGFloat MJRefreshFastAnimationDuration = 0.25; 8 | const CGFloat MJRefreshSlowAnimationDuration = 0.4; 9 | 10 | NSString *const MJRefreshKeyPathContentOffset = @"contentOffset"; 11 | NSString *const MJRefreshKeyPathContentInset = @"contentInset"; 12 | NSString *const MJRefreshKeyPathContentSize = @"contentSize"; 13 | NSString *const MJRefreshKeyPathPanState = @"state"; 14 | 15 | NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey"; 16 | 17 | NSString *const MJRefreshHeaderIdleText = @"下拉可以刷新"; 18 | NSString *const MJRefreshHeaderPullingText = @"松开立即刷新"; 19 | NSString *const MJRefreshHeaderRefreshingText = @"正在刷新数据中..."; 20 | 21 | NSString *const MJRefreshAutoFooterIdleText = @"点击或上拉加载更多"; 22 | NSString *const MJRefreshAutoFooterRefreshingText = @"正在加载更多的数据..."; 23 | NSString *const MJRefreshAutoFooterNoMoreDataText = @"已经全部加载完毕"; 24 | 25 | NSString *const MJRefreshBackFooterIdleText = @"上拉可以加载更多"; 26 | NSString *const MJRefreshBackFooterPullingText = @"松开立即加载更多"; 27 | NSString *const MJRefreshBackFooterRefreshingText = @"正在加载更多的数据..."; 28 | NSString *const MJRefreshBackFooterNoMoreDataText = @"已经全部加载完毕"; -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // UIScrollView+Extension.h 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 14-5-28. 7 | // Copyright (c) 2014年 小码哥. All rights reserved. 8 | // 9 | 10 | #import 11 | 12 | @interface UIScrollView (MJExtension) 13 | @property (assign, nonatomic) CGFloat mj_insetT; 14 | @property (assign, nonatomic) CGFloat mj_insetB; 15 | @property (assign, nonatomic) CGFloat mj_insetL; 16 | @property (assign, nonatomic) CGFloat mj_insetR; 17 | 18 | @property (assign, nonatomic) CGFloat mj_offsetX; 19 | @property (assign, nonatomic) CGFloat mj_offsetY; 20 | 21 | @property (assign, nonatomic) CGFloat mj_contentW; 22 | @property (assign, nonatomic) CGFloat mj_contentH; 23 | @end 24 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // UIScrollView+Extension.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 14-5-28. 7 | // Copyright (c) 2014年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "UIScrollView+MJExtension.h" 11 | #import 12 | 13 | @implementation UIScrollView (MJExtension) 14 | 15 | - (void)setMj_insetT:(CGFloat)mj_insetT 16 | { 17 | UIEdgeInsets inset = self.contentInset; 18 | inset.top = mj_insetT; 19 | self.contentInset = inset; 20 | } 21 | 22 | - (CGFloat)mj_insetT 23 | { 24 | return self.contentInset.top; 25 | } 26 | 27 | - (void)setMj_insetB:(CGFloat)mj_insetB 28 | { 29 | UIEdgeInsets inset = self.contentInset; 30 | inset.bottom = mj_insetB; 31 | self.contentInset = inset; 32 | } 33 | 34 | - (CGFloat)mj_insetB 35 | { 36 | return self.contentInset.bottom; 37 | } 38 | 39 | - (void)setMj_insetL:(CGFloat)mj_insetL 40 | { 41 | UIEdgeInsets inset = self.contentInset; 42 | inset.left = mj_insetL; 43 | self.contentInset = inset; 44 | } 45 | 46 | - (CGFloat)mj_insetL 47 | { 48 | return self.contentInset.left; 49 | } 50 | 51 | - (void)setMj_insetR:(CGFloat)mj_insetR 52 | { 53 | UIEdgeInsets inset = self.contentInset; 54 | inset.right = mj_insetR; 55 | self.contentInset = inset; 56 | } 57 | 58 | - (CGFloat)mj_insetR 59 | { 60 | return self.contentInset.right; 61 | } 62 | 63 | - (void)setMj_offsetX:(CGFloat)mj_offsetX 64 | { 65 | CGPoint offset = self.contentOffset; 66 | offset.x = mj_offsetX; 67 | self.contentOffset = offset; 68 | } 69 | 70 | - (CGFloat)mj_offsetX 71 | { 72 | return self.contentOffset.x; 73 | } 74 | 75 | - (void)setMj_offsetY:(CGFloat)mj_offsetY 76 | { 77 | CGPoint offset = self.contentOffset; 78 | offset.y = mj_offsetY; 79 | self.contentOffset = offset; 80 | } 81 | 82 | - (CGFloat)mj_offsetY 83 | { 84 | return self.contentOffset.y; 85 | } 86 | 87 | - (void)setMj_contentW:(CGFloat)mj_contentW 88 | { 89 | CGSize size = self.contentSize; 90 | size.width = mj_contentW; 91 | self.contentSize = size; 92 | } 93 | 94 | - (CGFloat)mj_contentW 95 | { 96 | return self.contentSize.width; 97 | } 98 | 99 | - (void)setMj_contentH:(CGFloat)mj_contentH 100 | { 101 | CGSize size = self.contentSize; 102 | size.height = mj_contentH; 103 | self.contentSize = size; 104 | } 105 | 106 | - (CGFloat)mj_contentH 107 | { 108 | return self.contentSize.height; 109 | } 110 | @end 111 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // UIScrollView+MJRefresh.h 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 给ScrollView增加下拉刷新、上拉刷新的功能 9 | 10 | #import 11 | #import "MJRefreshConst.h" 12 | 13 | @class MJRefreshHeader, MJRefreshFooter; 14 | 15 | @interface UIScrollView (MJRefresh) 16 | /** 下拉刷新控件 */ 17 | @property (strong, nonatomic) MJRefreshHeader *mj_header; 18 | @property (strong, nonatomic) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header"); 19 | /** 上拉刷新控件 */ 20 | @property (strong, nonatomic) MJRefreshFooter *mj_footer; 21 | @property (strong, nonatomic) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer"); 22 | 23 | #pragma mark - other 24 | - (NSInteger)mj_totalDataCount; 25 | @property (copy, nonatomic) void (^mj_reloadDataBlock)(NSInteger totalDataCount); 26 | @end 27 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // UIScrollView+MJRefresh.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 15/3/4. 7 | // Copyright (c) 2015年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "UIScrollView+MJRefresh.h" 11 | #import "MJRefreshHeader.h" 12 | #import "MJRefreshFooter.h" 13 | #import 14 | 15 | @implementation NSObject (MJRefresh) 16 | 17 | + (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2 18 | { 19 | method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2)); 20 | } 21 | 22 | + (void)exchangeClassMethod1:(SEL)method1 method2:(SEL)method2 23 | { 24 | method_exchangeImplementations(class_getClassMethod(self, method1), class_getClassMethod(self, method2)); 25 | } 26 | 27 | @end 28 | 29 | @implementation UIScrollView (MJRefresh) 30 | 31 | #pragma mark - header 32 | static const char MJRefreshHeaderKey = '\0'; 33 | - (void)setMj_header:(MJRefreshHeader *)mj_header 34 | { 35 | if (mj_header != self.mj_header) { 36 | // 删除旧的,添加新的 37 | [self.mj_header removeFromSuperview]; 38 | [self insertSubview:mj_header atIndex:0]; 39 | 40 | // 存储新的 41 | [self willChangeValueForKey:@"mj_header"]; // KVO 42 | objc_setAssociatedObject(self, &MJRefreshHeaderKey, 43 | mj_header, OBJC_ASSOCIATION_ASSIGN); 44 | [self didChangeValueForKey:@"mj_header"]; // KVO 45 | } 46 | } 47 | 48 | - (MJRefreshHeader *)mj_header 49 | { 50 | return objc_getAssociatedObject(self, &MJRefreshHeaderKey); 51 | } 52 | 53 | #pragma mark - footer 54 | static const char MJRefreshFooterKey = '\0'; 55 | - (void)setMj_footer:(MJRefreshFooter *)mj_footer 56 | { 57 | if (mj_footer != self.mj_footer) { 58 | // 删除旧的,添加新的 59 | [self.mj_footer removeFromSuperview]; 60 | [self addSubview:mj_footer]; 61 | 62 | // 存储新的 63 | [self willChangeValueForKey:@"mj_footer"]; // KVO 64 | objc_setAssociatedObject(self, &MJRefreshFooterKey, 65 | mj_footer, OBJC_ASSOCIATION_ASSIGN); 66 | [self didChangeValueForKey:@"mj_footer"]; // KVO 67 | } 68 | } 69 | 70 | - (MJRefreshFooter *)mj_footer 71 | { 72 | return objc_getAssociatedObject(self, &MJRefreshFooterKey); 73 | } 74 | 75 | #pragma mark - 过期 76 | - (void)setFooter:(MJRefreshFooter *)footer 77 | { 78 | self.mj_footer = footer; 79 | } 80 | 81 | - (MJRefreshFooter *)footer 82 | { 83 | return self.mj_footer; 84 | } 85 | 86 | - (void)setHeader:(MJRefreshHeader *)header 87 | { 88 | self.mj_header = header; 89 | } 90 | 91 | - (MJRefreshHeader *)header 92 | { 93 | return self.mj_header; 94 | } 95 | 96 | #pragma mark - other 97 | - (NSInteger)mj_totalDataCount 98 | { 99 | NSInteger totalCount = 0; 100 | if ([self isKindOfClass:[UITableView class]]) { 101 | UITableView *tableView = (UITableView *)self; 102 | 103 | for (NSInteger section = 0; section 11 | 12 | @interface UIView (MJExtension) 13 | @property (assign, nonatomic) CGFloat mj_x; 14 | @property (assign, nonatomic) CGFloat mj_y; 15 | @property (assign, nonatomic) CGFloat mj_w; 16 | @property (assign, nonatomic) CGFloat mj_h; 17 | @property (assign, nonatomic) CGSize mj_size; 18 | @property (assign, nonatomic) CGPoint mj_origin; 19 | @end 20 | -------------------------------------------------------------------------------- /Pods/MJRefresh/MJRefresh/UIView+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 代码地址: https://github.com/CoderMJLee/MJRefresh 2 | // 代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000 3 | // UIView+Extension.m 4 | // MJRefreshExample 5 | // 6 | // Created by MJ Lee on 14-5-28. 7 | // Copyright (c) 2014年 小码哥. All rights reserved. 8 | // 9 | 10 | #import "UIView+MJExtension.h" 11 | 12 | @implementation UIView (MJExtension) 13 | - (void)setMj_x:(CGFloat)mj_x 14 | { 15 | CGRect frame = self.frame; 16 | frame.origin.x = mj_x; 17 | self.frame = frame; 18 | } 19 | 20 | - (CGFloat)mj_x 21 | { 22 | return self.frame.origin.x; 23 | } 24 | 25 | - (void)setMj_y:(CGFloat)mj_y 26 | { 27 | CGRect frame = self.frame; 28 | frame.origin.y = mj_y; 29 | self.frame = frame; 30 | } 31 | 32 | - (CGFloat)mj_y 33 | { 34 | return self.frame.origin.y; 35 | } 36 | 37 | - (void)setMj_w:(CGFloat)mj_w 38 | { 39 | CGRect frame = self.frame; 40 | frame.size.width = mj_w; 41 | self.frame = frame; 42 | } 43 | 44 | - (CGFloat)mj_w 45 | { 46 | return self.frame.size.width; 47 | } 48 | 49 | - (void)setMj_h:(CGFloat)mj_h 50 | { 51 | CGRect frame = self.frame; 52 | frame.size.height = mj_h; 53 | self.frame = frame; 54 | } 55 | 56 | - (CGFloat)mj_h 57 | { 58 | return self.frame.size.height; 59 | } 60 | 61 | - (void)setMj_size:(CGSize)mj_size 62 | { 63 | CGRect frame = self.frame; 64 | frame.size = mj_size; 65 | self.frame = frame; 66 | } 67 | 68 | - (CGSize)mj_size 69 | { 70 | return self.frame.size; 71 | } 72 | 73 | - (void)setMj_origin:(CGPoint)mj_origin 74 | { 75 | CGRect frame = self.frame; 76 | frame.origin = mj_origin; 77 | self.frame = frame; 78 | } 79 | 80 | - (CGPoint)mj_origin 81 | { 82 | return self.frame.origin; 83 | } 84 | @end 85 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MJRefresh (3.1.0) 3 | 4 | DEPENDENCIES: 5 | - MJRefresh 6 | 7 | SPEC CHECKSUMS: 8 | MJRefresh: 743e6404967d1c2c688472ea3ecfde247d872db4 9 | 10 | PODFILE CHECKSUM: 3e97bbe32e7bf5574669b64ec6a6420fb7521e7f 11 | 12 | COCOAPODS: 1.0.0 13 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcschemes/MJRefresh.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcschemes/Pods-HXTechnologyBlogCrawler.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/huangxuan518.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MJRefresh.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-HXTechnologyBlogCrawler.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 8F74D9EA91F4C43190670066BAC37D44 21 | 22 | primary 23 | 24 | 25 | CAADC4FED420E6FE6B6A49C46F4FAFC6 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MJRefresh : NSObject 3 | @end 4 | @implementation PodsDummy_MJRefresh 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/MJRefresh/MJRefresh.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/MJRefresh 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJRefresh" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJRefresh" 4 | PODS_BUILD_DIR = $BUILD_DIR 5 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 8 | SKIP_INSTALL = YES 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## MJRefresh 5 | 6 | Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | MJRefresh 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - https://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_HXTechnologyBlogCrawler : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_HXTechnologyBlogCrawler 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | realpath() { 27 | DIRECTORY="$(cd "${1%/*}" && pwd)" 28 | FILENAME="${1##*/}" 29 | echo "$DIRECTORY/$FILENAME" 30 | } 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH") 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | if [[ "$CONFIGURATION" == "Debug" ]]; then 83 | install_resource "MJRefresh/MJRefresh/MJRefresh.bundle" 84 | fi 85 | if [[ "$CONFIGURATION" == "Release" ]]; then 86 | install_resource "MJRefresh/MJRefresh/MJRefresh.bundle" 87 | fi 88 | 89 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 90 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 92 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 93 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 94 | fi 95 | rm -f "$RESOURCES_TO_COPY" 96 | 97 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 98 | then 99 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 100 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 101 | while read line; do 102 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 103 | XCASSET_FILES+=("$line") 104 | fi 105 | done <<<"$OTHER_XCASSETS" 106 | 107 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 108 | fi 109 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJRefresh" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/MJRefresh" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/MJRefresh" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MJRefresh" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-HXTechnologyBlogCrawler/Pods-HXTechnologyBlogCrawler.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJRefresh" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/MJRefresh" 4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/MJRefresh" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MJRefresh" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HXTechnologyBlogCrawler 如果对你有一点点帮助,请给一颗★,你的支持是对我的最大鼓励! 2 | 技术博客搜索爬虫 3 | 4 | # 演示地址 5 | https://appetize.io/app/1511my7qa48yeeacwb2t4ycd2m 6 | 7 | # 预览图 8 | ![image](https://github.com/huangxuan518/HXTechnologyBlogCrawler/blob/master/HXTechnologyBlogCrawler/xiaoguo.gif) 9 | 10 | --------------------------------------------------------------------------------