├── .gitignore ├── Gank.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Gank.xcworkspace └── contents.xcworkspacedata ├── Gank ├── AHConfig.swift ├── AHHeader.swift ├── AHMainViewController.swift ├── AHNetworking │ ├── AHNetWorking.swift │ └── AHNewWork.swift ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon20@2x.png │ │ ├── icon20@3x.png │ │ ├── icon29@2x.png │ │ ├── icon29@3x.png │ │ ├── icon40@2x.png │ │ ├── icon40@3x.png │ │ ├── icon60@2x.png │ │ └── icon60@3x.png │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── iPhone4@2x.png │ │ ├── iPhone5@2x.png │ │ ├── iPhone6@2x.png │ │ └── iPhone6p@3x.png │ └── popover_background_right.imageset │ │ ├── Contents.json │ │ └── popover_background_right@2x.png ├── Base │ ├── AHLaunchViewController.swift │ ├── BaseViewController.swift │ ├── BaseWebViewController.swift │ ├── TabBarController.swift │ └── User.swift ├── Class │ ├── Gank │ │ ├── AHBrowserImageView.swift │ │ ├── AHClassCell.swift │ │ ├── AHClassCell.xib │ │ ├── AHClassViewController.swift │ │ ├── AHClassWebViewController.swift │ │ ├── AHGankViewController.swift │ │ ├── AHImageCell.swift │ │ ├── AHImageCell.xib │ │ ├── AHPhotoBrowser.swift │ │ ├── AHWaitingView.swift │ │ ├── AHWebToolView.swift │ │ └── AHWebToolView.xib │ ├── Home │ │ ├── AHHomeViewController.swift │ │ ├── AHHomeWebViewController.swift │ │ └── View │ │ │ ├── AHHeaderSectionView.swift │ │ │ ├── AHHeaderSectionView.xib │ │ │ ├── AHHomeCell.swift │ │ │ ├── AHHomeCell.xib │ │ │ ├── AHHomeHeaderView.swift │ │ │ ├── AHHomeHeaderView.xib │ │ │ ├── AHSearchView.swift │ │ │ └── AHSearchView.xib │ ├── Login │ │ ├── AHLoginViewController.swift │ │ ├── AHLoginViewController.xib │ │ ├── AHRegisterViewController.swift │ │ ├── AHRegisterViewController.xib │ │ ├── AHResetPasswordController.swift │ │ └── AHResetPasswordController.xib │ ├── Mine │ │ ├── AHCollectViewController.swift │ │ ├── AHFeedbackViewController.swift │ │ ├── AHFeedbackViewController.xib │ │ ├── AHMineViewController.swift │ │ ├── AHNoLoginCell.swift │ │ ├── AHNoLoginCell.xib │ │ ├── AHPersonalPageViewController.swift │ │ ├── AHSettingViewController.swift │ │ ├── AHUpdateNickViewController.swift │ │ ├── AHUpdateNickViewController.xib │ │ ├── AHUserCell.swift │ │ ├── AHUserCell.xib │ │ ├── Setting │ │ │ ├── AHAccountSafeViewController.swift │ │ │ └── AHAccountSafeViewController.xib │ │ └── View │ │ │ ├── AHCollectCell.swift │ │ │ └── AHCollectCell.xib │ ├── Search │ │ ├── AHSearchCell.swift │ │ ├── AHSearchCell.xib │ │ ├── AHSearchListView.swift │ │ ├── AHSearchViewController.swift │ │ ├── AHSearchViewController.xib │ │ └── AHSeatchTagBtn.swift │ └── TurnChannel │ │ ├── AHListView.swift │ │ ├── AHListViewPotocol.swift │ │ ├── AHMoreListView.swift │ │ ├── AHTagBtn.swift │ │ └── AHTurnChannelViewController.swift ├── Extension │ ├── Extension.swift │ ├── GlobalFunction.swift │ ├── String+Extension.swift │ ├── UIView+Extension.swift │ └── Validate.swift ├── Gank-Bridging-Header.h ├── Info.plist ├── LaunchScreen.storyboard ├── Model │ ├── AHClassModel.swift │ ├── AHHomeGankModel.swift │ ├── AHHomeGroupModel.swift │ ├── AHSearchGankModel.swift │ └── GankModel.swift ├── Potocol │ ├── ResuableProtocol.swift │ └── UserDefaultsProtocol.swift ├── Resource │ ├── add_button_normal@2x.png │ ├── adver.png │ ├── back@2x.png │ ├── close2_button@2x.png │ ├── close_button@2x.png │ ├── find@2x.png │ ├── find_select@2x.png │ ├── icon108.png │ ├── icon_class@2x.png │ ├── icon_clean@2x.png │ ├── icon_close_block@2x.png │ ├── icon_close_second@2x.png │ ├── icon_collect@2x.png │ ├── icon_collected@2x.png │ ├── icon_copy@2x.png │ ├── icon_editor@2x.png │ ├── icon_more@2x.png │ ├── icon_placeHead1@2x.png │ ├── icon_placeHead@2x.png │ ├── icon_safari@2x.png │ ├── icon_search@2x.png │ ├── icon_search_ gray@2x.png │ ├── icon_search_blue@2x.png │ ├── icon_setting@2x.png │ ├── icon_share@2x.png │ ├── icon_show@2x.png │ ├── icon_show_blue@2x.png │ ├── icon_time@2x.png │ ├── icon_user1@2x.png │ ├── icon_user@2x.png │ ├── loading1.png │ ├── loading2.png │ ├── loading3.png │ ├── loading4.png │ ├── loading5.png │ ├── loading6.png │ ├── loading7.png │ ├── loading8.png │ ├── loading9.png │ ├── nav_ list@2x.png │ ├── nav_back@2x.png │ ├── nav_back_blue@2x.png │ ├── placeImage@2x.png │ ├── property@2x.png │ ├── property_select@2x.png │ ├── tabBar_bgwhiteColor@2x.png │ ├── tabbar_home@2x.png │ ├── tabbar_home_highlighted@2x.png │ ├── toolBar_back@2x.png │ ├── toolBar_forward@2x.png │ ├── toolBar_like@2x.png │ ├── toolBar_like_select@2x.png │ └── toolBar_reload@2x.png └── Utility │ ├── AHDisplayViewController.swift │ ├── AHLaunchImageView.swift │ ├── AHLoadingView.swift │ ├── AHMoreCell.swift │ ├── AHMoreCell.xib │ ├── AHMoreView.swift │ ├── AHMoreView.xib │ ├── AHNavBar.swift │ ├── AHNoDataView.swift │ ├── AHTimeButton.swift │ ├── FPSLabel.h │ ├── FPSLabel.m │ ├── NSObject+alertController.swift │ ├── SQLite │ └── AHGankDAO.swift │ ├── SQLiteManager.swift │ ├── Transtion │ ├── AHClosePopTranstion.swift │ ├── AHPopTranstion.swift │ ├── AHPresentationController.swift │ ├── AHPushTransition.swift │ ├── AHTurnVCTransitionManager.swift │ └── ToolKit.swift │ └── UserDefaultsEVO.swift ├── GankTests ├── GankTests.swift └── Info.plist ├── GankUITests ├── GankUITests.swift └── Info.plist ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods └── Manifest.lock ├── README.md └── fastlane ├── Appfile ├── Fastfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /Gank.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Gank.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Gank/AHConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHConfig.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | 12 | let kWindow = UIApplication.shared.delegate!.window! 13 | 14 | let kScreen_BOUNDS = UIScreen.main.bounds 15 | let kScreen_W = UIScreen.main.bounds.size.width 16 | let kScreen_H = UIScreen.main.bounds.size.height 17 | let kScreen_Scale = UIScreen.main.scale 18 | 19 | let kStatusBarHeight: CGFloat = 20.0 20 | let kTopBarHeight: CGFloat = 44.0 21 | let kNavBarHeight: CGFloat = 64.0 22 | let kBottomBarHeight: CGFloat = 49.0 23 | 24 | let iPhone4_Width = 320.0 25 | let iPhone4_Height = 480.0 26 | let iPhone5_Width = 320.0 27 | let iPhone5_Height = 568.0 28 | let iPhone6_Width = 375.0 29 | let iPhone6_Height = 667.0 30 | let iPhone6Plus_Width = 414.0 31 | let iPhone6Plus_Height = 736.0 32 | 33 | let UIColorMainBG = RGBColor(r: 239, g: 239, b: 245, alpha: 1) 34 | let UIColorTextLightGray = RGBColor(r: 153, g: 153, b: 153, alpha: 1) 35 | let UIColorTextGray = RGBColor(r: 99, g: 99, b: 99, alpha: 1) 36 | let UIColorTextBlock = RGBColor(r: 47, g: 47, b: 47, alpha: 1) 37 | let UIColorTextBlue = RGBColor(r: 40, g: 154, b: 236, alpha: 1) 38 | let UIColorMainBlue = RGBColor(r: 30, g: 130, b: 210, alpha: 1) 39 | let UIColorLine = RGBColor(r: 217, g: 217, b: 217, alpha: 1) 40 | 41 | typealias JSONObject = [String: Any] 42 | 43 | struct AHConfig { 44 | // MARK: - 服务器地址 45 | static let Http_ = "http://gank.io/api/" 46 | } 47 | 48 | // MARK: - 通知通用字段 49 | extension NSNotification.Name { 50 | // 点击tabBar item 发出的通知 51 | static let AHTabBarDidSelectNotification = NSNotification.Name(rawValue: "AHTabBarDidSelectNotification") 52 | // 隐藏或显示statusBar发出的通知 53 | static let AHChangeStatusBarNotification = NSNotification.Name(rawValue: "AHChangeStatusBarNotification") 54 | } 55 | 56 | // MARK: - UserDefaults通用字段 57 | typealias UserDefaultsSettable = StringDefaultSettable & BoolDefaultSettable 58 | 59 | struct UserConfig: UserDefaultsSettable { 60 | enum StringKey: String { 61 | case mobilePhoneNumber /// 上一次登录的手机号 62 | case lastDate /// 首页缓存数据的日期 63 | } 64 | 65 | enum BoolKey: String { 66 | case isOnlyWifiDownPic /// 仅在WiFi下载图片开关的状态 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Gank/AHHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHeader.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/13. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import Alamofire 12 | import SwiftyJSON 13 | -------------------------------------------------------------------------------- /Gank/AHMainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHMainViewController.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHMainViewController: BaseViewController { 12 | 13 | lazy var tabBarVC: TabBarController = { 14 | let tabBarVC = TabBarController() 15 | tabBarVC.delegate = self 16 | return tabBarVC 17 | }() 18 | 19 | lazy var LaunchVC: AHLaunchViewController = { 20 | let LaunchVC = AHLaunchViewController(showTime: 3) 21 | LaunchVC.launchComplete = { [unowned self] in 22 | self.view.addSubview(self.tabBarVC.view) 23 | self.addChildViewController(self.tabBarVC) 24 | } 25 | return LaunchVC 26 | }() 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | setupLaunchVC() 31 | } 32 | 33 | override func didReceiveMemoryWarning() { 34 | super.didReceiveMemoryWarning() 35 | } 36 | 37 | func setupLaunchVC() { 38 | addChildViewController(LaunchVC) 39 | view.addSubview(LaunchVC.view) 40 | } 41 | } 42 | 43 | extension AHMainViewController: UITabBarControllerDelegate { 44 | func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { 45 | NotificationCenter.default.post(name: NSNotification.Name.AHTabBarDidSelectNotification, object: nil) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Gank/AHNetworking/AHNetWorking.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHNetWorking.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/13. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Alamofire 11 | 12 | typealias Success = (Any) -> Void 13 | typealias Failure = (Error) -> Void 14 | 15 | protocol AHNetWorking { } 16 | 17 | extension AHNetWorking { 18 | static func requestData(_ url: String, method: HTTPMethod = .get, parameters: JSONObject? = nil, success: @escaping Success, failure: @escaping Failure) { 19 | 20 | guard let urlString = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return } 21 | 22 | Alamofire.request(urlString, method: method, parameters: parameters).responseJSON { (response) in 23 | switch response.result { 24 | case .success: 25 | if let result = response.result.value { 26 | success(result) 27 | } 28 | case .failure(let error): 29 | failure(error) 30 | } 31 | } 32 | } 33 | 34 | static func cancelAllRequest() { 35 | Alamofire.SessionManager().session.getAllTasks { (tasks) -> Void in 36 | tasks.forEach({ $0.cancel() }) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Gank/AHNetworking/AHNewWork.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHNewWork.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/13. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | struct AHNewWork: AHNetWorking { 13 | /// 请求干货页面数据 14 | static func loadClassRequest(tpye: String, page: Int, success: @escaping Success, failure: @escaping Failure) { 15 | let url = AHConfig.Http_ + "data/\(tpye)/20/\(page)" 16 | 17 | requestData(url, success: { (result: Any) in 18 | 19 | // 缓存第一页的数据 20 | if page == 1 { 21 | AHGankDAO.cleanCacheGanks(type: tpye) 22 | let json = result as! JSONObject 23 | let array = json["results"] as! [JSONObject] 24 | AHGankDAO.cacheGanks(type: tpye, ganks: array) 25 | } 26 | 27 | let dicts = JSON(result) 28 | let datas: [AHClassModel] 29 | 30 | // 创建一个组队列 31 | let group = DispatchGroup() 32 | let urlconfig = URLSessionConfiguration.default 33 | urlconfig.timeoutIntervalForRequest = 2 34 | urlconfig.timeoutIntervalForResource = 2 35 | 36 | datas = dicts["results"].arrayValue.map({ dict in 37 | let model = AHClassModel(dict: dict) 38 | 39 | if let images = model.images, model.images?.count == 1 { 40 | let urlString = images.first! + "?imageInfo" 41 | let url = URL(string: urlString) 42 | 43 | let session = URLSession(configuration: urlconfig) 44 | // 当前线程加入组队列 45 | group.enter() 46 | let tast = session.dataTask(with: url!, completionHandler: { (data: Data?, _, error: Error?) in 47 | if let data = data { 48 | let json = JSON(data: data) 49 | if let width = json["width"].object as? CGFloat { 50 | model.imageW = width 51 | } 52 | if let height = json["height"].object as? CGFloat { 53 | model.imageH = height 54 | } 55 | } 56 | // 当前线程离开组队列 57 | group.leave() 58 | }) 59 | tast.resume() 60 | // 防止内存泄漏 61 | session.finishTasksAndInvalidate() 62 | } 63 | return model 64 | }) 65 | 66 | // 等组队列执行完, 在主线程回调 67 | group.notify(queue: DispatchQueue.main, execute: { 68 | success(datas) 69 | }) 70 | 71 | }, failure: { (error: Error) in 72 | failure(error) 73 | }) 74 | } 75 | 76 | /// 请求首页数据 77 | static func loadHomeRequest(date: String, success: @escaping Success, failure: @escaping Failure) { 78 | let url = AHConfig.Http_ + "day/\(date)" 79 | 80 | requestData(url, success: { (result: Any) in 81 | let dicts = JSON(result) 82 | var datas = [AHHomeGroupModel]() 83 | 84 | for dict in dicts["category"].arrayValue { 85 | let groupTitle = dict.stringValue 86 | let groupModel = AHHomeGroupModel(dict: dicts["results"], key: groupTitle) 87 | datas.append(groupModel) 88 | } 89 | 90 | // 缓存首页数据 91 | NSKeyedArchiver.archiveRootObject(datas, toFile: "homeGanks".cachesDir()) 92 | 93 | success(datas) 94 | 95 | }) { (error: Error) in 96 | failure(error) 97 | } 98 | } 99 | 100 | /// 请求搜索数据 101 | static func loadSearchRequest(text: String, page: Int, success: @escaping Success, failure: @escaping Failure) { 102 | let url = AHConfig.Http_ + "search/query/\(text)/category/all/count/20/page/\(page)" 103 | 104 | requestData(url, success: { (result: Any) in 105 | let dicts = JSON(result) 106 | let datas: [AHSearchGankModel] 107 | 108 | datas = dicts["results"].arrayValue.map({ dict in 109 | AHSearchGankModel(dict: dict) 110 | }) 111 | 112 | success(datas) 113 | 114 | }) { (error: Error) in 115 | failure(error) 116 | } 117 | } 118 | 119 | /// 请求发过干货日期数据 120 | static func loadDateRequest(success: @escaping Success, failure: @escaping Failure) { 121 | let url = AHConfig.Http_ + "day/history" 122 | 123 | requestData(url, success: { (result: Any) in 124 | let json = result as! JSONObject 125 | guard let dateArray = json["results"] else { return } 126 | success(dateArray) 127 | }) { (error: Error) in 128 | failure(error) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "icon60@3x.png", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon20@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon20@3x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon29@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon29@3x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon40@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon40@3x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon60@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/AppIcon.appiconset/icon60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/AppIcon.appiconset/icon60@3x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Gank/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "736h", 7 | "filename" : "iPhone6p@3x.png", 8 | "minimum-system-version" : "8.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "667h", 16 | "filename" : "iPhone6@2x.png", 17 | "minimum-system-version" : "8.0", 18 | "orientation" : "portrait", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "orientation" : "portrait", 23 | "idiom" : "iphone", 24 | "filename" : "iPhone4@2x.png", 25 | "extent" : "full-screen", 26 | "minimum-system-version" : "7.0", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "extent" : "full-screen", 31 | "idiom" : "iphone", 32 | "subtype" : "retina4", 33 | "filename" : "iPhone5@2x.png", 34 | "minimum-system-version" : "7.0", 35 | "orientation" : "portrait", 36 | "scale" : "2x" 37 | } 38 | ], 39 | "info" : { 40 | "version" : 1, 41 | "author" : "xcode" 42 | } 43 | } -------------------------------------------------------------------------------- /Gank/Assets.xcassets/LaunchImage.launchimage/iPhone4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/LaunchImage.launchimage/iPhone4@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/LaunchImage.launchimage/iPhone5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/LaunchImage.launchimage/iPhone5@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/LaunchImage.launchimage/iPhone6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/LaunchImage.launchimage/iPhone6@2x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/LaunchImage.launchimage/iPhone6p@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/LaunchImage.launchimage/iPhone6p@3x.png -------------------------------------------------------------------------------- /Gank/Assets.xcassets/popover_background_right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "resizing" : { 9 | "mode" : "9-part", 10 | "center" : { 11 | "mode" : "tile", 12 | "width" : 1, 13 | "height" : 1 14 | }, 15 | "cap-insets" : { 16 | "bottom" : 60, 17 | "top" : 63, 18 | "right" : 172, 19 | "left" : 147 20 | } 21 | }, 22 | "idiom" : "universal", 23 | "filename" : "popover_background_right@2x.png", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "idiom" : "universal", 28 | "scale" : "3x" 29 | } 30 | ], 31 | "info" : { 32 | "version" : 1, 33 | "author" : "xcode" 34 | } 35 | } -------------------------------------------------------------------------------- /Gank/Assets.xcassets/popover_background_right.imageset/popover_background_right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Assets.xcassets/popover_background_right.imageset/popover_background_right@2x.png -------------------------------------------------------------------------------- /Gank/Base/AHLaunchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHLaunchViewController.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/8. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias AHLaunchComplete = () -> (Void) 12 | 13 | class AHLaunchViewController: BaseViewController { 14 | 15 | var launchComplete: AHLaunchComplete? 16 | var showTime: Int 17 | 18 | lazy var skipBtn: AHTimeButton = { 19 | let btnW: CGFloat = 50.0 20 | let btnH: CGFloat = 30.0 21 | let margin: CGFloat = 20.0 22 | let btnF = CGRect(x: kScreen_W - btnW - margin, y: margin, width: btnW, height: btnH) 23 | let skipBtn = AHTimeButton(frame: btnF, time: self.showTime, clickAction: { 24 | self.skipAction() 25 | }) 26 | return skipBtn 27 | }() 28 | 29 | lazy var LaunchImageView: AHLaunchImageView = { 30 | let LaunchImageView = AHLaunchImageView(frame: kScreen_BOUNDS) 31 | return LaunchImageView 32 | }() 33 | 34 | init(showTime: Int) { 35 | self.showTime = showTime 36 | super.init(nibName: nil, bundle: nil) 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | setupUI() 47 | } 48 | 49 | override func didReceiveMemoryWarning() { 50 | super.didReceiveMemoryWarning() 51 | } 52 | 53 | func setupUI() { 54 | view.addSubview(skipBtn) 55 | view.insertSubview(LaunchImageView, belowSubview: skipBtn) 56 | } 57 | 58 | func skipAction() { 59 | if launchComplete != nil { 60 | launchComplete!() 61 | } 62 | launchComplete = nil 63 | view.removeFromSuperview() 64 | removeFromParentViewController() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Gank/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | 10 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 11 | switch (lhs, rhs) { 12 | case let (l?, r?): 13 | return l < r 14 | case (nil, _?): 15 | return true 16 | default: 17 | return false 18 | } 19 | } 20 | 21 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 22 | switch (lhs, rhs) { 23 | case let (l?, r?): 24 | return l > r 25 | default: 26 | return rhs < lhs 27 | } 28 | } 29 | 30 | import UIKit 31 | 32 | class BaseViewController: UIViewController, UIGestureRecognizerDelegate { 33 | 34 | // MARK: - property 35 | var popClosure: (() -> Void)? 36 | 37 | enum navBarBackItem { 38 | case blue 39 | case white 40 | } 41 | 42 | // MARK: - life cycle 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | view.backgroundColor = UIColorMainBG 46 | 47 | setupNav() 48 | } 49 | 50 | override func viewWillAppear(_ animated: Bool) { 51 | super.viewWillAppear(animated) 52 | } 53 | 54 | override func viewDidAppear(_ animated: Bool) { 55 | super.viewDidAppear(animated) 56 | if self.navigationController?.viewControllers.count == 1 { 57 | self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false 58 | } 59 | else { 60 | self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true 61 | } 62 | } 63 | 64 | override func viewDidDisappear(_ animated: Bool) { 65 | super.viewDidDisappear(animated) 66 | self.navigationController?.interactivePopGestureRecognizer?.delegate = self 67 | } 68 | 69 | override func didReceiveMemoryWarning() { 70 | super.didReceiveMemoryWarning() 71 | } 72 | 73 | deinit { 74 | AHLog("---dealloc---\(type(of: self))") 75 | } 76 | 77 | // MARK: - event && methods 78 | fileprivate func setupNav() { 79 | if navigationController?.viewControllers.count > 1 { 80 | navigationController?.interactivePopGestureRecognizer?.delegate = self 81 | 82 | popClosure = { [unowned self] in 83 | guard let navigationController = self.navigationController else { return } 84 | navigationController.popViewController(animated: true) 85 | } 86 | } 87 | setNavigationBarStyle() 88 | } 89 | 90 | func back() { 91 | if popClosure != nil { 92 | popClosure!() 93 | } 94 | } 95 | 96 | func setNavigationBarStyle(BarColor: UIColor = UIColorMainBlue, backItemColor: navBarBackItem = .white) { 97 | self.navigationController?.navigationBar.barTintColor = BarColor 98 | 99 | var titleColor = UIColor.white 100 | 101 | if BarColor.isEqual(UIColor.white) { 102 | titleColor = UIColorTextBlock 103 | } 104 | 105 | self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: titleColor, 106 | NSFontAttributeName : UIFont.systemFont(ofSize: 19)] 107 | 108 | if navigationController?.viewControllers.count > 1 { 109 | switch backItemColor { 110 | case .blue: 111 | navigationController?.navigationBar.tintColor = UIColorMainBlue 112 | let oriImage = UIImage(named: "nav_back_blue")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal) 113 | navigationItem.leftBarButtonItem = UIBarButtonItem(image: oriImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(back)) 114 | default: // white 115 | let oriImage = UIImage(named: "nav_back")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal) 116 | navigationItem.leftBarButtonItem = UIBarButtonItem(image: oriImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(back)) 117 | } 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /Gank/Base/BaseWebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseWebViewController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/20. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | class BaseWebViewController: BaseViewController { 13 | 14 | var urlString: String? 15 | 16 | lazy var webView: WKWebView = { 17 | let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: kScreen_W, height: kScreen_H)) 18 | webView.isMultipleTouchEnabled = true 19 | webView.autoresizesSubviews = true 20 | webView.scrollView.alwaysBounceVertical = true 21 | return webView 22 | }() 23 | 24 | lazy var progressView: UIProgressView = { 25 | let progressView = UIProgressView(progressViewStyle: .default) 26 | progressView.progressTintColor = UIColorTextBlue 27 | progressView.trackTintColor = UIColor.white 28 | progressView.frame = CGRect(x: 0, y: kNavBarHeight, width: kScreen_W, height: 2) 29 | progressView.alpha = 0.0 30 | return progressView 31 | }() 32 | 33 | fileprivate lazy var loadingView: AHLoadingView = { 34 | let loadingView = AHLoadingView(frame: CGRect(x: 0, y: 0, width: kScreen_W, height: kScreen_H)) 35 | return loadingView 36 | }() 37 | 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | 41 | setupUI() 42 | 43 | loadWithURLString(urlString) 44 | } 45 | 46 | deinit { 47 | webView.removeObserver(self, forKeyPath: "estimatedProgress") 48 | webView.navigationDelegate = nil 49 | } 50 | 51 | fileprivate func setupUI() { 52 | view.addSubview(webView) 53 | view.addSubview(loadingView) 54 | view.addSubview(progressView) 55 | 56 | webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) 57 | webView.navigationDelegate = self 58 | popClosure = { [unowned self] in 59 | self.didBackButtonClick() 60 | } 61 | } 62 | 63 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 64 | if keyPath == "estimatedProgress" { 65 | progressView.alpha = 1.0 66 | let animated = CGFloat(webView.estimatedProgress) > CGFloat(self.progressView.progress) 67 | progressView.setProgress(Float(webView.estimatedProgress), animated: animated) 68 | 69 | if webView.estimatedProgress >= 1.0 { 70 | UIView.animate(withDuration: 0.5, animations: { 71 | self.progressView.alpha = 0.0 72 | }, completion: { (_) in 73 | self.progressView.setProgress(0.0, animated: false) 74 | }) 75 | } 76 | } else { 77 | super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 78 | } 79 | } 80 | 81 | func loadWithURLString(_ urlString: String?) { 82 | guard let urlString = urlString else { return } 83 | guard let url = URL(string: urlString) else { return } 84 | 85 | self.webView.load(URLRequest(url: url)) 86 | } 87 | 88 | func didBackButtonClick() { 89 | if self.webView.canGoBack { 90 | self.webView.goBack() 91 | setupLeftBarButtonItems(shouldShow: true) 92 | } else { 93 | webViewPop() 94 | setupLeftBarButtonItems(shouldShow: false) 95 | } 96 | } 97 | 98 | func setupLeftBarButtonItems(shouldShow: Bool) { 99 | if shouldShow { 100 | let backImage = UIImage(named: "nav_back")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal) 101 | let backBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 22, height: 22)) 102 | backBtn.setImage(backImage, for: .normal) 103 | backBtn.addTarget(self, action: #selector(didBackButtonClick), for: .touchUpInside) 104 | let backItem = UIBarButtonItem(customView: backBtn) 105 | 106 | let spaceItem = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: self, action: nil) 107 | spaceItem.width = 25 108 | 109 | let closeImage = UIImage(named: "icon_close_second")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal) 110 | let closeBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 22, height: 22)) 111 | closeBtn.setImage(closeImage, for: .normal) 112 | closeBtn.addTarget(self, action: #selector(webViewPop), for: .touchUpInside) 113 | let closeItem = UIBarButtonItem(customView: closeBtn) 114 | 115 | navigationItem.leftBarButtonItems = [backItem, spaceItem, closeItem] 116 | } 117 | } 118 | 119 | func webViewPop() { 120 | guard let navigationController = self.navigationController else { return } 121 | navigationController.popViewController(animated: true) 122 | } 123 | } 124 | 125 | extension BaseWebViewController: WKNavigationDelegate { 126 | /// 页面加载完成之后调用 127 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { 128 | self.title = "详细内容" 129 | } 130 | 131 | /// 开始获取到网页内容时返回 132 | func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { 133 | loadingView.isHidden = true 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /Gank/Base/TabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/8. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TabBarController: UITabBarController { 12 | 13 | lazy var homeVC: UINavigationController = { 14 | let homeVC = AHHomeViewController() 15 | let nav = self.setupNav(viewController: homeVC, title: "最新干货", barItemTitle: "首页", image: "tabbar_home", selectedImage: "tabbar_home_highlighted") 16 | return nav 17 | }() 18 | 19 | lazy var gankVC: UINavigationController = { 20 | let gankVC = AHGankViewController() 21 | let nav = self.setupNav(viewController: gankVC, title: "干货", barItemTitle: "干货", image: "find", selectedImage: "find_select") 22 | return nav 23 | }() 24 | 25 | lazy var mineVC: UINavigationController = { 26 | let mineVC = AHMineViewController() 27 | let nav = self.setupNav(viewController: mineVC, title: "我的", barItemTitle: "我的", image: "property", selectedImage: "property_select") 28 | return nav 29 | }() 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | setupTabBar() 35 | } 36 | 37 | override func didReceiveMemoryWarning() { 38 | super.didReceiveMemoryWarning() 39 | } 40 | 41 | deinit { 42 | AHLog("---dealloc---\(type(of: self))") 43 | } 44 | 45 | fileprivate func setupNav(viewController: BaseViewController, title: String, barItemTitle: String, image:String, selectedImage: String) -> UINavigationController { 46 | viewController.title = title 47 | viewController.tabBarItem.title = barItemTitle 48 | viewController.tabBarItem.selectedImage = UIImage(named: selectedImage) 49 | viewController.tabBarItem.image = UIImage(named: image) 50 | let nav = UINavigationController(rootViewController: viewController) 51 | return nav 52 | } 53 | 54 | fileprivate func setupTabBar() { 55 | viewControllers = [homeVC, gankVC, mineVC] 56 | tabBar.shadowImage = UIImage() 57 | tabBar.selectionIndicatorImage = UIImage() 58 | tabBar.backgroundImage = UIImage(named: "tabBar_bgwhiteColor") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Gank/Base/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/16. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct User { 12 | 13 | static var info = BmobUser.current() 14 | 15 | static func logout() { 16 | BmobUser.logout() 17 | update() 18 | } 19 | 20 | static func update() { 21 | info = BmobUser.current() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Gank/Class/Gank/AHImageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHImageCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/14. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import YYWebImage 11 | 12 | class AHImageCell: UICollectionViewCell { 13 | 14 | var index: Int! 15 | 16 | var classModel: AHClassModel! { 17 | didSet { 18 | if let count = classModel.images?.count { 19 | let urlString = classModel.images![index] 20 | // 只有一张图片 21 | if count == 1 { 22 | // 可以得到图片的高度宽度, 根据宽高比缩放图片 23 | if classModel.imageW != 0 && classModel.imageH != 0 { 24 | // 先从缓存中查找对应的高清图 25 | let image = imageFromCache(urlString: urlString) 26 | 27 | if image != nil { // 缓存中有高清图, 直接加载 28 | imageView.image = image 29 | } else { 30 | let small_url = urlString + "?imageView2/1/w/\(Int(classModel.imageContainFrame.width))/h/\(Int(classModel.imageContainFrame.height))/interlace/1" 31 | 32 | // 当前网络不是wifi, 而且仅允许wifi网络下载图片 33 | if ToolKit.getNetWorkType() != "Wifi" && onlyWifiDownPic == true { 34 | // 缓存中有缩略图,直接加载 35 | imageView.image = imageFromCache(urlString: small_url) ?? UIImage(named: "placeImage") 36 | return 37 | } 38 | 39 | imageView.yy_imageURL = URL(string: small_url) 40 | } 41 | // 获取不到图片的高度宽度 42 | } else { 43 | if ToolKit.getNetWorkType() != "Wifi" && onlyWifiDownPic == true { 44 | imageView.image = imageFromCache(urlString: urlString) ?? UIImage(named: "placeImage") 45 | return 46 | } 47 | 48 | imageView.yy_imageURL = URL(string: urlString) 49 | } 50 | // 有多张图片 51 | } else if count > 1 { 52 | // 先从缓存中查找对应的高清图 53 | let image = imageFromCache(urlString: urlString) 54 | 55 | if image != nil { // 缓存中有高清图, 直接加载 56 | imageView.image = image 57 | } else { 58 | let small_url = urlString + "?imageView2/0/w/\(Int(cellMaxWidth))" 59 | 60 | if ToolKit.getNetWorkType() != "Wifi" && onlyWifiDownPic == true { 61 | imageView.image = imageFromCache(urlString: small_url) ?? UIImage(named: "placeImage") 62 | return 63 | } 64 | 65 | imageView.yy_imageURL = URL(string: small_url) 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | @IBOutlet weak var imageView: YYAnimatedImageView! 73 | 74 | override func awakeFromNib() { 75 | super.awakeFromNib() 76 | self.backgroundColor = UIColorMainBG 77 | } 78 | 79 | fileprivate func imageFromCache(urlString: String) -> UIImage? { 80 | let url = URL(string: urlString) 81 | let cacheKey = YYWebImageManager.shared().cacheKey(for: url!) 82 | let image = YYWebImageManager.shared().cache?.getImageForKey(cacheKey) 83 | 84 | return image 85 | } 86 | } 87 | 88 | extension AHImageCell: ViewNameReusable {} 89 | -------------------------------------------------------------------------------- /Gank/Class/Gank/AHImageCell.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 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Gank/Class/Gank/AHWaitingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHWaitingView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/17. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum AHWaitingViewType { 12 | case AHDWaitingViewTypeLoop 13 | case AHDWaitingViewTypePie 14 | } 15 | 16 | class AHWaitingView: UIView { 17 | 18 | // 模式 19 | var tpye: AHWaitingViewType! 20 | 21 | // 进度 22 | var progresses: CGFloat = 0.0 { 23 | didSet { 24 | setNeedsDisplay() 25 | if progresses >= 1 { 26 | removeFromSuperview() 27 | } 28 | } 29 | } 30 | 31 | override init(frame: CGRect) { 32 | super.init(frame: frame) 33 | backgroundColor = UIColor.clear 34 | tpye = AHWaitingViewType.AHDWaitingViewTypePie 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | override func draw(_ rect: CGRect) { 42 | guard let ctx = UIGraphicsGetCurrentContext() else { 43 | return 44 | } 45 | 46 | let centenX = rect.size.width * 0.5 47 | let centenY = rect.size.height * 0.5 48 | RGBColor(r: 255, g: 255, b: 255, alpha: 0.8).set() 49 | 50 | if self.tpye == AHWaitingViewType.AHDWaitingViewTypePie { 51 | 52 | let bigRadius = min(centenX, centenY) 53 | let bigW = bigRadius * 2 54 | let bigH = bigW 55 | let bigX = (rect.size.width - bigW) * 0.5 56 | let bigY = (rect.size.height - bigH) * 0.5 57 | ctx.addEllipse(in: CGRect(x: bigX, y: bigY, width: bigW, height: bigH)) 58 | ctx.fillPath() 59 | 60 | RGBColor(r: 0, g: 0, b: 0, alpha: 0.8).set() 61 | let radius = bigRadius - 1 62 | let w = radius * 2 63 | let h = w 64 | let x = (rect.size.width - w) * 0.5 65 | let y = (rect.size.height - h) * 0.5 66 | ctx.addEllipse(in: CGRect(x: x, y: y, width: w, height: h)) 67 | ctx.fillPath() 68 | 69 | RGBColor(r: 255, g: 255, b: 255, alpha: 0.8).set() 70 | ctx.move(to: CGPoint(x: centenX, y: centenY)) 71 | ctx.addLine(to: CGPoint(x: centenX, y: 0)) 72 | 73 | let to = -(.pi) * 0.5 + progresses * .pi * 2.0 + 0.05 // 初始值 74 | ctx.addArc(center: CGPoint(x: centenX, y: centenY), radius: radius - 2, startAngle: -(.pi) * 0.5, endAngle: to, clockwise: false) 75 | ctx.closePath() 76 | ctx.fillPath() 77 | } else { 78 | RGBColor(r: 255, g: 255, b: 255, alpha: 0.7).set() 79 | ctx.setLineWidth(10) 80 | ctx.setLineCap(CGLineCap.round) 81 | let to = -(.pi) * 0.5 + progresses * .pi * 2.0 + 0.05; // 初始值0.05 82 | let radius = min(rect.size.width, rect.size.height) * 0.5 - 10.0; 83 | ctx.addArc(center: CGPoint(x: centenX, y: centenY), radius: radius, startAngle: -(.pi) * 0.5, endAngle: to, clockwise: false) 84 | ctx.strokePath() 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Gank/Class/Gank/AHWebToolView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHWebToolView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/20. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHWebToolView: UIView { 12 | 13 | @IBOutlet weak var backBtn: UIButton! 14 | @IBOutlet weak var forwardBtn: UIButton! 15 | @IBOutlet weak var reloadBtn: UIButton! 16 | @IBOutlet weak var likeBtn: UIButton! 17 | 18 | override func awakeFromNib() { 19 | super.awakeFromNib() 20 | } 21 | 22 | class func webToolView() -> AHWebToolView { 23 | return AHWebToolView.viewFromNib() as! AHWebToolView 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHHeaderSectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHeaderSectionView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/22. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHHeaderSectionView: UIView { 12 | @IBOutlet weak var titleLabel: UILabel! 13 | 14 | var groupModel: AHHomeGroupModel! { 15 | didSet { 16 | titleLabel.text = groupModel.groupTitle 17 | } 18 | } 19 | 20 | override func awakeFromNib() { 21 | super.awakeFromNib() 22 | titleLabel.textColor = UIColorTextLightGray 23 | backgroundColor = RGBColor(r: 248, g: 248, b: 248, alpha: 1.0) 24 | } 25 | 26 | class func headerSectionView() -> AHHeaderSectionView { 27 | return AHHeaderSectionView.viewFromNib() as! AHHeaderSectionView 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHHeaderSectionView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHHomeCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHomeCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/23. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHHomeCell: UITableViewCell { 12 | 13 | @IBOutlet weak var editorBtn: UIButton! 14 | 15 | @IBOutlet weak var timeBtn: UIButton! 16 | 17 | @IBOutlet weak var contentLabel: UILabel! 18 | 19 | @IBOutlet weak var separatorLine: UIView! 20 | 21 | var moreButtonClickedClouse: ((_ indexPath: IndexPath) -> Void)? 22 | 23 | var indexPath: IndexPath! { 24 | didSet { 25 | self.separatorLine.isHidden = (indexPath.row == 0) 26 | } 27 | } 28 | 29 | var gankModel: AHHomeGankModel! { 30 | didSet { 31 | contentLabel.text = gankModel.desc! 32 | contentLabel.numberOfLines = 0 33 | 34 | editorBtn.setTitle(gankModel.user, for: .normal) 35 | editorBtn.isHidden = (gankModel.user == nil) ? true : false 36 | 37 | timeBtn.setTitle(gankModel.publishedAt, for: .normal) 38 | 39 | moreBrn.frame = gankModel.moreBtnFrame 40 | moreBrn.isHidden = !gankModel.isShouldShowMoreButton 41 | 42 | // cell是否展开 43 | if gankModel.isOpen { 44 | self.contentLabel.numberOfLines = 0 45 | self.moreBrn.setTitle("收起", for: .normal) 46 | } else { 47 | self.contentLabel.numberOfLines = 3 48 | self.moreBrn.setTitle("全文", for: .normal) 49 | } 50 | } 51 | } 52 | 53 | lazy var moreBrn: UIButton = { 54 | let moreBrn = UIButton() 55 | moreBrn.setTitle("全文", for: .normal) 56 | moreBrn.setTitle("收起", for: .selected) 57 | moreBrn.titleLabel?.font = UIFont.systemFont(ofSize: 15) 58 | moreBrn.titleLabel?.textAlignment = .left 59 | moreBrn.setTitleColor(UIColorMainBlue, for: .normal) 60 | self.contentView.addSubview(moreBrn) 61 | moreBrn.addTarget(self, action: #selector(moreBtnClicked), for: .touchUpInside) 62 | return moreBrn 63 | }() 64 | 65 | func moreBtnClicked () { 66 | self.gankModel._cellH = nil 67 | 68 | if moreButtonClickedClouse != nil { 69 | moreButtonClickedClouse!(self.indexPath) 70 | } 71 | } 72 | 73 | override func awakeFromNib() { 74 | super.awakeFromNib() 75 | 76 | editorBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0) 77 | timeBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0) 78 | } 79 | 80 | override func setSelected(_ selected: Bool, animated: Bool) { 81 | super.setSelected(selected, animated: animated) 82 | } 83 | 84 | } 85 | 86 | extension AHHomeCell: ViewNameReusable {} 87 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHHomeHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHomeHeaderView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/22. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHHomeHeaderView: UIView { 12 | 13 | @IBOutlet weak var timeLabel: UILabel! 14 | 15 | @IBOutlet weak var imageView: UIImageView! 16 | 17 | class func headerView() -> AHHomeHeaderView { 18 | return AHHomeHeaderView.viewFromNib() as! AHHomeHeaderView 19 | } 20 | 21 | override func awakeFromNib() { 22 | super.awakeFromNib() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHHomeHeaderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHSearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHSearchView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/9. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHSearchView: UIView { 12 | 13 | @IBOutlet weak var seatchTitle: UILabel! 14 | weak var locationVC: AHHomeViewController? 15 | 16 | /// 转场代理 17 | fileprivate lazy var transitionManager: AHTurnVCTransitionManager = { 18 | let manager = AHTurnVCTransitionManager() 19 | manager.presentFrame = kScreen_BOUNDS 20 | return manager 21 | }() 22 | 23 | class func searchView() -> AHSearchView { 24 | return self.viewFromNib() as! AHSearchView 25 | } 26 | 27 | override func awakeFromNib() { 28 | super.awakeFromNib() 29 | let tap = UITapGestureRecognizer(target: self, action: #selector(showSearchVC)) 30 | self.addGestureRecognizer(tap) 31 | } 32 | 33 | func showSearchVC() { 34 | let searchVC = AHSearchViewController() 35 | let nav = UINavigationController(rootViewController: searchVC) 36 | 37 | // 自定义转场动画 38 | nav.transitioningDelegate = transitionManager 39 | nav.modalPresentationStyle = UIModalPresentationStyle.custom 40 | locationVC?.present(nav, animated: true, completion: nil) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Gank/Class/Home/View/AHSearchView.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 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHFeedbackViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHFeedbackViewController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/15. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import IQKeyboardManagerSwift 11 | 12 | class AHFeedbackViewController: BaseViewController { 13 | 14 | @IBOutlet weak var textView: UITextView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | setupUI() 20 | } 21 | 22 | override func viewDidAppear(_ animated: Bool) { 23 | super.viewDidAppear(animated) 24 | textView.becomeFirstResponder() 25 | } 26 | 27 | override func didReceiveMemoryWarning() { 28 | super.didReceiveMemoryWarning() 29 | } 30 | 31 | deinit { 32 | IQKeyboardManager.sharedManager().enable = true 33 | } 34 | 35 | fileprivate func setupUI() { 36 | title = "反馈" 37 | setNavigationBarStyle(BarColor: UIColor.white, backItemColor: .blue) 38 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "提交", style: .plain, target: self, action: #selector(submitAction)) 39 | 40 | IQKeyboardManager.sharedManager().enable = false 41 | } 42 | 43 | func submitAction() { 44 | if textView.text!.characters.count == 0 { 45 | ToolKit.showInfo(withStatus: "内容不能为空") 46 | return 47 | } 48 | 49 | self.view.endEditing(true) 50 | ToolKit.show(withStatus: " 正在提交 ") 51 | 52 | let feedbackInfo = BmobObject(className: "UserFeedback") 53 | feedbackInfo?.setObject(User.info?.objectId, forKey: "userId") 54 | feedbackInfo?.setObject(textView.text!, forKey: "content") 55 | 56 | feedbackInfo?.saveInBackground(resultBlock: { (isSuccessful, error) in 57 | if error != nil { // 提交失败 58 | AHLog(error!) 59 | ToolKit.showError(withStatus: " 提交失败 ") 60 | } else { // 提交成功 61 | ToolKit.showSuccess(withStatus: " 提交成功 ") 62 | } 63 | }) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHNoLoginCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHNoLoginCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/8. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHNoLoginCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | } 16 | 17 | override func setSelected(_ selected: Bool, animated: Bool) { 18 | super.setSelected(selected, animated: animated) 19 | } 20 | } 21 | 22 | extension AHNoLoginCell: ViewNameReusable {} 23 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHNoLoginCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHPersonalPageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHPersonalPageViewController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/16. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHPersonalPageViewController: BaseViewController { 12 | 13 | fileprivate lazy var tableView: UITableView = { 14 | let tabelView = UITableView(frame: CGRect(x: 0, y: 0, width: kScreen_W, height: kScreen_H - kNavBarHeight), style: .grouped) 15 | tabelView.backgroundColor = UIColorMainBG 16 | tabelView.delegate = self 17 | tabelView.dataSource = self 18 | tabelView.contentInset.bottom = kBottomBarHeight 19 | return tabelView 20 | }() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | setupUI() 26 | } 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | UIApplication.shared.statusBarStyle = .default 32 | 33 | self.tableView.reloadData() 34 | } 35 | 36 | override func didReceiveMemoryWarning() { 37 | super.didReceiveMemoryWarning() 38 | } 39 | 40 | 41 | fileprivate func setupUI() { 42 | title = "编辑个人主页" 43 | 44 | setNavigationBarStyle(BarColor: UIColor.white, backItemColor: .blue) 45 | 46 | view.addSubview(tableView) 47 | } 48 | 49 | } 50 | 51 | extension AHPersonalPageViewController: UITableViewDelegate, UITableViewDataSource { 52 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 53 | return 1 54 | } 55 | 56 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 57 | let cell = cellForValue1() 58 | cell.textLabel?.text = "昵称" 59 | cell.detailTextLabel?.text = User.info?.object(forKey: "nickName") as? String 60 | return cell 61 | } 62 | 63 | fileprivate func cellForValue1() -> UITableViewCell { 64 | var cell = tableView.dequeueReusableCell(withIdentifier: "personalCell") 65 | if cell == nil { 66 | cell = UITableViewCell(style: .value1, reuseIdentifier: "personalCell") 67 | cell!.accessoryType = .disclosureIndicator 68 | cell!.textLabel?.textColor = UIColorTextGray 69 | } 70 | return cell! 71 | } 72 | 73 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 74 | return 15 75 | } 76 | 77 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 78 | tableView.deselectRow(at: indexPath, animated: true) 79 | 80 | let vc = AHUpdateNickViewController() 81 | navigationController?.pushViewController(vc, animated: true) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHUpdateNickViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHUpdateNickViewController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/16. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import IQKeyboardManagerSwift 11 | 12 | class AHUpdateNickViewController: BaseViewController { 13 | 14 | @IBOutlet weak var textField: UITextField! 15 | 16 | fileprivate var lastNickName: String? 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | setupUI() 21 | ToolKit.checkUserLoginedWithOtherDevice { } 22 | } 23 | 24 | override func viewWillAppear(_ animated: Bool) { 25 | super.viewWillAppear(animated) 26 | UIApplication.shared.statusBarStyle = .default 27 | } 28 | 29 | override func didReceiveMemoryWarning() { 30 | super.didReceiveMemoryWarning() 31 | } 32 | 33 | deinit { 34 | IQKeyboardManager.sharedManager().enable = true 35 | } 36 | 37 | fileprivate func setupUI() { 38 | title = "昵称" 39 | setNavigationBarStyle(BarColor: UIColor.white, backItemColor: .blue) 40 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "保存", style: .plain, target: self, action: #selector(saveAction)) 41 | 42 | textField.text = User.info?.object(forKey: "nickName") as? String 43 | lastNickName = User.info?.object(forKey: "nickName") as? String 44 | 45 | IQKeyboardManager.sharedManager().enable = false 46 | } 47 | 48 | func saveAction() { 49 | if User.info == nil { 50 | let loginVC = AHLoginViewController() 51 | let nav = UINavigationController(rootViewController: loginVC) 52 | kWindow?.rootViewController?.present(nav, animated: true, completion: nil) 53 | return 54 | } 55 | 56 | if self.textField.text?.characters.count == 0 { 57 | ToolKit.showInfo(withStatus: "昵称不能为空") 58 | return 59 | } 60 | 61 | ToolKit.show(withStatus: " 正在保存 ") 62 | self.view.endEditing(true) 63 | 64 | User.info?.setObject(self.textField.text, forKey: "nickName") 65 | User.info?.updateInBackground { (isSuccessful, error) in 66 | if isSuccessful { 67 | ToolKit.showSuccess(withStatus: " 保存成功 ") 68 | } else { 69 | ToolKit.showError(withStatus: " 保存失败 ") 70 | User.info?.setObject(self.lastNickName, forKey: "nickName") 71 | AHLog(error!) 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHUpdateNickViewController.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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHUserCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHUserCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/9. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHUserCell: UITableViewCell { 12 | 13 | @IBOutlet weak var iconImageView: UIImageView! 14 | @IBOutlet weak var nickName: UILabel! 15 | 16 | var userInfo: BmobUser? { 17 | didSet { 18 | guard let name = userInfo?.object(forKey: "nickName") as? String else { return } 19 | nickName.text = name 20 | } 21 | } 22 | 23 | override func awakeFromNib() { 24 | super.awakeFromNib() 25 | } 26 | 27 | override func setSelected(_ selected: Bool, animated: Bool) { 28 | super.setSelected(selected, animated: animated) 29 | } 30 | } 31 | 32 | extension AHUserCell: ViewNameReusable {} 33 | -------------------------------------------------------------------------------- /Gank/Class/Mine/AHUserCell.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 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Gank/Class/Mine/Setting/AHAccountSafeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHAccountSafeViewController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/17. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHAccountSafeViewController: BaseViewController { 12 | 13 | // MARK: - control 14 | @IBOutlet weak var currentPasswordTF: UITextField! 15 | @IBOutlet weak var newPasswordTF: UITextField! 16 | @IBOutlet weak var confirmPasswordTF: UITextField! 17 | 18 | // MARK: - life cycle 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | setupUI() 23 | } 24 | 25 | override func didReceiveMemoryWarning() { 26 | super.didReceiveMemoryWarning() 27 | } 28 | 29 | // MARK: - event && methods 30 | fileprivate func setupUI() { 31 | title = "修改密码" 32 | view.backgroundColor = UIColor.white 33 | setNavigationBarStyle(BarColor: UIColor.white, backItemColor: .blue) 34 | } 35 | 36 | @IBAction func showSecureTextEvent(_ btn: UIButton) { 37 | if btn.tag == 1 { 38 | currentPasswordTF.isSecureTextEntry = !currentPasswordTF.isSecureTextEntry 39 | 40 | if currentPasswordTF.isSecureTextEntry { 41 | btn.setImage(UIImage(named: "icon_show"), for: .normal) 42 | } else { 43 | btn.setImage(UIImage(named: "icon_show_blue"), for: .normal) 44 | } 45 | } else if btn.tag == 2 { 46 | newPasswordTF.isSecureTextEntry = !newPasswordTF.isSecureTextEntry 47 | 48 | if newPasswordTF.isSecureTextEntry { 49 | btn.setImage(UIImage(named: "icon_show"), for: .normal) 50 | } else { 51 | btn.setImage(UIImage(named: "icon_show_blue"), for: .normal) 52 | } 53 | } else { 54 | confirmPasswordTF.isSecureTextEntry = !confirmPasswordTF.isSecureTextEntry 55 | 56 | if confirmPasswordTF.isSecureTextEntry { 57 | btn.setImage(UIImage(named: "icon_show"), for: .normal) 58 | } else { 59 | btn.setImage(UIImage(named: "icon_show_blue"), for: .normal) 60 | } 61 | } 62 | } 63 | 64 | @IBAction func updatePasswordEvent() { 65 | view.endEditing(true) 66 | 67 | if currentPasswordTF.text?.length == 0 { 68 | ToolKit.showInfo(withStatus: "请填写当前密码") 69 | } else { 70 | if newPasswordTF.text?.length == 0 { 71 | ToolKit.showInfo(withStatus: "请填写新密码") 72 | } else { 73 | if Validate.checkPassword(newPasswordTF.text!) { 74 | if confirmPasswordTF.text! != newPasswordTF.text! { 75 | ToolKit.showInfo(withStatus: "两次密码输入不一致") 76 | } else { 77 | sendUpdatePasswordRequest() 78 | } 79 | } else { 80 | ToolKit.showInfo(withStatus: "密码为6-18位字母和数字组合") 81 | } 82 | } 83 | } 84 | } 85 | 86 | fileprivate func sendUpdatePasswordRequest() { 87 | ToolKit.show(withStatus: "正在修改中", style: .dark, maskType: .clear) 88 | 89 | let user = User.info 90 | let oldPassword = currentPasswordTF.text! 91 | let newPassword = newPasswordTF.text! 92 | 93 | user?.updateCurrentUserPassword(withOldPassword: oldPassword, newPassword: newPassword, block: { (isSuccessful, error) in 94 | if isSuccessful { 95 | ToolKit.showSuccess(withStatus: "密码修改成功\n 请重新登录") 96 | User.logout() 97 | self.navigationController!.popToRootViewController(animated: true) 98 | } else { 99 | AHLog(error) 100 | let nserror = error! as NSError 101 | switch nserror.code { 102 | case 210: 103 | ToolKit.showError(withStatus: "当前密码不正确") 104 | default: 105 | ToolKit.showError(withStatus: "密码修改失败, 请稍后重试") 106 | } 107 | } 108 | }) 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /Gank/Class/Mine/View/AHCollectCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHCollectCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/13. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHCollectCell: UITableViewCell { 12 | 13 | @IBOutlet weak var contentLabel: UILabel! 14 | @IBOutlet weak var userBtn: UIButton! 15 | @IBOutlet weak var timeBtn: UIButton! 16 | @IBOutlet weak var classBtn: UIButton! 17 | 18 | var gankModel: GankModel! { 19 | didSet { 20 | contentLabel.text = gankModel.desc 21 | userBtn.setTitle(gankModel.user, for: .normal) 22 | timeBtn.setTitle(gankModel.publishedAt, for: .normal) 23 | classBtn.setTitle(gankModel.type, for: .normal) 24 | } 25 | } 26 | 27 | override func awakeFromNib() { 28 | super.awakeFromNib() 29 | timeBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 0) 30 | classBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 0) 31 | userBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 0) 32 | } 33 | 34 | override func setSelected(_ selected: Bool, animated: Bool) { 35 | super.setSelected(selected, animated: animated) 36 | } 37 | } 38 | 39 | extension AHCollectCell: ViewNameReusable {} 40 | -------------------------------------------------------------------------------- /Gank/Class/Search/AHSearchCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHSearchCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/15. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHSearchCell: UITableViewCell { 12 | 13 | @IBOutlet weak var descLabel: UILabel! 14 | @IBOutlet weak var timeBtn: UIButton! 15 | @IBOutlet weak var classBtn: UIButton! 16 | 17 | var searchModel: AHSearchGankModel! { 18 | didSet { 19 | descLabel.text = searchModel.desc! 20 | timeBtn.setTitle(searchModel.publishedAt!, for: .normal) 21 | classBtn.setTitle(searchModel.type!, for: .normal) 22 | } 23 | } 24 | 25 | override func awakeFromNib() { 26 | super.awakeFromNib() 27 | 28 | self.layoutMargins = .zero 29 | timeBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 0) 30 | classBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 0) 31 | } 32 | 33 | override func setSelected(_ selected: Bool, animated: Bool) { 34 | super.setSelected(selected, animated: animated) 35 | } 36 | } 37 | 38 | extension AHSearchCell: ViewNameReusable {} 39 | -------------------------------------------------------------------------------- /Gank/Class/Search/AHSeatchTagBtn.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHSeatchTagBtn.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/12. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHSeatchTagBtn: UIButton { 12 | let margin: CGFloat = 8 13 | 14 | override func layoutSubviews() { 15 | super.layoutSubviews() 16 | let btnW = self.frame.size.width 17 | let btnH = self.frame.size.height 18 | titleLabel?.frame = CGRect(x: margin, y: margin, width: btnW - 2 * margin, height: btnH - margin * 2) 19 | 20 | imageView?.frame = CGRect(x: -3, y: -3, width: 10, height: 10) 21 | } 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | setup() 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | fileprivate func setup() { 33 | layer.cornerRadius = 3 34 | backgroundColor = RGBColor(r: 246, g: 246, b: 246, alpha: 1) 35 | adjustsImageWhenHighlighted = false 36 | setTitleColor(UIColorTextBlock, for: .normal) 37 | titleLabel?.textAlignment = .center 38 | titleLabel?.font = UIFont.systemFont(ofSize: 12) 39 | imageView?.isHighlighted = true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Gank/Class/TurnChannel/AHListViewPotocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHListViewPotocol.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/27. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol AHListViewPotocol { 12 | /// 存放ListView上所有的btn的标题 13 | var tagTitleArray: [String] { get set } 14 | 15 | /// 存放所有的btn 16 | var tagArray: [AHTagBtn] { get set } 17 | 18 | /// 一共有多少列 19 | var listCols: Int { get } 20 | 21 | /// 间距 22 | var margin: CGFloat { get } 23 | 24 | /// 整体的高度 25 | var listViewH: CGFloat { get } 26 | } 27 | 28 | extension AHListViewPotocol where Self: UIView { 29 | var listCols: Int { 30 | return 4 31 | } 32 | 33 | var margin: CGFloat { 34 | return 10.0 35 | } 36 | 37 | var listViewH: CGFloat { 38 | return (tagArray.count <= 0 ? 30.0 : ((tagArray.last?.MaxY)! + margin)) 39 | } 40 | 41 | /// 返回手指移动到的按钮 42 | func getBtnCenterInButtons(curBtn: AHTagBtn) -> AHTagBtn? { 43 | for btn in tagArray { 44 | if curBtn == btn { 45 | continue 46 | } 47 | if btn.frame.contains(curBtn.center) { 48 | return btn 49 | } 50 | } 51 | return nil 52 | } 53 | 54 | /// 跟新按钮的tag 55 | func updateTag() { 56 | for (i, btn) in tagArray.enumerated() { 57 | btn.tag = i 58 | } 59 | } 60 | 61 | /// 更新以后按钮frame 62 | func updateLaterTagButtonFrame(laterIndex: Int) { 63 | for i in laterIndex.. Void)? 15 | 16 | /// 存放所有的btn 17 | var tagArray: [AHTagBtn] = [AHTagBtn]() 18 | 19 | /// 存放MoreListView上所有的btn的标题 20 | var tagTitleArray: [String] = [String]() 21 | 22 | // MARK: - control 23 | fileprivate lazy var infoView: UIView = { 24 | let infoView = UIView() 25 | infoView.frame = CGRect(x: 0, y: 0, width: kScreen_W, height: 30) 26 | let label = UILabel() 27 | label.frame = CGRect(x: 10, y: 0, width: kScreen_W - 10, height: 30) 28 | label.text = "点击添加更多频道" 29 | label.textAlignment = .left 30 | label.textColor = UIColorTextBlock 31 | label.font = UIFont.systemFont(ofSize: 14) 32 | infoView.addSubview(label) 33 | // infoView.backgroundColor = RGBColor(220.0, g: 220.0, b: 220.0, alpha: 1) 34 | return infoView 35 | }() 36 | 37 | // MARK: - method 38 | override init(frame: CGRect) { 39 | super.init(frame: frame) 40 | setupUI() 41 | } 42 | 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | fileprivate func setupUI() { 48 | addSubview(infoView) 49 | } 50 | } 51 | 52 | // MARK: - prot methods 53 | extension AHMoreListView { 54 | /// 添加标签 55 | func addTag(tagTitle: String) { 56 | 57 | let tagBtn = AHTagBtn() 58 | tagBtn.tag = tagArray.count 59 | tagBtn.setTitle(tagTitle, for: .normal) 60 | tagBtn.addTarget(self, action: #selector(addMoreTag(btn:)), for: .touchUpInside) 61 | 62 | addSubview(tagBtn) 63 | tagArray.append(tagBtn) 64 | tagTitleArray.append(tagBtn.titleLabel!.text!) 65 | 66 | updateTagBtnFrame(btn: tagBtn) 67 | 68 | // 更新自己的frame 69 | UIView.animate(withDuration: 0.25, animations: { 70 | self.Height = self.listViewH 71 | }) 72 | 73 | } 74 | 75 | /// 添加多个标签 76 | func addTags(titles: [String]) { 77 | for title in titles { 78 | addTag(tagTitle: title) 79 | } 80 | } 81 | 82 | /// 删除标签 83 | func deleteTags(btn: AHTagBtn) { 84 | btn.removeFromSuperview() 85 | 86 | tagArray.remove(at: btn.tag) 87 | 88 | tagTitleArray.remove(at: btn.tag) 89 | 90 | updateTag() 91 | 92 | // 跟新后面按钮的frame 93 | UIView.animate(withDuration: 0.25, animations: { 94 | self.updateLaterTagButtonFrame(laterIndex: btn.tag) 95 | }) 96 | 97 | // 更新自己的frame 98 | UIView.animate(withDuration: 0.25, animations: { 99 | self.Height = self.listViewH 100 | }) 101 | } 102 | } 103 | 104 | // MARK: - event response 105 | extension AHMoreListView { 106 | func addMoreTag(btn: AHTagBtn) { 107 | deleteTags(btn: btn) 108 | guard let title = btn.titleLabel?.text else { 109 | return 110 | } 111 | 112 | if listViewAddTagClouse != nil { 113 | listViewAddTagClouse!(title) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Gank/Class/TurnChannel/AHTagBtn.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHTagBtn.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/10. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHTagBtn: UIButton { 12 | 13 | fileprivate let margin: CGFloat = 10 14 | 15 | override func layoutSubviews() { 16 | super.layoutSubviews() 17 | 18 | let btnW = self.frame.size.width 19 | let btnH = self.frame.size.height 20 | titleLabel?.frame = CGRect(x: margin, y: margin, width: btnW - 2 * margin, height: btnH - margin * 2) 21 | 22 | imageView?.frame = CGRect(x: 2, y: 2, width: 10, height: 10) 23 | } 24 | 25 | override init(frame: CGRect) { 26 | super.init(frame: frame) 27 | setup() 28 | } 29 | 30 | required init?(coder aDecoder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | fileprivate func setup() { 35 | layer.cornerRadius = 5 36 | layer.borderWidth = 1 37 | layer.borderColor = RGBColor(r: 230.0, g: 230.0, b: 230.0, alpha: 1).cgColor 38 | backgroundColor = RGBColor(r: 250.0, g: 250.0, b: 250.0, alpha: 1) 39 | adjustsImageWhenHighlighted = false 40 | setTitleColor(UIColorTextGray, for: .normal) 41 | setTitleColor(UIColorTextBlue, for: .selected) 42 | titleLabel?.textAlignment = .center 43 | titleLabel?.font = UIFont.systemFont(ofSize: 13) 44 | titleLabel?.adjustsFontSizeToFitWidth = true 45 | imageView?.isHighlighted = true 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Gank/Class/TurnChannel/AHTurnChannelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHTurnChannelViewController.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/10. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHTurnChannelViewController: BaseViewController { 12 | 13 | // MARK: - property 14 | /// 回调 15 | var turnChannelClouse: ((_ showTags: [String], _ moreTags: [String]) -> Void)? 16 | 17 | /// 已显示的tags 18 | var showTagsArray: [String] = [String]() 19 | 20 | /// 未显示的tags 21 | var moreTagsArray: [String] = [String]() 22 | 23 | fileprivate let listViewY: CGFloat = 70 24 | 25 | // MARK: - control 26 | lazy var closeBtn: UIButton = { 27 | let closeBtn = UIButton() 28 | let btnW: CGFloat = 30 29 | let btnF = CGRect(x: kScreen_W - btnW - 5, y: 30, width: btnW, height: btnW) 30 | closeBtn.setImage(UIImage(named: "icon_close_block"), for: .normal) 31 | closeBtn.frame = btnF 32 | closeBtn.addTarget(self, action: #selector(close), for: .touchUpInside) 33 | return closeBtn 34 | }() 35 | 36 | lazy var titleView: UIView = { 37 | let titleView = UIView(frame: CGRect(x: 0, y: 0, width: kScreen_W, height: 35)); 38 | let titleLable = UILabel(frame: titleView.bounds) 39 | titleLable.text = "频道管理" 40 | titleLable.textAlignment = .center 41 | titleLable.font = UIFont.systemFont(ofSize: 15) 42 | titleLable.textColor = UIColorTextBlock 43 | titleView.addSubview(titleLable) 44 | 45 | let lineView = UIView(frame: CGRect(x: 0, y: 34, width: kScreen_W, height: 1)) 46 | lineView.backgroundColor = RGBColor(r: 222, g: 222, b: 222, alpha: 1) 47 | titleView.addSubview(lineView) 48 | return titleView 49 | }() 50 | 51 | lazy var contentView: UIScrollView = { 52 | let contentView = UIScrollView(frame: self.view.bounds) 53 | return contentView 54 | }() 55 | 56 | lazy var listView: AHListView = { 57 | let listView = AHListView(frame: CGRect(x: 0, y: self.listViewY, width: kScreen_W, height: 0)) 58 | listView.addTags(titles: self.showTagsArray) 59 | listView.listViewMoveTagClouse = { [unowned self] title in 60 | self.moreListView.addTag(tagTitle: title) 61 | } 62 | return listView 63 | }() 64 | 65 | lazy var moreListView: AHMoreListView = { 66 | let moreListView = AHMoreListView(frame: CGRect(x: 0, y: self.listView.MaxY, width: kScreen_W, height: 0)) 67 | moreListView.addTags(titles: self.moreTagsArray) 68 | moreListView.listViewAddTagClouse = { [unowned self] title in 69 | self.listView.addTag(tagTitle: title) 70 | } 71 | return moreListView 72 | }() 73 | 74 | // MARK: - life cycle 75 | override func viewDidLoad() { 76 | super.viewDidLoad() 77 | 78 | self.listView.addObserver(self, forKeyPath: "frame", options: .new, context: nil) 79 | self.moreListView.addObserver(self, forKeyPath: "frame", options: .new, context: nil) 80 | 81 | self.title = "频道" 82 | view.backgroundColor = UIColorMainBG 83 | contentView.contentSize = CGSize(width: kScreen_W, height: self.moreListView.Height + self.listView.Height + listViewY) 84 | view.addSubview(contentView) 85 | // contentView.addSubview(titleView) 86 | contentView.addSubview(closeBtn) 87 | // 先添加moreListView, 再添加listView 88 | // 确保listView上的btn移动时, 不会被moreListView遮挡 89 | contentView.addSubview(moreListView) 90 | contentView.addSubview(listView) 91 | 92 | } 93 | 94 | override func viewWillAppear(_ animated: Bool) { 95 | super.viewWillAppear(animated) 96 | UIApplication.shared.statusBarStyle = .default 97 | } 98 | 99 | override func viewWillDisappear(_ animated: Bool) { 100 | super.viewWillDisappear(animated) 101 | UIApplication.shared.statusBarStyle = .lightContent 102 | } 103 | 104 | deinit { 105 | self.listView.removeObserver(self, forKeyPath: "frame") 106 | self.moreListView.removeObserver(self, forKeyPath: "frame") 107 | } 108 | 109 | // MARK: - event && methods 110 | func close() { 111 | if turnChannelClouse != nil { 112 | turnChannelClouse!(listView.tagTitleArray, moreListView.tagTitleArray) 113 | } 114 | 115 | self.dismiss(animated: true, completion: {}) 116 | } 117 | 118 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 119 | if keyPath == "frame" { 120 | let object = object as AnyObject 121 | if object.isKind(of: AHListView.self) { 122 | guard case let frame as CGRect = change?[NSKeyValueChangeKey.newKey] else { 123 | return 124 | } 125 | // 根据AHListView的frame变化更新moreListView的Y 126 | self.moreListView.Y = frame.maxY 127 | } else if object.isKind(of: AHMoreListView.self) { 128 | // 根据moreListView的frame变化更新contentSize.height 129 | contentView.contentSize.height = self.moreListView.Height + self.listView.Height + listViewY 130 | } 131 | } else { 132 | super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Gank/Extension/Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/26. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - Bundle - Extension 12 | extension Bundle { 13 | static var releaseVersion: String? { 14 | return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String 15 | } 16 | 17 | static var buildVersion: String? { 18 | return Bundle.main.infoDictionary?["CFBundleVersion"] as? String 19 | } 20 | 21 | static var appName: String { 22 | return Bundle.main.infoDictionary?["CFBundleDisplayName"] as! String 23 | } 24 | } 25 | 26 | // MARK: - UIDevice - Extension 27 | extension UIDevice { 28 | static func getUUID() -> String { 29 | // let UUID = Foundation.UUID().uuidString 30 | return UIDevice.current.identifierForVendor!.uuidString 31 | } 32 | } 33 | 34 | // MARK: - DispatchTime - Extension 35 | extension DispatchTime: ExpressibleByIntegerLiteral { 36 | public init(integerLiteral value: Int) { 37 | self = DispatchTime.now() + .seconds(value) 38 | } 39 | } 40 | 41 | extension DispatchTime: ExpressibleByFloatLiteral { 42 | public init(floatLiteral value: Double) { 43 | self = DispatchTime.now() + .milliseconds(Int(value * 1000)) 44 | } 45 | } 46 | 47 | // MARK: - NSObject - Extension 48 | extension NSObject { 49 | static var className: String { 50 | let name = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String 51 | let reaName = name + "." 52 | return NSStringFromClass(self).substring(from: reaName.endIndex) 53 | } 54 | } 55 | 56 | // MARK: - CGFloat - Extension 57 | extension CGFloat { 58 | func format(f: Int) -> String { 59 | return NSString(format:"%.\(f)f" as NSString, self) as String 60 | } 61 | } 62 | 63 | // MARK: - Date - Extension 64 | extension Date { 65 | func toString(WithFormat format: String) -> String { 66 | let dateFormatter = DateFormatter() 67 | dateFormatter.dateFormat = format 68 | return dateFormatter.string(from: self) 69 | } 70 | } 71 | 72 | // MARK: - CALayer - Extension 73 | extension CALayer { 74 | var XibBorderColor: UIColor? { 75 | get { 76 | return UIColor(cgColor: self.borderColor!) 77 | } 78 | set(boderColor) { 79 | self.borderColor = boderColor?.cgColor 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Gank/Extension/GlobalFunction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GlobalFunction.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | func RGBColor(r: CGFloat, g: CGFloat, b: CGFloat, alpha: CGFloat) -> UIColor{ 12 | return UIColor(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: alpha) 13 | } 14 | 15 | func RGBColor(_ hex: String) -> UIColor { 16 | var cString:String = hex.trimmingCharacters(in:.whitespaces).uppercased() 17 | 18 | if (cString.hasPrefix("#")) { 19 | cString = (cString as NSString).substring(from: 1) 20 | } 21 | 22 | let rString = (cString as NSString).substring(to: 2) 23 | let gString = ((cString as NSString).substring(from: 2) as NSString).substring(to: 2) 24 | let bString = ((cString as NSString).substring(from: 4) as NSString).substring(to: 2) 25 | 26 | var r:CUnsignedInt = 0, g:CUnsignedInt = 0, b:CUnsignedInt = 0; 27 | Scanner(string: rString).scanHexInt32(&r) 28 | Scanner(string: gString).scanHexInt32(&g) 29 | Scanner(string: bString).scanHexInt32(&b) 30 | return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(1)) 31 | } 32 | 33 | func AHLog(_ message: T, fileName: String = #file, methodName: String = #function, lineNumber: Int = #line){ 34 | #if DEBUG 35 | var fileN = (fileName as NSString).lastPathComponent as NSString 36 | fileN = fileN.substring(with: NSMakeRange(0, fileN.length - 6)) as NSString 37 | 38 | let nowDate = Date().toString(WithFormat: "yyyy-MM-dd hh:mm:ss") 39 | print("--\(nowDate)--\(fileN)--\(methodName)--[\(lineNumber)]: \(message)") 40 | #endif 41 | } 42 | 43 | //获取正确的删除索引 44 | func getRemoveIndex(value: T, array: [T]) -> [Int]{ 45 | var indexArray = [Int]() 46 | var correctArray = [Int]() 47 | 48 | //获取指定值在数组中的索引 49 | for (index,_) in array.enumerated() { 50 | if array[index] == value { 51 | indexArray.append(index) 52 | } 53 | } 54 | 55 | //计算正确的删除索引 56 | for (index, originIndex) in indexArray.enumerated() { 57 | //指定值索引减去索引数组的索引 58 | let correctIndex = originIndex - index 59 | 60 | //添加到正确的索引数组中 61 | correctArray.append(correctIndex) 62 | } 63 | return correctArray 64 | } 65 | 66 | //从数组中删除指定元素 67 | func removeValueFromArray(value: T, array: inout [T]){ 68 | let correctArray = getRemoveIndex(value: value, array: array) 69 | 70 | //从原数组中删除指定元素 71 | for index in correctArray { 72 | array.remove(at: index) 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Gank/Extension/String+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extension.swift 3 | // SubApp 4 | // 5 | // Created by GuYi on 16/8/8. 6 | // Copyright © 2016年 aicai. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | 13 | /// 长度 14 | var length: Int { 15 | return self.characters.count 16 | } 17 | 18 | // MARK: - 快速生成缓存路径 19 | func cachesDir() -> String { 20 | let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last! 21 | 22 | // 生成缓存路径 23 | let name = (self as NSString).lastPathComponent 24 | let filePath = (path as NSString).appendingPathComponent(name) 25 | 26 | return filePath 27 | } 28 | 29 | // MARK: - 快速生成文档路径 30 | func docDir() -> String { 31 | let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last! 32 | 33 | let name = (self as NSString).lastPathComponent 34 | let filePath = (path as NSString).appendingPathComponent(name) 35 | 36 | return filePath 37 | } 38 | 39 | // MARK: - 快速生成临时路径 40 | func tmpDir() -> String { 41 | let path = NSTemporaryDirectory() 42 | 43 | let name = (self as NSString).lastPathComponent 44 | let filePath = (path as NSString).appendingPathComponent(name) 45 | 46 | return filePath 47 | } 48 | 49 | // MARK: - 快速生成富文本 50 | func getAttributedString(_ fontNum: CGFloat, color: UIColor, loc: Int, len: Int) -> NSAttributedString { 51 | let font = UIFont.systemFont(ofSize: fontNum) 52 | let attString = NSMutableAttributedString(string: self) 53 | 54 | attString.addAttributes([ NSFontAttributeName : font, 55 | NSForegroundColorAttributeName : color ], range: NSMakeRange(loc, len)) 56 | return attString 57 | } 58 | 59 | // MARK: - 快速生成年收益文本 60 | func getRateSting(_ smallFont: CGFloat) -> NSAttributedString { 61 | let font = UIFont.systemFont(ofSize: smallFont) 62 | let attString = NSMutableAttributedString(string: self) 63 | 64 | attString.addAttributes([ NSFontAttributeName : font ], range: NSMakeRange((self as NSString).length - 1, 1)) 65 | return attString 66 | } 67 | 68 | // MARK: - 毫秒转化时间 69 | static func get1970Date(_ time: NSNumber, formater: String) -> String { 70 | let date = Date(timeIntervalSince1970: time.doubleValue / 1000.0) 71 | let formatter = DateFormatter() 72 | formatter.dateStyle = DateFormatter.Style.medium 73 | formatter.timeStyle = DateFormatter.Style.short 74 | formatter.dateFormat = formater 75 | return formatter.string(from: date) 76 | } 77 | 78 | // MARK: - 获得千分位分隔符字符串 79 | static func getBigDecimalString(_ string: String) -> String { 80 | var numString = string as NSString 81 | 82 | if numString.doubleValue < 1000.0 { 83 | return numString as String 84 | } 85 | let defultFormatter = NumberFormatter() 86 | defultFormatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4 87 | defultFormatter.numberStyle = NumberFormatter.Style.decimal 88 | //需要限定位数,否则double型小数点后为0时会被省略 89 | if numString.range(of: ".").location != NSNotFound { 90 | defultFormatter.maximumFractionDigits = 2 91 | defultFormatter.minimumFractionDigits = 2 92 | numString = defultFormatter.string(from: NSNumber(value: numString.doubleValue as Double))! as NSString 93 | } else { 94 | defultFormatter.maximumFractionDigits = 0 95 | numString = defultFormatter.string(from: NSNumber(value: numString.intValue as Int32))! as NSString 96 | } 97 | return numString as String 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Gank/Extension/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | var X: CGFloat { 14 | get { 15 | return self.frame.origin.x 16 | } 17 | set(newX) { 18 | self.frame.origin.x = newX 19 | } 20 | } 21 | 22 | var Y: CGFloat { 23 | get { 24 | return self.frame.origin.y 25 | } 26 | set(newY) { 27 | self.frame.origin.y = newY 28 | } 29 | } 30 | 31 | var Height: CGFloat { 32 | get { 33 | return self.frame.size.height 34 | } 35 | set(newHeight) { 36 | self.frame.size.height = newHeight 37 | } 38 | } 39 | 40 | var Width: CGFloat { 41 | get { 42 | return self.frame.size.width 43 | } 44 | set(newWidth) { 45 | self.frame.size.width = newWidth 46 | } 47 | } 48 | 49 | var CenterX: CGFloat { 50 | get { 51 | return self.center.x 52 | } 53 | set(newCenterX) { 54 | self.center.x = newCenterX 55 | } 56 | } 57 | 58 | var CenterY: CGFloat { 59 | get { 60 | return self.center.y 61 | } 62 | set(newCenterY) { 63 | self.center.y = newCenterY 64 | } 65 | } 66 | 67 | var MaxX: CGFloat { 68 | get { 69 | return self.frame.maxX 70 | } 71 | } 72 | 73 | var MaxY: CGFloat { 74 | get { 75 | return self.frame.maxY 76 | } 77 | } 78 | 79 | class func viewFromNib() -> Any? { 80 | return Bundle.main.loadNibNamed(self.className, owner: nil, options: nil)?.last as Any? 81 | } 82 | 83 | class func nib() -> UINib { 84 | return UINib(nibName: self.className, bundle: nil) 85 | } 86 | 87 | // 判断一个控件是否真正显示在主窗口 88 | func isShowingOnKeyWindow() -> Bool { 89 | 90 | // 以主窗口左上角为坐标原点, 计算self的矩形框 91 | let newFrame = kWindow!.convert(self.frame, from: self.superview) 92 | let winBounds = kWindow!.bounds 93 | 94 | // 主窗口的bounds 和 self的矩形框 是否有重叠 95 | let intersects = newFrame.intersects(winBounds) 96 | return !self.isHidden && self.alpha > 0.01 && self.window == kWindow && intersects 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Gank/Extension/Validate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validate.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/8. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Validate { 12 | //MARK:- 正则匹配手机号 13 | static func checkMobile(_ mobileNumbel: String) -> Bool { 14 | /** 15 | * 手机号码: 16 | * 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[6, 7, 8], 18[0-9], 170[0-9] 17 | * 移动号段: 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 18 | * 联通号段: 130,131,132,155,156,185,186,145,176,1709 19 | * 电信号段: 133,153,180,181,189,177,1700 20 | */ 21 | let MOBIL = "^1((3[0-9]|4[57]|5[0-35-9]|7[0678]|8[0-9])\\d{8}$)" 22 | /** 23 | * 中国移动:China Mobile 24 | * 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 25 | */ 26 | let CM = "(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)" 27 | /** 28 | * 中国联通:China Unicom 29 | * 130,131,132,155,156,185,186,145,176,1709 30 | */ 31 | let CU = "(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)" 32 | /** 33 | * 中国电信:China Telecom 34 | * 133,153,180,181,189,177,1700 35 | */ 36 | let CT = "(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)" 37 | let regextestmobile = NSPredicate(format: "SELF MATCHES %@", MOBIL) 38 | let regextestcm = NSPredicate(format: "SELF MATCHES %@", CM) 39 | let regextestcu = NSPredicate(format: "SELF MATCHES %@", CU) 40 | let regextestct = NSPredicate(format: "SELF MATCHES %@", CT) 41 | if regextestmobile.evaluate(with: mobileNumbel) || regextestcm.evaluate(with: mobileNumbel) || regextestcu.evaluate(with: mobileNumbel) || regextestct.evaluate(with: mobileNumbel) { 42 | return true 43 | } 44 | return false 45 | } 46 | 47 | //MARK:- 正则匹配用户身份证号15或18位 48 | static func checkUserIdCard(_ idCard: String) -> Bool { 49 | let pattern = "(^[0-9]{15}$)|([0-9]{17}([0-9]|X)$)"; 50 | let pred = NSPredicate(format: "SELF MATCHES %@", pattern) 51 | let isMatch:Bool = pred.evaluate(with: idCard) 52 | return isMatch; 53 | } 54 | 55 | //MARK:- 正则匹配用户密码6-18位数字和字母组合 56 | static func checkPassword(_ password: String) -> Bool { 57 | let pattern = "^(?![0-9]+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,18}" 58 | let pred = NSPredicate(format: "SELF MATCHES %@", pattern) 59 | let isMatch: Bool = pred.evaluate(with: password) 60 | return isMatch; 61 | } 62 | 63 | //MARK:- 正则匹配URL 64 | static func checkURL(_ url: String) -> Bool { 65 | let pattern = "^[0-9A-Za-z]{1,50}" 66 | let pred = NSPredicate(format: "SELF MATCHES %@", pattern) 67 | let isMatch: Bool = pred.evaluate(with: url) 68 | return isMatch; 69 | } 70 | 71 | //MARK:- 正则匹配用户姓名,20位的中文或英文 72 | static func checkUserName(_ userName: String) -> Bool { 73 | let pattern = "^[a-zA-Z\\u4E00-\\u9FA5]{1,20}" 74 | let pred = NSPredicate(format: "SELF MATCHES %@", pattern) 75 | let isMatch: Bool = pred.evaluate(with: userName) 76 | return isMatch; 77 | } 78 | 79 | //MARK:- 正则匹配用户email 80 | static func checkEmail(_ email: String) -> Bool { 81 | let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" 82 | let pred = NSPredicate(format: "SELF MATCHES %@", pattern) 83 | let isMatch: Bool = pred.evaluate(with: email) 84 | return isMatch; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Gank/Gank-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import "FPSLabel.h" 15 | -------------------------------------------------------------------------------- /Gank/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | 开发者头条 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLSchemes 27 | 28 | wx63cb6a9a9328f689 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSApplicationQueriesSchemes 35 | 36 | wechat 37 | weixin 38 | 39 | LSRequiresIPhoneOS 40 | 41 | NSAppTransportSecurity 42 | 43 | NSAllowsArbitraryLoads 44 | 45 | 46 | NSPhotoLibraryUsageDescription 47 | 使用相册 48 | UILaunchStoryboardName 49 | LaunchScreen 50 | UIRequiredDeviceCapabilities 51 | 52 | armv7 53 | 54 | UIStatusBarHidden 55 | 56 | UIStatusBarStyle 57 | UIStatusBarStyleDefault 58 | UISupportedInterfaceOrientations 59 | 60 | UIInterfaceOrientationPortrait 61 | 62 | UIViewControllerBasedStatusBarAppearance 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Gank/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Gank/Model/AHHomeGankModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHomeGankModel.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/22. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class AHHomeGankModel: GankModel, NSCoding{ 13 | fileprivate var separatorLineH: CGFloat = 1 14 | 15 | fileprivate var editorLabelH: CGFloat = 15 16 | 17 | fileprivate var timeLabelH: CGFloat = 15 18 | 19 | var isShouldShowMoreButton: Bool = false 20 | 21 | var moreBtnFrame: CGRect = CGRect.zero 22 | 23 | var isOpen: Bool = false 24 | 25 | var _cellH: CGFloat? 26 | // cell的整体高度 27 | var cellH: CGFloat { 28 | if _cellH == nil { 29 | 30 | _cellH = separatorLineH + editorLabelH + 8 31 | 32 | // 文字的高度 33 | let maxSize = CGSize(width: cellMaxWidth, height: CGFloat(MAXFLOAT)) 34 | let descTextH = desc?.boundingRect(with: maxSize, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 15)], context: nil).size.height 35 | 36 | var contentTextH: CGFloat = descTextH! 37 | 38 | // 文字大于三行 39 | if descTextH! > UIFont.systemFont(ofSize: 15).lineHeight * 3 { 40 | contentTextH = UIFont.systemFont(ofSize: 15).lineHeight * 3 41 | isShouldShowMoreButton = true 42 | } 43 | 44 | if isOpen { 45 | contentTextH = descTextH! 46 | } 47 | 48 | _cellH = _cellH! + 8 + contentTextH 49 | 50 | if isShouldShowMoreButton { 51 | let moreBtnX = cellMargin * 0.5 52 | let moreBtnY = _cellH! 53 | let moreBtnW: CGFloat = 40.0 54 | let moreBtnH: CGFloat = 20.0 55 | self.moreBtnFrame = CGRect(x: moreBtnX, y: moreBtnY, width: moreBtnW, height: moreBtnH) 56 | 57 | _cellH = _cellH! + moreBtnH 58 | } 59 | 60 | _cellH = _cellH! + 8 + timeLabelH + cellMargin 61 | } 62 | return _cellH! 63 | } 64 | 65 | init(dict: JSON) { 66 | super.init() 67 | for (index, subJson) : (String, JSON) in dict { 68 | switch index { 69 | case _idKey: 70 | self.id = subJson.string 71 | case descKey: 72 | self.desc = subJson.string 73 | case publishedAtKey: 74 | self.publishedAt = subJson.string 75 | case urlKey: 76 | self.url = subJson.string 77 | case typeKey: 78 | self.type = subJson.string 79 | case userKey: 80 | self.user = subJson.string 81 | default: break 82 | } 83 | } 84 | 85 | // 时间处理 86 | let time = self.publishedAt! as NSString 87 | self.publishedAt = time.substring(to: 10) as String 88 | } 89 | 90 | required init(coder aDecoder: NSCoder) { 91 | super.init() 92 | self.id = aDecoder.decodeObject(forKey: "id") as? String 93 | self.publishedAt = aDecoder.decodeObject(forKey: "publishedAt") as? String 94 | self.desc = aDecoder.decodeObject(forKey: "desc") as? String 95 | self.url = aDecoder.decodeObject(forKey: "url") as? String 96 | self.user = aDecoder.decodeObject(forKey: "user") as? String 97 | self.type = aDecoder.decodeObject(forKey: "type") as? String 98 | } 99 | 100 | func encode(with aCoder: NSCoder) { 101 | aCoder.encode(id, forKey: "id") 102 | aCoder.encode(publishedAt, forKey: "publishedAt") 103 | aCoder.encode(desc, forKey: "desc") 104 | aCoder.encode(url, forKey: "url") 105 | aCoder.encode(user, forKey: "user") 106 | aCoder.encode(type, forKey: "type") 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Gank/Model/AHHomeGroupModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHHomeGroupModel.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/22. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class AHHomeGroupModel: NSObject, NSCoding { 13 | var groupTitle: String 14 | var ganks: [AHHomeGankModel] 15 | 16 | 17 | init(dict: JSON, key: String) { 18 | groupTitle = key 19 | ganks = dict[key].arrayValue.map({ dict in 20 | AHHomeGankModel(dict: dict) 21 | }) 22 | super.init() 23 | } 24 | 25 | required init(coder aDecoder: NSCoder) { 26 | self.ganks = aDecoder.decodeObject(forKey: "ganks") as! [AHHomeGankModel] 27 | self.groupTitle = aDecoder.decodeObject(forKey: "groupTitle") as! String 28 | } 29 | 30 | func encode(with aCoder: NSCoder) { 31 | aCoder.encode(ganks, forKey: "ganks") 32 | aCoder.encode(groupTitle, forKey: "groupTitle") 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Gank/Model/AHSearchGankModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHSearchGankModel.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/10. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class AHSearchGankModel: GankModel { 13 | 14 | init(dict: JSON) { 15 | super.init() 16 | for (index, subJson) : (String, JSON) in dict { 17 | switch index { 18 | case "ganhuo_id": 19 | self.id = subJson.string 20 | case "desc": 21 | self.desc = subJson.string 22 | case "publishedAt": 23 | self.publishedAt = subJson.string 24 | case "url": 25 | self.url = subJson.string 26 | case "type": 27 | self.type = subJson.string 28 | case "who": 29 | self.user = subJson.string 30 | default: break 31 | } 32 | } 33 | // 时间处理 34 | let time = self.publishedAt! as NSString 35 | self.publishedAt = time.substring(to: 10) as String 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Gank/Model/GankModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GankModel.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/13. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GankModel: NSObject { 12 | var id: String? 13 | var publishedAt: String? 14 | var desc: String? 15 | var url: String? 16 | var user: String? 17 | var type: String? 18 | 19 | // 在bmob服务器的唯一标示 20 | var objectId: String? 21 | convenience init(bmob: BmobObject) { 22 | self.init() 23 | self.id = bmob.object(forKey: "gankId") as? String 24 | self.desc = bmob.object(forKey: "gankDesc") as? String 25 | self.publishedAt = bmob.object(forKey: "gankPublishAt") as? String 26 | self.url = bmob.object(forKey: "gankUrl") as? String 27 | self.type = bmob.object(forKey: "gankType") as? String 28 | self.user = bmob.object(forKey: "gankUser") as? String 29 | self.objectId = bmob.objectId 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Gank/Potocol/ResuableProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResuableProtocol.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/26. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ViewNameReusable: class { } 12 | 13 | extension ViewNameReusable where Self: UIView { 14 | static var reuseIdentifier: String { 15 | return String(describing: self) 16 | } 17 | } 18 | 19 | extension UITableView { 20 | func dequeueReusableCell(forIndexPath indexPath: IndexPath) -> T where T: ViewNameReusable { 21 | guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else { 22 | fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)") 23 | } 24 | return cell 25 | } 26 | 27 | func dequeueReusableCellFromNib() -> T where T: ViewNameReusable { 28 | guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier) as? T else { 29 | //return T(style: .default, reuseIdentifier: T.reuseIdentifier) 30 | return T.viewFromNib() as! T 31 | } 32 | return cell 33 | } 34 | } 35 | 36 | extension UICollectionView { 37 | func dequeueReusableCell(forIndexPath indexPath: IndexPath) -> T where T: ViewNameReusable { 38 | guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath) as? T else { 39 | fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)") 40 | } 41 | return cell 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Gank/Potocol/UserDefaultsProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsProtocol.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/2/26. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - 命名空间生成唯一Key 12 | protocol KeyNamespaceable { 13 | static func namespaced(_ key: T) -> String 14 | } 15 | 16 | extension KeyNamespaceable { 17 | static func namespaced(_ key: T) -> String { 18 | return String(describing: self) + ".\(key.rawValue)" 19 | } 20 | } 21 | 22 | // MARK: - StringDefaultSettable 23 | protocol StringDefaultSettable: KeyNamespaceable { 24 | associatedtype StringKey: RawRepresentable 25 | } 26 | 27 | extension StringDefaultSettable where StringKey.RawValue == String { 28 | static func set(_ value: String, forKey key: StringKey) { 29 | UserDefaults.standard.set(value, forKey: namespaced(key)) 30 | } 31 | 32 | static func string(forKey key: StringKey) -> String? { 33 | return UserDefaults.standard.string(forKey: namespaced(key)) 34 | } 35 | } 36 | 37 | // MARK: - BoolDefaultSettable 38 | protocol BoolDefaultSettable: KeyNamespaceable { 39 | associatedtype BoolKey: RawRepresentable 40 | } 41 | 42 | extension BoolDefaultSettable where BoolKey.RawValue == String { 43 | static func set(_ value: Bool, forKey key: BoolKey) { 44 | UserDefaults.standard.set(value, forKey: namespaced(key)) 45 | } 46 | 47 | static func bool(forKey key: BoolKey) -> Bool { 48 | return UserDefaults.standard.bool(forKey: namespaced(key)) 49 | } 50 | } 51 | 52 | // MARK: - IntDefaultSettable 53 | protocol IntDefaultSettable: KeyNamespaceable { 54 | associatedtype IntKey: RawRepresentable 55 | } 56 | 57 | extension IntDefaultSettable where IntKey.RawValue == String { 58 | static func set(_ value: Int, forKey key: IntKey) { 59 | UserDefaults.standard.set(value, forKey: namespaced(key)) 60 | } 61 | 62 | static func integer(forKey key: IntKey) -> Int { 63 | return UserDefaults.standard.integer(forKey: namespaced(key)) 64 | } 65 | } 66 | 67 | // MARK: - UrlDefaultSettable 68 | protocol UrlDefaultSettable: KeyNamespaceable { 69 | associatedtype UrlKey: RawRepresentable 70 | } 71 | 72 | extension UrlDefaultSettable where UrlKey.RawValue == String { 73 | static func set(_ value: URL?, forKey key: UrlKey) { 74 | UserDefaults.standard.set(value, forKey: namespaced(key)) 75 | } 76 | 77 | static func url(forKey key: UrlKey) -> URL? { 78 | return UserDefaults.standard.url(forKey: namespaced(key)) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Gank/Resource/add_button_normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/add_button_normal@2x.png -------------------------------------------------------------------------------- /Gank/Resource/adver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/adver.png -------------------------------------------------------------------------------- /Gank/Resource/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/back@2x.png -------------------------------------------------------------------------------- /Gank/Resource/close2_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/close2_button@2x.png -------------------------------------------------------------------------------- /Gank/Resource/close_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/close_button@2x.png -------------------------------------------------------------------------------- /Gank/Resource/find@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/find@2x.png -------------------------------------------------------------------------------- /Gank/Resource/find_select@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/find_select@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon108.png -------------------------------------------------------------------------------- /Gank/Resource/icon_class@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_class@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_clean@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_clean@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_close_block@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_close_block@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_close_second@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_close_second@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_collect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_collect@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_collected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_collected@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_copy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_copy@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_editor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_editor@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_more@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_placeHead1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_placeHead1@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_placeHead@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_placeHead@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_safari@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_safari@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_search@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_search_ gray@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_search_ gray@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_search_blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_search_blue@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_setting@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_setting@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_share@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_share@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_show@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_show@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_show_blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_show_blue@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_time@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_time@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_user1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_user1@2x.png -------------------------------------------------------------------------------- /Gank/Resource/icon_user@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/icon_user@2x.png -------------------------------------------------------------------------------- /Gank/Resource/loading1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading1.png -------------------------------------------------------------------------------- /Gank/Resource/loading2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading2.png -------------------------------------------------------------------------------- /Gank/Resource/loading3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading3.png -------------------------------------------------------------------------------- /Gank/Resource/loading4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading4.png -------------------------------------------------------------------------------- /Gank/Resource/loading5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading5.png -------------------------------------------------------------------------------- /Gank/Resource/loading6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading6.png -------------------------------------------------------------------------------- /Gank/Resource/loading7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading7.png -------------------------------------------------------------------------------- /Gank/Resource/loading8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading8.png -------------------------------------------------------------------------------- /Gank/Resource/loading9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/loading9.png -------------------------------------------------------------------------------- /Gank/Resource/nav_ list@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/nav_ list@2x.png -------------------------------------------------------------------------------- /Gank/Resource/nav_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/nav_back@2x.png -------------------------------------------------------------------------------- /Gank/Resource/nav_back_blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/nav_back_blue@2x.png -------------------------------------------------------------------------------- /Gank/Resource/placeImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/placeImage@2x.png -------------------------------------------------------------------------------- /Gank/Resource/property@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/property@2x.png -------------------------------------------------------------------------------- /Gank/Resource/property_select@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/property_select@2x.png -------------------------------------------------------------------------------- /Gank/Resource/tabBar_bgwhiteColor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/tabBar_bgwhiteColor@2x.png -------------------------------------------------------------------------------- /Gank/Resource/tabbar_home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/tabbar_home@2x.png -------------------------------------------------------------------------------- /Gank/Resource/tabbar_home_highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/tabbar_home_highlighted@2x.png -------------------------------------------------------------------------------- /Gank/Resource/toolBar_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/toolBar_back@2x.png -------------------------------------------------------------------------------- /Gank/Resource/toolBar_forward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/toolBar_forward@2x.png -------------------------------------------------------------------------------- /Gank/Resource/toolBar_like@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/toolBar_like@2x.png -------------------------------------------------------------------------------- /Gank/Resource/toolBar_like_select@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/toolBar_like_select@2x.png -------------------------------------------------------------------------------- /Gank/Resource/toolBar_reload@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AHuaner/Gank/eafd4ee0fa8e812f5d9333e4ff04eb4923dbb32f/Gank/Resource/toolBar_reload@2x.png -------------------------------------------------------------------------------- /Gank/Utility/AHLaunchImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHLaunchImageView.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/8. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHLaunchImageView: UIImageView { 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | setupUI() 16 | } 17 | 18 | required init?(coder aDecoder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | fileprivate func setupUI() { 23 | backgroundColor = UIColorMainBG 24 | image = UIImage(named: "adver.png") 25 | isUserInteractionEnabled = true 26 | } 27 | 28 | deinit { 29 | AHLog("---dealloc---\(type(of: self))") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Gank/Utility/AHLoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHLoadingView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/12. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHLoadingView: UIView { 12 | 13 | lazy var animView: UIImageView = { 14 | let animView = UIImageView() 15 | animView.frame.size = CGSize(width: 300, height: 225) 16 | self.addSubview(animView) 17 | return animView 18 | }() 19 | 20 | override init(frame: CGRect) { 21 | super.init(frame: frame) 22 | setupUI() 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | fileprivate func setupUI() { 30 | var images = [UIImage]() 31 | for i in 1...8 { 32 | let image = UIImage(named: "loading\(i)") 33 | images.append(image!) 34 | } 35 | animView.animationDuration = 1 36 | animView.animationRepeatCount = Int(MAXINTERP) 37 | animView.animationImages = images 38 | animView.startAnimating() 39 | animView.center = self.center 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Gank/Utility/AHMoreCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHMoreCell.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/17. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHMoreCell: UITableViewCell { 12 | 13 | @IBOutlet weak var iconView: UIImageView! 14 | 15 | @IBOutlet weak var titLabel: UILabel! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | } 20 | 21 | override func setSelected(_ selected: Bool, animated: Bool) { 22 | super.setSelected(selected, animated: animated) 23 | } 24 | 25 | static func cellWithTableView(_ tableview: UITableView) -> AHMoreCell { 26 | var cell = tableview.dequeueReusableCell(withIdentifier: "AHMoreCell") 27 | if cell == nil { 28 | cell = self.viewFromNib() as! AHMoreCell 29 | } 30 | return cell as! AHMoreCell 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Gank/Utility/AHMoreCell.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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Gank/Utility/AHMoreView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHMoreView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/17. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHMoreView: UIView { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | 15 | var tableViewdidSelectClouse: ((IndexPath) -> Void)? 16 | 17 | var titles = ["收藏", "分享", "复制链接", "Safari打开"] 18 | var images = ["icon_collect", "icon_share", "icon_copy", "icon_safari"]; 19 | 20 | override func awakeFromNib() { 21 | super.awakeFromNib() 22 | self.autoresizingMask = UIViewAutoresizing() 23 | self.backgroundColor = UIColor.clear 24 | tableView.delegate = self 25 | tableView.dataSource = self 26 | tableView.backgroundColor = RGBColor("606060") 27 | tableView.separatorStyle = .none 28 | } 29 | 30 | class func moreView() -> AHMoreView { 31 | return self.viewFromNib() as! AHMoreView 32 | } 33 | 34 | func gankBe(collected: Bool) { 35 | if collected { 36 | titles[0] = "取消收藏" 37 | images[0] = "icon_collected" 38 | } else { 39 | titles[0] = "收藏" 40 | images[0] = "icon_collect" 41 | } 42 | self.tableView.reloadData() 43 | } 44 | } 45 | 46 | extension AHMoreView: UITableViewDelegate, UITableViewDataSource { 47 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 48 | guard let clouse = tableViewdidSelectClouse else { return } 49 | clouse(indexPath) 50 | } 51 | 52 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 53 | return titles.count 54 | } 55 | 56 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 57 | let cell = AHMoreCell.cellWithTableView(tableView) 58 | cell.iconView.image = UIImage(named: images[indexPath.row]) 59 | cell.titLabel.text = titles[indexPath.row] 60 | return cell 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Gank/Utility/AHMoreView.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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Gank/Utility/AHNavBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHNavBar.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2016/12/22. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHNavBar: UIView { 12 | 13 | var title: String? { 14 | didSet { 15 | titleLabel.text = title! 16 | } 17 | } 18 | 19 | var bgAlpha: CGFloat = 0 { 20 | didSet { 21 | self.backgroundColor = UIColorMainBlue.withAlphaComponent(bgAlpha) 22 | } 23 | } 24 | 25 | fileprivate lazy var titleLabel: UILabel = { 26 | let titleLabel = UILabel() 27 | titleLabel.font = UIFont.systemFont(ofSize: 17) 28 | titleLabel.textColor = UIColor.white 29 | titleLabel.backgroundColor = UIColor.clear 30 | titleLabel.textAlignment = .center 31 | titleLabel.frame = CGRect(x: 0, y: 20, width: 150, height: 44) 32 | titleLabel.CenterX = self.CenterX 33 | return titleLabel 34 | }() 35 | 36 | lazy var searchView: AHSearchView = { 37 | let searchView = AHSearchView.searchView() 38 | return searchView 39 | }() 40 | 41 | lazy var lineView: UIView = { 42 | let lineView = UIView(frame: CGRect(x: 0, y: 64, width: kScreen_W, height: 0.5)) 43 | lineView.backgroundColor = UIColorLine 44 | return lineView 45 | }() 46 | 47 | override init(frame: CGRect) { 48 | super.init(frame: frame) 49 | 50 | backgroundColor = UIColor.clear 51 | addSubview(searchView) 52 | 53 | let W: CGFloat = 80 54 | self.searchView.frame = CGRect(x: kScreen_W - W - 15, y: 27, width: W, height: 30) 55 | } 56 | 57 | required init?(coder aDecoder: NSCoder) { 58 | fatalError("init(coder:) has not been implemented") 59 | } 60 | 61 | func showLongStyle() { 62 | UIView.animate(withDuration: 0.5) { 63 | self.searchView.frame = CGRect(x: 15, y: 27, width: kScreen_W - 30, height: 30) 64 | self.searchView.seatchTitle.text = "搜索更多干货" 65 | self.searchView.seatchTitle.sizeToFit() 66 | } 67 | } 68 | 69 | func showShortStyle() { 70 | let W: CGFloat = 80 71 | UIView.animate(withDuration: 0.5) { 72 | self.searchView.frame = CGRect(x: kScreen_W - W - 15, y: 27, width: W, height: 30) 73 | self.searchView.seatchTitle.text = "搜索" 74 | self.searchView.seatchTitle.sizeToFit() 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /Gank/Utility/AHNoDataView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHNoDataView.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/3/2. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHNoDataView: UIView { 12 | 13 | /// 跳转到干货界面的闭包 14 | var goGankClouse: (() -> Void)? { 15 | didSet { 16 | goGankBtn.isHidden = false 17 | } 18 | } 19 | 20 | fileprivate lazy var describeLabel: UILabel = { 21 | let label = UILabel() 22 | label.text = "还没有收藏的干货" 23 | label.textColor = UIColorTextGray 24 | label.font = UIFont.systemFont(ofSize: 20) 25 | label.sizeToFit() 26 | label.CenterX = self.CenterX 27 | label.CenterY = self.CenterY - 50 28 | return label 29 | }() 30 | 31 | fileprivate lazy var goGankBtn: UIButton = { 32 | let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 130, height: 35)) 33 | btn.setTitle("去读干货", for: .normal) 34 | btn.setTitleColor(UIColorTextBlue, for: .normal) 35 | btn.titleLabel?.font = UIFont.systemFont(ofSize: 17) 36 | btn.layer.borderColor = UIColorTextBlue.cgColor 37 | btn.layer.borderWidth = 1 38 | btn.layer.cornerRadius = 17.5 39 | btn.CenterX = self.describeLabel.CenterX 40 | btn.CenterY = self.describeLabel.CenterY + 50 41 | btn.isHidden = true 42 | btn.addTarget(self, action: #selector(goGankAction), for: .touchUpInside) 43 | return btn 44 | }() 45 | 46 | init(describeText: String, frame: CGRect) { 47 | super.init(frame: frame) 48 | 49 | describeLabel.text = describeText 50 | 51 | self.addSubview(describeLabel) 52 | self.addSubview(goGankBtn) 53 | } 54 | 55 | required init?(coder aDecoder: NSCoder) { 56 | fatalError("init(coder:) has not been implemented") 57 | } 58 | 59 | func goGankAction() { 60 | if goGankClouse != nil { 61 | goGankClouse!() 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Gank/Utility/AHTimeButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHTimeButton.swift 3 | // Gank 4 | // 5 | // Created by CoderAhuan on 2016/12/8. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHTimeButton: UIButton { 12 | fileprivate var time: Int 13 | fileprivate var clickClouse: (() -> Void) 14 | fileprivate var timer: Timer? 15 | 16 | init(frame: CGRect, time: Int, clickAction: @escaping () -> Void) { 17 | self.time = time 18 | self.clickClouse = clickAction 19 | 20 | super.init(frame: frame) 21 | self.frame = frame 22 | setup() 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | func setup() { 30 | setTitle("\(self.time)s跳过", for: .normal) 31 | titleLabel?.font = UIFont.systemFont(ofSize: 12) 32 | backgroundColor = UIColor.orange 33 | layer.cornerRadius = self.Height * 0.4 34 | addTarget(self, action: #selector(skipAction), for: .touchUpInside) 35 | timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) 36 | } 37 | 38 | func skipAction() { 39 | timer?.invalidate() 40 | timer = nil 41 | clickClouse() 42 | } 43 | 44 | func timerAction() { 45 | time = time - 1 46 | setTitle("\(self.time)s跳过", for: .normal) 47 | if time <= 0 { 48 | skipAction() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Gank/Utility/FPSLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // FPSLabel.h 3 | // FPSLabel 4 | // 5 | // Created by GuYi on 16/7/21. 6 | // Copyright © 2016年 aicai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FPSLabel : UILabel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Gank/Utility/FPSLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // FPSLabel.m 3 | // FPSLabel 4 | // 5 | // Created by GuYi on 16/7/21. 6 | // Copyright © 2016年 aicai. All rights reserved. 7 | // 8 | 9 | #import "FPSLabel.h" 10 | 11 | #define kSize CGSizeMake(55, 20) 12 | 13 | @implementation FPSLabel 14 | { 15 | CADisplayLink *_link; 16 | NSUInteger _count; 17 | NSTimeInterval _lastTime; 18 | UIFont *_font; 19 | UIFont *_subFont; 20 | } 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame { 23 | if (frame.size.width == 0 && frame.size.height == 0) { 24 | frame.size = kSize; 25 | } 26 | self = [super initWithFrame:frame]; 27 | self. 28 | self.layer.cornerRadius = 5; 29 | self.clipsToBounds = YES; 30 | self.textAlignment = NSTextAlignmentCenter; 31 | self.userInteractionEnabled = NO; 32 | self.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.700]; 33 | 34 | _font = [UIFont fontWithName:@"Menlo" size:14]; 35 | if (_font) { 36 | _subFont = [UIFont fontWithName:@"Menlo" size:4]; 37 | } else { 38 | _font = [UIFont fontWithName:@"Courier" size:14]; 39 | _subFont = [UIFont fontWithName:@"Courier" size:4]; 40 | } 41 | 42 | _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)]; 43 | [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [_link invalidate]; 49 | } 50 | 51 | - (CGSize)sizeThatFits:(CGSize)size { 52 | return kSize; 53 | } 54 | 55 | - (void)tick:(CADisplayLink *)link { 56 | if (_lastTime == 0) { 57 | _lastTime = link.timestamp; 58 | return; 59 | } 60 | 61 | _count++; 62 | NSTimeInterval delta = link.timestamp - _lastTime; 63 | if (delta < 1) return; 64 | _lastTime = link.timestamp; 65 | float fps = _count / delta; 66 | _count = 0; 67 | 68 | CGFloat progress = fps / 60.0; 69 | UIColor *color = [UIColor colorWithHue:0.27 * (progress - 0.2) saturation:1 brightness:0.9 alpha:1]; 70 | 71 | NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d FPS",(int)round(fps)]]; 72 | [text addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, text.length - 3)]; 73 | [text addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(text.length - 3, 3)]; 74 | [text addAttribute:NSFontAttributeName value:_font range:NSMakeRange(0, text.length)]; 75 | [text addAttribute:NSFontAttributeName value:_subFont range:NSMakeRange(text.length - 4, 1)]; 76 | self.attributedText = text; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Gank/Utility/NSObject+alertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+alertController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/13. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias ConfirmsHandlerClouse = ((_ action: UIAlertAction) -> Void) 12 | typealias CancelClouse = ((_ action: UIAlertAction) -> Void) 13 | 14 | extension NSObject { 15 | 16 | /** 17 | * @param title 标题 18 | * @param message 信息 19 | * @param cancelTitle 取消标题, 默认文字是 "取消" 20 | * @param confirmTitle 确定标题, 默认文字是 "确定" 21 | * @param confirm 确定Clouse 22 | * @param cancel 取消Clouse 23 | */ 24 | func showAlertController(locationVC: UIViewController, title: String, message: String, cancelTitle: String = "取消", confirmTitle: String = "确定", confrimClouse: @escaping ConfirmsHandlerClouse, cancelClouse: @escaping CancelClouse) { 25 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) 26 | 27 | let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { (alertAction) in 28 | cancelClouse(alertAction) 29 | // locationVC.dismiss(animated: true, completion: nil) 30 | } 31 | 32 | let confirmAction = UIAlertAction(title: confirmTitle, style: .default) { (alertAction) in 33 | confrimClouse(alertAction) 34 | } 35 | 36 | alertController.addAction(cancelAction) 37 | alertController.addAction(confirmAction) 38 | locationVC.present(alertController, animated: true, completion: nil) 39 | } 40 | 41 | /** 42 | * @param title 标题 43 | * @param message 信息 44 | * @param confirmTitle 确定标题 45 | * @param confirm 确定Clouse 46 | */ 47 | func showAlertController(locationVC: UIViewController, title: String, message: String, confirmTitle: String, confrimClouse : @escaping ConfirmsHandlerClouse) { 48 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) 49 | 50 | let confirmAction = UIAlertAction(title: confirmTitle, style: .default) { (alertAction) in 51 | confrimClouse(alertAction) 52 | } 53 | 54 | alertController.addAction(confirmAction) 55 | locationVC.present(alertController, animated: true, completion: nil) 56 | } 57 | 58 | /** 59 | * @param title 标题 60 | * @param message 信息 61 | * @param cancelTitle 取消标题 62 | * @param cancel 取消Clouse 63 | */ 64 | func showAlertController(locationVC: UIViewController, title: String, message: String, cancelTitle: String, cancelClouse: @escaping CancelClouse) { 65 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) 66 | 67 | let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { (alertAction) in 68 | cancelClouse(alertAction) 69 | // locationVC.dismiss(animated: true, completion: nil) 70 | } 71 | 72 | alertController.addAction(cancelAction) 73 | locationVC.present(alertController, animated: true, completion: nil) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Gank/Utility/SQLite/AHGankDAO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHGankDAO.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/18. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct AHGankDAO { 12 | /// 缓存干货数据 13 | static func cacheGanks(type: String, ganks: [JSONObject]) { 14 | let userId = "AHuaner" 15 | SQLiteManager.shareManager().dbQueue?.inTransaction({ (db, rollback) -> Void in 16 | for dict in ganks { 17 | let gankId = dict["_id"] as! String 18 | // JSON -> 二进制 -> 字符串 19 | let data = try! JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted) 20 | let gankText = String(data: data, encoding: String.Encoding.utf8)! 21 | 22 | let sql = "INSERT INTO T_\(type) (gankId, gankText, userId) VALUES ('\(gankId)', '\(gankText)', '\(userId)')" 23 | 24 | do { 25 | try db?.executeUpdate(sql, values: nil) 26 | } catch { 27 | print("error") 28 | } 29 | } 30 | }) 31 | } 32 | 33 | /// 从数据库中加载缓存数据 34 | static func loadCacheGanks(type: String, finished: @escaping ([JSONObject]) -> ()) { 35 | let sql = "SELECT * FROM T_\(type) " 36 | SQLiteManager.shareManager().dbQueue?.inDatabase({ (db) in 37 | var ganks = [JSONObject]() 38 | if let res = db?.executeQuery(sql, withArgumentsIn: nil) { 39 | while res.next() { 40 | guard let gankText = res.string(forColumn: "gankText") else { return } 41 | let data = gankText.data(using: String.Encoding.utf8)! 42 | 43 | let dict = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! JSONObject 44 | ganks.append(dict) 45 | } 46 | finished(ganks) 47 | } 48 | finished(ganks) 49 | }) 50 | } 51 | 52 | /// 清空对应的表 53 | static func cleanCacheGanks(type: String) { 54 | let sql = "DELETE FROM T_\(type)" 55 | SQLiteManager.shareManager().dbQueue?.inDatabase({ (db) -> Void in 56 | db?.executeUpdate(sql, withArgumentsIn: nil) 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Gank/Utility/SQLiteManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SQLiteManager.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/18. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FMDB 11 | 12 | class SQLiteManager: NSObject { 13 | 14 | private static let manager: SQLiteManager = SQLiteManager() 15 | 16 | /// 单粒 17 | class func shareManager() -> SQLiteManager { 18 | return manager 19 | } 20 | 21 | var dbQueue: FMDatabaseQueue? 22 | 23 | /// 打开数据库 24 | func openDB(DBName: String) { 25 | 26 | let path = DBName.docDir() 27 | 28 | // 创建数据库对象 29 | dbQueue = FMDatabaseQueue(path: path) 30 | } 31 | 32 | /// 创建表 33 | func creatTable(tableName: String) { 34 | let sql = "CREATE TABLE IF NOT EXISTS T_\(tableName)( \n" + 35 | "gankId TEXT PRIMARY KEY, \n" + 36 | "gankText TEXT, \n" + 37 | "userId TEXT, \n" + 38 | "createDate TEXT NOT NULL DEFAULT (datetime('now', 'localtime')) \n" + 39 | "); \n" 40 | 41 | dbQueue!.inDatabase { (db) -> Void in 42 | db!.executeUpdate(sql, withArgumentsIn: nil) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Gank/Utility/Transtion/AHClosePopTranstion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHClosePopTranstion.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/7. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHClosePopTranstion: NSObject { 12 | 13 | } 14 | 15 | extension AHClosePopTranstion: UIViewControllerAnimatedTransitioning { 16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 17 | return 0.3 18 | } 19 | 20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 21 | let containerView = transitionContext.containerView 22 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! 23 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! 24 | 25 | containerView.addSubview(toVC.view) 26 | containerView.addSubview(fromVC.view) 27 | 28 | let snapView = fromVC.navigationController?.view.superview?.viewWithTag(3333) 29 | let maskView = fromVC.navigationController?.view.superview?.viewWithTag(4444) 30 | 31 | toVC.tabBarController?.tabBar.isHidden = false 32 | 33 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 34 | fromVC.view.transform = CGAffineTransform(translationX: 0, y: fromVC.view.Height) 35 | }) { (finish) in 36 | 37 | snapView?.removeFromSuperview() 38 | maskView?.removeFromSuperview() 39 | // toVC.navigationController?.delegate = nil 40 | 41 | transitionContext.completeTransition(true) 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Gank/Utility/Transtion/AHPopTranstion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHPopTranstion.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/7. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHPopTranstion: NSObject { 12 | 13 | } 14 | 15 | extension AHPopTranstion: UIViewControllerAnimatedTransitioning { 16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 17 | return 0.5 18 | } 19 | 20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 21 | let containerView = transitionContext.containerView 22 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! 23 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! 24 | 25 | toVC.view.alpha = 0 26 | fromVC.view.isHidden = true 27 | 28 | guard let snapShotView = fromVC.view.snapshotView(afterScreenUpdates: false) else { return } 29 | snapShotView.frame = fromVC.view.frame 30 | 31 | containerView.addSubview(toVC.view) 32 | containerView.addSubview(snapShotView) 33 | 34 | let snapView = fromVC.navigationController?.view.superview?.viewWithTag(3333) 35 | let maskView = fromVC.navigationController?.view.superview?.viewWithTag(4444) 36 | 37 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { 38 | snapShotView.transform = CGAffineTransform(translationX: 0, y: -snapShotView.Height) 39 | snapView?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0); 40 | maskView?.backgroundColor = UIColor.black.withAlphaComponent(0) 41 | }) { (finish) in 42 | snapShotView.removeFromSuperview() 43 | snapView?.removeFromSuperview() 44 | maskView?.removeFromSuperview() 45 | 46 | toVC.tabBarController?.tabBar.isHidden = false 47 | toVC.view.alpha = 1.0 48 | // toVC.navigationController?.delegate = nil 49 | 50 | transitionContext.completeTransition(true) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Gank/Utility/Transtion/AHPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHPresentationController.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/8. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHPresentationController: UIPresentationController { 12 | var presentFrame = CGRect.zero 13 | 14 | override func containerViewWillLayoutSubviews() { 15 | presentedView?.frame = presentFrame 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Gank/Utility/Transtion/AHPushTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHPushTransition.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/6. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHPushTransition: NSObject { 12 | 13 | } 14 | 15 | extension AHPushTransition: UIViewControllerAnimatedTransitioning { 16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 17 | return 0.6 18 | } 19 | 20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 21 | 22 | let containerView = transitionContext.containerView 23 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! AHGankViewController 24 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! 25 | 26 | let tempView = UIView(frame: fromVC.popRect) 27 | tempView.backgroundColor = RGBColor(r: 234, g: 234, b: 234, alpha: 1) 28 | 29 | guard let snapShotView = fromVC.tabBarController?.view.snapshotView(afterScreenUpdates: false) else { return } 30 | snapShotView.frame = containerView.frame 31 | snapShotView.tag = 3333 32 | 33 | let maskView = UIView(frame: containerView.frame) 34 | maskView.backgroundColor = UIColor.black.withAlphaComponent(0.6) 35 | maskView.tag = 4444 36 | 37 | fromVC.view.alpha = 0 38 | toVC.view.alpha = 0 39 | 40 | containerView.addSubview(toVC.view) 41 | containerView.addSubview(snapShotView) 42 | containerView.addSubview(maskView) 43 | containerView.addSubview(tempView) 44 | 45 | // 计算缩放比例 46 | let tempViewYScale = max(2.5 * tempView.frame.minY / tempView.Height, 2.5 * (containerView.Height - tempView.frame.minY) / tempView.Height) 47 | 48 | UIView.animate(withDuration: 0.2, animations: { 49 | snapShotView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9); 50 | }) { (finished) in 51 | UIView.animate(withDuration: self.transitionDuration(using: transitionContext) - 0.2, animations: { 52 | tempView.transform = CGAffineTransform(scaleX: 1, y: tempViewYScale); 53 | }, completion: { (finished) in 54 | tempView.removeFromSuperview() 55 | snapShotView.removeFromSuperview() 56 | maskView.removeFromSuperview() 57 | 58 | toVC.view.alpha = 1.0 59 | fromVC.view.alpha = 1.0 60 | 61 | toVC.navigationController?.view.superview?.insertSubview(snapShotView, at: 0) 62 | toVC.navigationController?.view.superview?.insertSubview(maskView, aboveSubview: snapShotView) 63 | toVC.navigationController?.delegate = nil 64 | //告诉系统动画结束 65 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 66 | }) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Gank/Utility/Transtion/AHTurnVCTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AHTurnVCTransitionManager.swift 3 | // Gank 4 | // 5 | // Created by AHuaner on 2017/1/8. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AHTurnVCTransitionManager: NSObject { 12 | var presentFrame = CGRect.zero 13 | fileprivate var isPresent = false 14 | } 15 | 16 | extension AHTurnVCTransitionManager: UIViewControllerTransitioningDelegate { 17 | 18 | // 该方法用于返回一个负责转场动画的对象 19 | // 可以在该对象中控制弹出视图的尺寸等 20 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?{ 21 | 22 | let presentController = AHPresentationController(presentedViewController: presented, presenting: presenting) 23 | 24 | presentController.presentFrame = presentFrame 25 | 26 | return presentController 27 | } 28 | 29 | // 该方法用于返回一个负责转场如何出现的对象 30 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 31 | 32 | isPresent = true 33 | return self 34 | } 35 | 36 | // 该方法用于返回一个负责转场如何消失的对象 37 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 38 | 39 | isPresent = false 40 | return self 41 | } 42 | } 43 | 44 | extension AHTurnVCTransitionManager: UIViewControllerAnimatedTransitioning { 45 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 46 | return 0.5 47 | } 48 | 49 | // 专门用于管理modal如何展现和消失的, 无论是展现还是消失都会调用该方法 50 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 51 | if isPresent { 52 | popoverViewWillShow(transitionContext) 53 | } else { 54 | popoverViewWillHide(transitionContext) 55 | } 56 | } 57 | 58 | fileprivate func popoverViewWillShow(_ transitionContext: UIViewControllerContextTransitioning) { 59 | 60 | guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return } 61 | 62 | transitionContext.containerView.addSubview(toView) 63 | toView.alpha = 0 64 | 65 | UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { 66 | toView.alpha = 1 67 | }, completion: { (_) in 68 | transitionContext.completeTransition(true) 69 | }) 70 | } 71 | 72 | fileprivate func popoverViewWillHide(_ transitionContext: UIViewControllerContextTransitioning?) { 73 | 74 | guard let fromView = transitionContext?.view(forKey: UITransitionContextViewKey.from) else { return } 75 | 76 | UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { 77 | fromView.alpha = 0 78 | }, completion: { (_) in 79 | transitionContext?.completeTransition(true) 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Gank/Utility/UserDefaultsEVO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsEVO.swift 3 | // UserDefaultsEVO 4 | // 5 | // Created by CoderAhuan on 2017/2/22. 6 | // Copyright © 2017年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: UserDefault通用字段 12 | extension UserDefaults { 13 | enum AHData: String, UserDefaultSettable { 14 | case mobilePhoneNumber /// 上一次登录的手机号 15 | case lastDate /// 首页缓存数据的日期 16 | } 17 | } 18 | 19 | public protocol UserDefaultSettable { 20 | var uniqueKey: String { get } 21 | } 22 | 23 | public extension UserDefaultSettable where Self: RawRepresentable, Self.RawValue == String { 24 | public var uniqueKey: String { 25 | return "\(Self.self).\(rawValue)" 26 | } 27 | 28 | public func store(value: Any?){ 29 | UserDefaults.standard.set(value, forKey: uniqueKey) 30 | } 31 | public var storedValue: Any? { 32 | return UserDefaults.standard.value(forKey: uniqueKey) 33 | } 34 | public var storedString: String? { 35 | return storedValue as? String 36 | } 37 | 38 | public func store(url: URL?) { 39 | UserDefaults.standard.set(url, forKey: uniqueKey) 40 | } 41 | public var storedURL: URL? { 42 | return UserDefaults.standard.url(forKey: uniqueKey) 43 | } 44 | 45 | public func store(value: Bool) { 46 | UserDefaults.standard.set(value, forKey: uniqueKey) 47 | } 48 | public var storedBool: Bool { 49 | return UserDefaults.standard.bool(forKey: uniqueKey) 50 | } 51 | 52 | public func store(value: Int) { 53 | UserDefaults.standard.set(value, forKey: uniqueKey) 54 | } 55 | public var storedInt: Int { 56 | return UserDefaults.standard.integer(forKey: uniqueKey) 57 | } 58 | 59 | public func store(value: Double) { 60 | UserDefaults.standard.set(value, forKey: uniqueKey) 61 | } 62 | public var storedDouble: Double { 63 | return UserDefaults.standard.double(forKey: uniqueKey) 64 | } 65 | 66 | public func store(value: Float) { 67 | UserDefaults.standard.set(value, forKey: uniqueKey) 68 | } 69 | public var storedFloat: Float { 70 | return UserDefaults.standard.float(forKey: uniqueKey) 71 | } 72 | 73 | /// removed object from standard userdefaults 74 | public func removed() { 75 | UserDefaults.standard.removeObject(forKey: uniqueKey) 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /GankTests/GankTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GankTests.swift 3 | // GankTests 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Gank 11 | 12 | class GankTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GankTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /GankUITests/GankUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GankUITests.swift 3 | // GankUITests 4 | // 5 | // Created by CoderAhuan on 2016/12/7. 6 | // Copyright © 2016年 CoderAhuan. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GankUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GankUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 CoderAhuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'Gank' do 4 | use_frameworks! 5 | pod 'Alamofire', '~> 4.0' 6 | pod 'SwiftyJSON' 7 | pod 'SVProgressHUD' 8 | pod 'SDWebImage' 9 | pod 'MJRefresh' 10 | pod 'YYWebImage' 11 | pod 'FMDB' 12 | pod 'IQKeyboardManagerSwift' 13 | pod 'BmobSDK' 14 | 15 | # U-Share SDK UI模块(分享面板,建议添加) 16 | pod 'UMengUShare/UI' 17 | # 集成微信 18 | pod 'UMengUShare/Social/WeChat' 19 | # 集成短信 20 | #pod 'UMengUShare/Social/SMS' 21 | 22 | # 腾讯Bugly 23 | pod 'Bugly' 24 | 25 | 26 | pod 'RxSwift', '~> 3.0' 27 | pod 'RxCocoa', '~> 3.0' 28 | 29 | end 30 | 31 | post_install do |installer| 32 | installer.pods_project.targets.each do |target| 33 | target.build_configurations.each do |config| 34 | config.build_settings['SWIFT_VERSION'] = '3.0' 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - BmobSDK (2.0.1) 4 | - Bugly (2.4.2) 5 | - FMDB (2.6.2): 6 | - FMDB/standard (= 2.6.2) 7 | - FMDB/standard (2.6.2) 8 | - IQKeyboardManagerSwift (4.0.7) 9 | - MJRefresh (3.1.12) 10 | - RxCocoa (3.0.1): 11 | - RxSwift (~> 3.0) 12 | - RxSwift (3.0.1) 13 | - SDWebImage (3.8.2): 14 | - SDWebImage/Core (= 3.8.2) 15 | - SDWebImage/Core (3.8.2) 16 | - SVProgressHUD (2.1.1) 17 | - SwiftyJSON (3.1.3) 18 | - UMengUShare/Core (6.1.1): 19 | - UMengUShare/Network 20 | - UMengUShare/Network (6.1.1) 21 | - UMengUShare/Social/WeChat (6.1.1): 22 | - UMengUShare/Core 23 | - UMengUShare/UI (6.1.1): 24 | - UMengUShare/Core 25 | - YYCache (1.0.4) 26 | - YYImage (1.0.4): 27 | - YYImage/Core (= 1.0.4) 28 | - YYImage/Core (1.0.4) 29 | - YYWebImage (1.0.5): 30 | - YYCache 31 | - YYImage 32 | 33 | DEPENDENCIES: 34 | - Alamofire (~> 4.0) 35 | - BmobSDK 36 | - Bugly 37 | - FMDB 38 | - IQKeyboardManagerSwift 39 | - MJRefresh 40 | - RxCocoa (~> 3.0) 41 | - RxSwift (~> 3.0) 42 | - SDWebImage 43 | - SVProgressHUD 44 | - SwiftyJSON 45 | - UMengUShare/Social/WeChat 46 | - UMengUShare/UI 47 | - YYWebImage 48 | 49 | SPEC CHECKSUMS: 50 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 51 | BmobSDK: 315013af15af31697a3b92cad2c74effed849b85 52 | Bugly: b09cd3c7e54048a0e079be88a7a99610f14eab8f 53 | FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a 54 | IQKeyboardManagerSwift: 58bbcf46b5cf9fd4ed625d28a60cb2fbae4ab0f4 55 | MJRefresh: b96cdb21c4aa75a7b07654311ab2f315c497e806 56 | RxCocoa: 15a52fc590dcc700cb4a690a633b5c5184ce3a78 57 | RxSwift: af5680055c4ad04480189c52d28385b1029493a6 58 | SDWebImage: 098e97e6176540799c27e804c96653ee0833d13c 59 | SVProgressHUD: 1127180503b9a308ddf7006f3cc58fc05cc1092d 60 | SwiftyJSON: 38a8ea2006779c0fc4c310cb2ee8195327740faf 61 | UMengUShare: 0a80f54ee06f71f3de7c57764c36339e8bf9b7e7 62 | YYCache: 8105b6638f5e849296c71f331ff83891a4942952 63 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 64 | YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928 65 | 66 | PODFILE CHECKSUM: 65177e52e0f3e4a43cd304f4f7651e4710884f1a 67 | 68 | COCOAPODS: 1.2.1.beta.1 69 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - BmobSDK (2.0.1) 4 | - Bugly (2.4.2) 5 | - FMDB (2.6.2): 6 | - FMDB/standard (= 2.6.2) 7 | - FMDB/standard (2.6.2) 8 | - IQKeyboardManagerSwift (4.0.7) 9 | - MJRefresh (3.1.12) 10 | - RxCocoa (3.0.1): 11 | - RxSwift (~> 3.0) 12 | - RxSwift (3.0.1) 13 | - SDWebImage (3.8.2): 14 | - SDWebImage/Core (= 3.8.2) 15 | - SDWebImage/Core (3.8.2) 16 | - SVProgressHUD (2.1.1) 17 | - SwiftyJSON (3.1.3) 18 | - UMengUShare/Core (6.1.1): 19 | - UMengUShare/Network 20 | - UMengUShare/Network (6.1.1) 21 | - UMengUShare/Social/WeChat (6.1.1): 22 | - UMengUShare/Core 23 | - UMengUShare/UI (6.1.1): 24 | - UMengUShare/Core 25 | - YYCache (1.0.4) 26 | - YYImage (1.0.4): 27 | - YYImage/Core (= 1.0.4) 28 | - YYImage/Core (1.0.4) 29 | - YYWebImage (1.0.5): 30 | - YYCache 31 | - YYImage 32 | 33 | DEPENDENCIES: 34 | - Alamofire (~> 4.0) 35 | - BmobSDK 36 | - Bugly 37 | - FMDB 38 | - IQKeyboardManagerSwift 39 | - MJRefresh 40 | - RxCocoa (~> 3.0) 41 | - RxSwift (~> 3.0) 42 | - SDWebImage 43 | - SVProgressHUD 44 | - SwiftyJSON 45 | - UMengUShare/Social/WeChat 46 | - UMengUShare/UI 47 | - YYWebImage 48 | 49 | SPEC CHECKSUMS: 50 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 51 | BmobSDK: 315013af15af31697a3b92cad2c74effed849b85 52 | Bugly: b09cd3c7e54048a0e079be88a7a99610f14eab8f 53 | FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a 54 | IQKeyboardManagerSwift: 58bbcf46b5cf9fd4ed625d28a60cb2fbae4ab0f4 55 | MJRefresh: b96cdb21c4aa75a7b07654311ab2f315c497e806 56 | RxCocoa: 15a52fc590dcc700cb4a690a633b5c5184ce3a78 57 | RxSwift: af5680055c4ad04480189c52d28385b1029493a6 58 | SDWebImage: 098e97e6176540799c27e804c96653ee0833d13c 59 | SVProgressHUD: 1127180503b9a308ddf7006f3cc58fc05cc1092d 60 | SwiftyJSON: 38a8ea2006779c0fc4c310cb2ee8195327740faf 61 | UMengUShare: 0a80f54ee06f71f3de7c57764c36339e8bf9b7e7 62 | YYCache: 8105b6638f5e849296c71f331ff83891a4942952 63 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 64 | YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928 65 | 66 | PODFILE CHECKSUM: 65177e52e0f3e4a43cd304f4f7651e4710884f1a 67 | 68 | COCOAPODS: 1.2.1.beta.1 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The Gank.io client based on Swift 4 | 5 | # ScreenShots 6 | 7 | ![](http://oqe8vk151.bkt.clouddn.com/homepage1.png?imageView2/2/w/375/h/667/q/100) 8 | 9 | ![](http://oqe8vk151.bkt.clouddn.com/homepage2.png?imageView2/2/w/375/h/667/q/100) 10 | 11 | ![](http://oqe8vk151.bkt.clouddn.com/gank1.png?imageView2/2/w/375/h/667/q/100) 12 | 13 | ![](http://oqe8vk151.bkt.clouddn.com/gankdetail.png?imageView2/2/w/375/h/667/q/100) 14 | 15 | ![](http://oqe8vk151.bkt.clouddn.com/channel.png?imageView2/2/w/375/h/667/q/100) 16 | 17 | ![](http://oqe8vk151.bkt.clouddn.com/mine.png?imageView2/2/w/375/h/667/q/100) 18 | 19 | ![](http://oqe8vk151.bkt.clouddn.com/collection.png?imageView2/2/w/375/h/667/q/100) 20 | 21 | ![](http://oqe8vk151.bkt.clouddn.com/searchs.png?imageView2/2/w/375/h/667/q/100) 22 | 23 | # Thanks 24 | 25 | [daimajia](https://github.com/daimajia) 26 | [gank.io](http://gank.io/) 27 | 28 | # License 29 | 30 | Gank is available under the MIT license. See the [LICENSE](https://github.com/AHuaner/Gank/blob/master/LICENSE) file for more info. 31 | 32 | 33 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier "com.ahuaner.gank" # The bundle identifier of your app 2 | apple_id "ahuaner@126.com" # Your Apple email address 3 | 4 | team_id "49P572Q8VR" # Developer Portal Team ID 5 | 6 | # you can even provide different app identifiers, Apple IDs and team names per lane: 7 | # More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md 8 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # Customise this file, documentation can be found here: 2 | # https://github.com/fastlane/fastlane/tree/master/fastlane/docs 3 | # All available actions: https://docs.fastlane.tools/actions 4 | # can also be listed using the `fastlane actions` command 5 | 6 | # Change the syntax highlighting to Ruby 7 | # All lines starting with a # are ignored when running `fastlane` 8 | 9 | # If you want to automatically update fastlane if a new version is available: 10 | # update_fastlane 11 | 12 | # This is the minimum version number required. 13 | # Update this, if you use features of a newer version 14 | fastlane_version "2.28.3" 15 | 16 | default_platform :ios 17 | 18 | platform :ios do 19 | before_all do 20 | # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..." 21 | # cocoapods 22 | 23 | end 24 | 25 | desc "Runs all the tests" 26 | lane :test do 27 | scan 28 | end 29 | 30 | desc "Submit a new Beta Build to Apple TestFlight" 31 | desc "This will also make sure the profile is up to date" 32 | lane :beta do 33 | # match(type: "appstore") # more information: https://codesigning.guide 34 | produce( 35 | username: 'ahuaner@126.com', 36 | app_identifier: 'com.ahuaner.gank', 37 | app_name: 'Gank ahuaner', 38 | language: 'English', 39 | app_version: '1.0', 40 | sku: 'com.ahuaner.gank', 41 | skip_itc: true, 42 | enabled_features: {} 43 | ) 44 | 45 | cert() 46 | 47 | sigh( 48 | adhoc: true, 49 | force: true, 50 | provisioning_name: 'gankAdhoc', 51 | filename: 'gankAdhoc.mobileprovision' 52 | ) 53 | 54 | gym( 55 | scheme: "Gank", 56 | clean: true, 57 | export_method: 'ad-hoc' 58 | ) 59 | # pilot 60 | 61 | # sh "your_script.sh" 62 | # You can also use other beta testing services here (run `fastlane actions`) 63 | end 64 | 65 | desc "update_sigh" 66 | lane :update_sigh do 67 | sigh( 68 | adhoc: true, 69 | # force: true, 70 | provisioning_name: 'gankAdhoc', 71 | filename: 'gankAdhoc.mobileprovision' 72 | ) 73 | 74 | sigh( 75 | development: true, 76 | # force: true, 77 | provisioning_name: 'gankDev', 78 | filename: 'gankDev.mobileprovision' 79 | ) 80 | end 81 | 82 | 83 | desc "Deploy a new version to the App Store" 84 | lane :release do 85 | # match(type: "appstore") 86 | # snapshot 87 | gym(scheme: "Gank") # Build your app - more options available 88 | deliver(force: true) 89 | # frameit 90 | end 91 | 92 | # You can define as many lanes as you want 93 | 94 | after_all do |lane| 95 | # This block is called, only if the executed lane was successful 96 | 97 | # slack( 98 | # message: "Successfully deployed new App Update." 99 | # ) 100 | end 101 | 102 | error do |lane, exception| 103 | # slack( 104 | # message: exception.message, 105 | # success: false 106 | # ) 107 | end 108 | end 109 | 110 | 111 | # More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md 112 | # All available actions: https://docs.fastlane.tools/actions 113 | 114 | # fastlane reports which actions are used 115 | # No personal data is recorded. Learn more at https://github.com/fastlane/enhancer 116 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | ## Choose your installation method: 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Homebrew 16 | Installer Script 17 | Rubygems 18 |
macOSmacOSmacOS or Linux with Ruby 2.0.0 or above
brew cask install fastlaneDownload the zip file. Then double click on the install script (or run it in a terminal window).sudo gem install fastlane -NV
30 | 31 | # Available Actions 32 | ## iOS 33 | ### ios test 34 | ``` 35 | fastlane ios test 36 | ``` 37 | Runs all the tests 38 | ### ios beta 39 | ``` 40 | fastlane ios beta 41 | ``` 42 | Submit a new Beta Build to Apple TestFlight 43 | 44 | This will also make sure the profile is up to date 45 | ### ios update_sigh 46 | ``` 47 | fastlane ios update_sigh 48 | ``` 49 | update_sigh 50 | ### ios release 51 | ``` 52 | fastlane ios release 53 | ``` 54 | Deploy a new version to the App Store 55 | 56 | ---- 57 | 58 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 59 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 60 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 61 | --------------------------------------------------------------------------------