Swift实现糗事百科Demo(实战项目)

来源:互联网 发布:管家婆软件操作流程 编辑:程序博客网 时间:2024/05/16 16:58

本项目借用了某兄弟上传到code4app上的JokerClient源码中的接口,在此先谢谢这位兄弟!


在这里,你将会学习到解析JSON数据,网络请求功能,动态调整cell内容等功能!!!




最终的结果 是这样的,项目相对简单,很适合入门!下面让我们一起开始教程之旅吧!


1、先看下项目工程结构:



第一步:创建Utitlities文件夹,先完成基础通用的辅助功能

1、网络请求类:HttpRequest.Swift

[objc] view plain copy print?
  1. import Foundation  
  2.   
  3. ///  
  4. /// @brief 网络请求相关类  
  5. /// @date  2014-10-09  
  6. /// @author huangyibiao  
  7. ///  
  8. class HttpRequest: NSObject {  
  9.     override init() {  
  10.         super.init()  
  11.     }  
  12.       
  13.     ///  
  14.     /// @brief 把二进制数据转换成JSON格式的数据  
  15.     /// @param data NSDictionary?类型,网络请求返回来的二进制数据  
  16.     /// @return 如果解析成功,返回NSDictionary?类型的JSON数据;如果解析失败,会返回nil  
  17.     ///  
  18.     class func parseJSONData(data: AnyObject?) ->NSDictionary? {  
  19.         if let downloadData: NSData = data as? NSData {  
  20.             var jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(downloadData,  
  21.                 options: NSJSONReadingOptions.MutableContainers,  
  22.                 error: nil) as? NSDictionary  
  23.               
  24.             return jsonData as? NSDictionary  
  25.         }  
  26.           
  27.         // 当解析失败时,会返回nil  
  28.         return nil  
  29.     }  
  30.       
  31.     ///  
  32.     /// @brief 异步网络请求方法,需要请求地址参数及回调  
  33.     /// @param urlString 请求地址  
  34.     /// @param completion 请求完成或者请求失败的回调  
  35.     /// @return 请求成功时,会返回NSDictionary?的字典格式的数据,如果请求失败,会返回nil  
  36.     ///  
  37.     class func request(#urlString: String?, completion: (data: NSDictionary?) ->Void) {  
  38.         if urlString == nil {  
  39.             dispatch_async(dispatch_get_main_queue(), { () -> Void in  
  40.                 println("urlstring 为空")  
  41.                 // 请求出现,则返回nil对象表示  
  42.                 completion(data: nil)  
  43.             })  
  44.             return  
  45.         }  
  46.           
  47.         let url = NSURL.URLWithString(urlString!)  
  48.         let request = NSURLRequest(URL: url)  
  49.           
  50.         let queue = NSOperationQueue()  
  51.         NSURLConnection.sendAsynchronousRequest(request, queue: queue) {  
  52.             (response, data, error) -> Void in  
  53.             if error != nil {  
  54.                 dispatch_async(dispatch_get_main_queue(), { () -> Void in  
  55.                     println(error)  
  56.                     // 请求出现,则返回nil对象表示  
  57.                     completion(data: nil)  
  58.                 })  
  59.             } else { // 请求成功,则返回正确的数据  
  60.                 let parseData = self.parseJSONData(data)  
  61.                   
  62.                 dispatch_async(dispatch_get_main_queue(), { () -> Void in  
  63.                     completion(data: parseData)  
  64.                 })  
  65.             }  
  66.         }  
  67.     }  
  68. }  

2、对String的扩展:
[objc] view plain copy print?
  1. import Foundation  
  2. import UIKit  
  3.   
  4. ///  
  5. /// @brief String的通用扩展方法  
  6. /// @date  2014-10-09  
  7. /// @author huangyibiao  
  8. ///  
  9. extension String {  
  10.     ///  
  11.     /// @brief 获取字符串的高度  
  12.     /// @param fontSize 字体大小  
  13.     /// @param width 限制一行显示的宽度  
  14.     /// @return 返回文本在width宽度的条件下的总高度  
  15.     ///  
  16.     func height(let fontSize: CGFloat, let width: CGFloat) ->CGFloat {  
  17.         let font = UIFont.systemFontOfSize(fontSize)  
  18.         let size = CGSizeMake(width, CGFloat.max)  
  19.           
  20.         var style = NSMutableParagraphStyle()  
  21.         style.lineBreakMode = NSLineBreakMode.ByCharWrapping  
  22.           
  23.         var attributes = [NSFontAttributeName: font, NSParagraphStyleAttributeName: style.copy()];  
  24.           
  25.         // 强转成NSString  
  26.         var text = self as NSString  
  27.         var rect = text.boundingRectWithSize(size,  
  28.             options: NSStringDrawingOptions.UsesLineFragmentOrigin,  
  29.             attributes: attributes,  
  30.             context: nil)  
  31.           
  32.         return rect.size.height  
  33.     }  
  34.       
  35.     ///  
  36.     /// @brief 把时间戳转换成“2014年12月12日 8:20:20”格式的日期字符串  
  37.     /// @param timeStamp 时间戳  
  38.     /// @return “2014年12月12日 8:20:20”格式的日期字符串  
  39.     ///  
  40.     func dateStringFromTimeStamp(let timeStamp: NSString) ->String {  
  41.         var formatter = NSDateFormatter()  
  42.         formatter.dateFormat = "yyyy年MM月dd日 HH:MM:ss"  
  43.           
  44.         let date = NSDate(timeIntervalSince1970: timeStamp.doubleValue)  
  45.         return formatter.stringFromDate(date)  
  46.     }  
  47. }  

3、UIView扩展:
[objc] view plain copy print?
  1. import Foundation  
  2. import UIKit  
  3.   
  4. ///  
  5. /// @brief UIView的扩展方法,方便工程全局使用扩展方法来创建或者使用所有继承于UIView的控件  
  6. /// @date  2014-10-09  
  7. /// @author huangyibiao  
  8. ///  
  9. extension UIView {  
  10.     ///  
  11.     /// 获取或设置origin.x  
  12.     ///  
  13.     func originX() ->CGFloat {  
  14.         return self.frame.origin.x  
  15.     }  
  16.       
  17.     func originX(let originX: CGFloat) {  
  18.         var rect = self.frame  
  19.         rect.origin.x = originX  
  20.         self.frame = rect  
  21.     }  
  22.       
  23.     ///  
  24.     /// 获取或设置origin.y  
  25.     ///  
  26.     func originY() ->CGFloat {  
  27.         return self.frame.origin.y  
  28.     }  
  29.       
  30.     func originY(let originY: CGFloat) {  
  31.         var rect = self.frame  
  32.         rect.origin.y = originY  
  33.         self.frame = rect  
  34.     }  
  35.       
  36.     ///  
  37.     /// 获取或设置origin  
  38.     ///  
  39.     func origin() ->CGPoint {  
  40.         return self.frame.origin  
  41.     }  
  42.       
  43.     func origin(let origin: CGPoint) {  
  44.         var rect = self.frame  
  45.         rect.origin = origin  
  46.         self.frame = rect  
  47.     }  
  48.       
  49.     ///  
  50.     /// 获取或设置width  
  51.     ///  
  52.     func width() ->CGFloat {  
  53.         return self.frame.size.width  
  54.     }  
  55.       
  56.     func width(let width: CGFloat) {  
  57.         var rect = self.frame  
  58.         rect.size.width = width  
  59.         self.frame = rect  
  60.     }  
  61.       
  62.     ///  
  63.     /// 获取或设置height  
  64.     ///  
  65.     func height() ->CGFloat {  
  66.         return self.frame.size.height  
  67.     }  
  68.       
  69.     func height(let height: CGFloat) {  
  70.         var rect = self.frame  
  71.         rect.size.height = height  
  72.         self.frame = rect  
  73.     }  
  74.       
  75.     ///  
  76.     /// 获取rightX  
  77.     ///  
  78.     func rightX() ->CGFloat {  
  79.         return originX() + width()  
  80.     }  
  81.       
  82.     ///  
  83.     /// 获取或设置bottomY  
  84.     ///  
  85.     func bottomY() ->CGFloat {  
  86.         return originY() + height()  
  87.     }  
  88.       
  89.     func bottomY(let bottomY: CGFloat) {  
  90.         var rect = self.frame  
  91.         rect.origin.y = bottomY - height()  
  92.         self.frame = rect  
  93.     }  
  94. }  

第二步:分析好项目的特性,这几个显示的内容格式很像,因此这里使用了同一个基类,这也需要我们先封装一个基类:

BaseRefreshController

[objc] view plain copy print?
  1. ///  
  2. /// @brief 由HotController、LatestController、TruthController继承,用于  
  3. ///        统一管理数据显示  
  4. /// @data   2014-10-09  
  5. /// @author huangyibiao  
  6. class BaseRefreshController: UIViewController,  
  7.     UITableViewDataSource,  
  8.     UITableViewDelegate,  
  9.     RefreshViewDelegate,  
  10.     JokerCellDelegate {  
  11.     var refreshView: RefreshView?  
  12.     var dataSource = NSMutableArray()  
  13.     var tableView: UITableView?  
  14.     var currentPage: Int = 1  
  15.     var cellIdentifier = "JokerCellIdentifier"  
  16.       
  17.     override func viewDidLoad() {  
  18.         super.viewDidLoad()  
  19.           
  20.         self.automaticallyAdjustsScrollViewInsets = false;  
  21.         self.view.backgroundColor = UIColor.whiteColor()  
  22.         // table view  
  23.         self.tableView = UITableView(frame: CGRectMake(064self.view.width(), kScreenHeight - 49 - 64))  
  24.         self.tableView?.dataSource = self  
  25.         self.tableView?.delegate = self  
  26.         self.tableView?.separatorStyle = UITableViewCellSeparatorStyle.None  
  27.         self.view.addSubview(self.tableView!)  
  28.         var nib = UINib(nibName: "JokerCell", bundle: nil)  
  29.         self.tableView!.registerNib(nib, forCellReuseIdentifier: cellIdentifier)  
  30.           
  31.         // refresh view  
  32.         var array = NSBundle.mainBundle().loadNibNamed("RefreshView", ownerself, options: nil) as Array  
  33.         self.refreshView = array[0] as? RefreshView  
  34.         self.tableView!.tableFooterView = self.refreshView  
  35.         self.refreshView!.delegate = self  
  36.     }  
  37.       
  38.     ///  
  39.     /// @brief 加载更多数据,此方法由子类调用  
  40.     /// @param urlString 请求地址,其中不指定page值  
  41.     func downloadData(#urlString: String) {  
  42.         let url = "\(urlString)\(self.currentPage)"  
  43.           
  44.         self.refreshView!.startLoadingMore()  
  45.           
  46.         HttpRequest.request(urlString: url) { (data) -> Void in  
  47.             if data == nil {  
  48.                 UIAlertView.show(title: "温馨提示", message"加载失败!")  
  49.             } else {  
  50.                 var itemArray = data?["items"] as NSArray  
  51.                   
  52.                 for item: AnyObject in itemArray {  
  53.                     self.dataSource.addObject(item)  
  54.                 }  
  55.                   
  56.                 self.tableView!.reloadData()  
  57.                 self.refreshView!.stopLoadingMore()  
  58.                 self.currentPage++  
  59.             }  
  60.         }  
  61.     }  
  62.       
  63.     ///  
  64.     /// UITableViewDataSource  
  65.     ///  
  66.     func numberOfSectionsInTableView(tableView: UITableView) -> Int {  
  67.         return 1  
  68.     }  
  69.       
  70.     func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {  
  71.         return self.dataSource.count  
  72.     }  
  73.       
  74.     func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
  75.         var cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier, forIndexPath: indexPath) as? JokerCell  
  76.           
  77.         cell?.delegate = self  
  78.           
  79.         if indexPath.row < self.dataSource.count {  
  80.             var dataDict = self.dataSource[indexPath.row] as NSDictionary  
  81.             cell?.data = dataDict  
  82.         }  
  83.           
  84.         return cell!  
  85.     }  
  86.       
  87.     ///  
  88.     /// UITableViewDelegate  
  89.     ///  
  90.     func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {  
  91.         var index = indexPath.row  
  92.         var data = self.dataSource[index] as NSDictionary  
  93.           
  94.         return  JokerCell.cellHeight(data)  
  95.     }  
  96.       
  97.     func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {  
  98.         var index = indexPath.row  
  99.         var data = self.dataSource[index] as NSDictionary  
  100.           
  101.         let comment = CommentController()  
  102.         comment.jokeId = data["id"] as? String  
  103.         self.navigationController?.pushViewController(comment, animatedtrue)  
  104.     }  
  105.       
  106.     ///  
  107.     /// JokerCellDelegate 代理方法实现  
  108.     ///  
  109.     func jokerCell(cell: JokerCell, didClickPicture picutre: String) {  
  110.         let browser = PhotoBrowserController()  
  111.         browser.bigImageUrlString = picutre  
  112.         self.navigationController?.pushViewController(browser, animatedtrue)  
  113.     }  
  114. }  

如果使用的cell不一样,那么子类需要重写对应的代理方法,像查看评论这个就需要重写:

CommentController:

[objc] view plain copy print?
  1. import UIKit  
  2.   
  3. ///  
  4. /// @brief  查看评价信息  
  5. /// @data   2014-10-09  
  6. /// @author huangyibiao  
  7. class CommentController: BaseRefreshController {  
  8.     // 不可空  
  9.     var jokeId: String?  
  10.       
  11.     override func viewDidLoad() {  
  12.         super.viewDidLoad()  
  13.   
  14.         self.title = "评论"  
  15.         var nib = UINib(nibName: "CommentCell", bundle: nil)  
  16.         self.tableView!.registerNib(nib, forCellReuseIdentifier"CommentCellIdentifier")  
  17.         self.tableView!.height(kScreenHeight - 64)  
  18.     }  
  19.       
  20.     override func viewWillAppear(animated: Bool) {  
  21.         super.viewWillAppear(animated)  
  22.           
  23.         if let root = self.tabBarController as? RootTabBarController {  
  24.             root.tabBarView?.hidden = true  
  25.         }  
  26.           
  27.         if self.jokeId != nil {  
  28.             downloadData(urlString: "http://m2.qiushibaike.com/article/\(self.jokeId!)/comments?count=20&page=")  
  29.         }  
  30.     }  
  31.       
  32.     override func viewWillDisappear(animated: Bool) {  
  33.         super.viewWillDisappear(animated)  
  34.           
  35.           
  36.         if let root = self.tabBarController as? RootTabBarController {  
  37.             root.tabBarView?.hidden = false  
  38.         }  
  39.     }  
  40.       
  41.     ///  
  42.     /// RefreshViewDelegate  
  43.     func refresh(refreshView: RefreshView, didClickButton button: UIButton) {  
  44.         if self.jokeId != nil {  
  45.             downloadData(urlString: "http://m2.qiushibaike.com/article/\(self.jokeId!)/comments?count=20&page=")  
  46.         }  
  47.     }  
  48.   
  49.     ///  
  50.     /// UITableViewDataSource  
  51.     ///  
  52.     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
  53.         var cell = tableView.dequeueReusableCellWithIdentifier("CommentCellIdentifier", forIndexPath: indexPath) as? CommentCell  
  54.           
  55.         if indexPath.row < self.dataSource.count {  
  56.             var dataDict = self.dataSource[indexPath.row] as NSDictionary  
  57.             cell?.data = dataDict  
  58.         }  
  59.   
  60.           
  61.         return cell!  
  62.     }  
  63.       
  64.     ///  
  65.     /// UITableViewDelegate  
  66.     ///  
  67.     override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {  
  68.         var index = indexPath.row  
  69.         var data = self.dataSource[index] as NSDictionary  
  70.           
  71.         return  CommentCell.cellHeight(data)  
  72.     }  
  73.   
  74.     override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {  
  75.         // 重写而已,以防调用父类的方法  
  76.     }  
  77. }  

这样封装好以后,下面的最新、热门、真相这三个模块就很简化了:
[objc] view plain copy print?
  1. import Foundation  
  2. import UIKit  
  3.   
  4. ///  
  5. /// @brief 热门 模块视图控制器  
  6. /// @author huangyibiao  
  7. class HotController: BaseRefreshController {  
  8.     ///  
  9.     /// @brief 生命周期函数  
  10.     ///  
  11.     override func viewDidLoad() {  
  12.         super.viewDidLoad()  
  13.           
  14.         self.title = "热门";  
  15.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/suggest?count=20&page=")  
  16.     }  
  17.       
  18.     ///  
  19.     /// RefreshViewDelegate  
  20.     func refresh(refreshView: RefreshView, didClickButton button: UIButton) {  
  21.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/suggest?count=20&page=")  
  22.     }  
  23. }  

[objc] view plain copy print?
  1. import Foundation  
  2. import UIKit  
  3.   
  4. ///  
  5. /// @brief 最新 模块视图控制器  
  6. /// @author huangyibiao  
  7. class LatestController: BaseRefreshController {  
  8.     ///  
  9.     /// @brief 生命周期函数  
  10.     ///  
  11.     override func viewDidLoad() {  
  12.         super.viewDidLoad()  
  13.           
  14.         self.title = "最新";  
  15.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/latest?count=20&page=")  
  16.     }  
  17.       
  18.     ///  
  19.     /// RefreshViewDelegate  
  20.     func refresh(refreshView: RefreshView, didClickButton button: UIButton) {  
  21.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/latest?count=20&page=")  
  22.     }  
  23. }  

[objc] view plain copy print?
  1. import Foundation  
  2. import UIKit  
  3.   
  4. ///  
  5. /// @brief 真相 模块视图控制器  
  6. /// @author huangyibiao  
  7. class TruthController: BaseRefreshController {  
  8.     ///  
  9.     /// @brief 生命周期函数  
  10.     ///  
  11.     override func viewDidLoad() {  
  12.         super.viewDidLoad()  
  13.           
  14.         self.title = "真相";  
  15.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/imgrank?count=20&page=")  
  16.     }  
  17.       
  18.     ///  
  19.     /// RefreshViewDelegate  
  20.     func refresh(refreshView: RefreshView, didClickButton button: UIButton) {  
  21.         downloadData(urlString: "http://m2.qiushibaike.com/article/list/imgrank?count=20&page=")  
  22.     }  
  23. }  

那么剩下定制cell及动态调整内容就让大家自己去查看源码了!!!!!

想要源码吗?猛击这里


阅读全文
0 0