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

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

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

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

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

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

1、网络请求类:HttpRequest.swift

import Foundation

///
/// @brief 网络请求相关类
/// @date  2014-10-09
/// @author huangyibiao
///
class HttpRequest: NSObject {
    override init() {
        super.init()
    }

    ///
    /// @brief 把二进制数据转换成JSON格式的数据
    /// @param data NSDictionary?类型,网络请求返回来的二进制数据
    /// @return 如果解析成功,返回NSDictionary?类型的JSON数据;如果解析失败,会返回nil
    ///
    class func parseJSONData(data: AnyObject?) ->NSDictionary? {
        if let downloadData: NSData = data as? NSData {
            var jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(downloadData,
                options: NSJSONReadingOptions.MutableContainers,
                error: nil) as? NSDictionary

            return jsonData as? NSDictionary
        }

        // 当解析失败时,会返回nil
        return nil
    }

    ///
    /// @brief 异步网络请求方法,需要请求地址参数及回调
    /// @param urlString 请求地址
    /// @param completion 请求完成或者请求失败的回调
    /// @return 请求成功时,会返回NSDictionary?的字典格式的数据,如果请求失败,会返回nil
    ///
    class func request(#urlString: String?, completion: (data: NSDictionary?) ->Void) {
        if urlString == nil {
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                println("urlstring 为空")
                // 请求出现,则返回nil对象表示
                completion(data: nil)
            })
            return
        }

        let url = NSURL.URLWithString(urlString!)
        let request = NSURLRequest(URL: url)

        let queue = NSOperationQueue()
        NSURLConnection.sendAsynchronousRequest(request, queue: queue) {
            (response, data, error) -> Void in
            if error != nil {
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    println(error)
                    // 请求出现,则返回nil对象表示
                    completion(data: nil)
                })
            } else { // 请求成功,则返回正确的数据
                let parseData = self.parseJSONData(data)

                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    completion(data: parseData)
                })
            }
        }
    }
}

2、对String的扩展:

import Foundation
import UIKit

///
/// @brief String的通用扩展方法
/// @date  2014-10-09
/// @author huangyibiao
///
extension String {
    ///
    /// @brief 获取字符串的高度
    /// @param fontSize 字体大小
    /// @param width 限制一行显示的宽度
    /// @return 返回文本在width宽度的条件下的总高度
    ///
    func height(let fontSize: CGFloat, let width: CGFloat) ->CGFloat {
        let font = UIFont.systemFontOfSize(fontSize)
        let size = CGSizeMake(width, CGFloat.max)

        var style = NSMutableParagraphStyle()
        style.lineBreakMode = NSLineBreakMode.ByCharWrapping

        var attributes = [NSFontAttributeName: font, NSParagraphStyleAttributeName: style.copy()];

        // 强转成NSString
        var text = self as NSString
        var rect = text.boundingRectWithSize(size,
            options: NSStringDrawingOptions.UsesLineFragmentOrigin,
            attributes: attributes,
            context: nil)

        return rect.size.height
    }

    ///
    /// @brief 把时间戳转换成“2014年12月12日 8:20:20”格式的日期字符串
    /// @param timeStamp 时间戳
    /// @return “2014年12月12日 8:20:20”格式的日期字符串
    ///
    func dateStringFromTimeStamp(let timeStamp: NSString) ->String {
        var formatter = NSDateFormatter()
        formatter.dateFormat = "yyyy年MM月dd日 HH:MM:ss"

        let date = NSDate(timeIntervalSince1970: timeStamp.doubleValue)
        return formatter.stringFromDate(date)
    }
}

3、UIView扩展:

import Foundation
import UIKit

///
/// @brief UIView的扩展方法,方便工程全局使用扩展方法来创建或者使用所有继承于UIView的控件
/// @date  2014-10-09
/// @author huangyibiao
///
extension UIView {
    ///
    /// 获取或设置origin.x
    ///
    func originX() ->CGFloat {
        return self.frame.origin.x
    }

    func originX(let originX: CGFloat) {
        var rect = self.frame
        rect.origin.x = originX
        self.frame = rect
    }

    ///
    /// 获取或设置origin.y
    ///
    func originY() ->CGFloat {
        return self.frame.origin.y
    }

    func originY(let originY: CGFloat) {
        var rect = self.frame
        rect.origin.y = originY
        self.frame = rect
    }

    ///
    /// 获取或设置origin
    ///
    func origin() ->CGPoint {
        return self.frame.origin
    }

    func origin(let origin: CGPoint) {
        var rect = self.frame
        rect.origin = origin
        self.frame = rect
    }

    ///
    /// 获取或设置width
    ///
    func width() ->CGFloat {
        return self.frame.size.width
    }

    func width(let width: CGFloat) {
        var rect = self.frame
        rect.size.width = width
        self.frame = rect
    }

    ///
    /// 获取或设置height
    ///
    func height() ->CGFloat {
        return self.frame.size.height
    }

    func height(let height: CGFloat) {
        var rect = self.frame
        rect.size.height = height
        self.frame = rect
    }

    ///
    /// 获取rightX
    ///
    func rightX() ->CGFloat {
        return originX() + width()
    }

    ///
    /// 获取或设置bottomY
    ///
    func bottomY() ->CGFloat {
        return originY() + height()
    }

    func bottomY(let bottomY: CGFloat) {
        var rect = self.frame
        rect.origin.y = bottomY - height()
        self.frame = rect
    }
}

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

BaseRefreshController

///
/// @brief 由HotController、LatestController、TruthController继承,用于
///        统一管理数据显示
/// @data   2014-10-09
/// @author huangyibiao
class BaseRefreshController: UIViewController,
    UITableViewDataSource,
    UITableViewDelegate,
    RefreshViewDelegate,
    JokerCellDelegate {
    var refreshView: RefreshView?
    var dataSource = NSMutableArray()
    var tableView: UITableView?
    var currentPage: Int = 1
    var cellIdentifier = "JokerCellIdentifier"

    override func viewDidLoad() {
        super.viewDidLoad()

        self.automaticallyAdjustsScrollViewInsets = false;
        self.view.backgroundColor = UIColor.whiteColor()
        // table view
        self.tableView = UITableView(frame: CGRectMake(0, 64, self.view.width(), kScreenHeight - 49 - 64))
        self.tableView?.dataSource = self
        self.tableView?.delegate = self
        self.tableView?.separatorStyle = UITableViewCellSeparatorStyle.None
        self.view.addSubview(self.tableView!)
        var nib = UINib(nibName: "JokerCell", bundle: nil)
        self.tableView!.registerNib(nib, forCellReuseIdentifier: cellIdentifier)

        // refresh view
        var array = NSBundle.mainBundle().loadNibNamed("RefreshView", owner: self, options: nil) as Array
        self.refreshView = array[0] as? RefreshView
        self.tableView!.tableFooterView = self.refreshView
        self.refreshView!.delegate = self
    }

    ///
    /// @brief 加载更多数据,此方法由子类调用
    /// @param urlString 请求地址,其中不指定page值
    func downloadData(#urlString: String) {
        let url = "\(urlString)\(self.currentPage)"

        self.refreshView!.startLoadingMore()

        HttpRequest.request(urlString: url) { (data) -> Void in
            if data == nil {
                UIAlertView.show(title: "温馨提示", message: "加载失败!")
            } else {
                var itemArray = data?["items"] as NSArray

                for item: AnyObject in itemArray {
                    self.dataSource.addObject(item)
                }

                self.tableView!.reloadData()
                self.refreshView!.stopLoadingMore()
                self.currentPage++
            }
        }
    }

    ///
    /// UITableViewDataSource
    ///
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataSource.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier, forIndexPath: indexPath) as? JokerCell

        cell?.delegate = self

        if indexPath.row < self.dataSource.count {
            var dataDict = self.dataSource[indexPath.row] as NSDictionary
            cell?.data = dataDict
        }

        return cell!
    }

    ///
    /// UITableViewDelegate
    ///
    func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        var index = indexPath.row
        var data = self.dataSource[index] as NSDictionary

        return  JokerCell.cellHeight(data)
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        var index = indexPath.row
        var data = self.dataSource[index] as NSDictionary

        let comment = CommentController()
        comment.jokeId = data["id"] as? String
        self.navigationController?.pushViewController(comment, animated: true)
    }

    ///
    /// JokerCellDelegate 代理方法实现
    ///
    func jokerCell(cell: JokerCell, didClickPicture picutre: String) {
        let browser = PhotoBrowserController()
        browser.bigImageUrlString = picutre
        self.navigationController?.pushViewController(browser, animated: true)
    }
}

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

CommentController:

import UIKit

///
/// @brief  查看评价信息
/// @data   2014-10-09
/// @author huangyibiao
class CommentController: BaseRefreshController {
    // 不可空
    var jokeId: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "评论"
        var nib = UINib(nibName: "CommentCell", bundle: nil)
        self.tableView!.registerNib(nib, forCellReuseIdentifier: "CommentCellIdentifier")
        self.tableView!.height(kScreenHeight - 64)
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        if let root = self.tabBarController as? RootTabBarController {
            root.tabBarView?.hidden = true
        }

        if self.jokeId != nil {
            downloadData(urlString: "http://m2.qiushibaike.com/article/\(self.jokeId!)/comments?count=20&page=")
        }
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        if let root = self.tabBarController as? RootTabBarController {
            root.tabBarView?.hidden = false
        }
    }

    ///
    /// RefreshViewDelegate
    func refresh(refreshView: RefreshView, didClickButton button: UIButton) {
        if self.jokeId != nil {
            downloadData(urlString: "http://m2.qiushibaike.com/article/\(self.jokeId!)/comments?count=20&page=")
        }
    }

    ///
    /// UITableViewDataSource
    ///
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("CommentCellIdentifier", forIndexPath: indexPath) as? CommentCell

        if indexPath.row < self.dataSource.count {
            var dataDict = self.dataSource[indexPath.row] as NSDictionary
            cell?.data = dataDict
        }

        return cell!
    }

    ///
    /// UITableViewDelegate
    ///
    override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        var index = indexPath.row
        var data = self.dataSource[index] as NSDictionary

        return  CommentCell.cellHeight(data)
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        // 重写而已,以防调用父类的方法
    }
}

这样封装好以后,下面的最新、热门、真相这三个模块就很简化了:

import Foundation
import UIKit

///
/// @brief 热门 模块视图控制器
/// @author huangyibiao
class HotController: BaseRefreshController {
    ///
    /// @brief 生命周期函数
    ///
    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "热门";
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/suggest?count=20&page=")
    }

    ///
    /// RefreshViewDelegate
    func refresh(refreshView: RefreshView, didClickButton button: UIButton) {
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/suggest?count=20&page=")
    }
}

import Foundation
import UIKit

///
/// @brief 最新 模块视图控制器
/// @author huangyibiao
class LatestController: BaseRefreshController {
    ///
    /// @brief 生命周期函数
    ///
    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "最新";
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/latest?count=20&page=")
    }

    ///
    /// RefreshViewDelegate
    func refresh(refreshView: RefreshView, didClickButton button: UIButton) {
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/latest?count=20&page=")
    }
}

import Foundation
import UIKit

///
/// @brief 真相 模块视图控制器
/// @author huangyibiao
class TruthController: BaseRefreshController {
    ///
    /// @brief 生命周期函数
    ///
    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "真相";
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/imgrank?count=20&page=")
    }

    ///
    /// RefreshViewDelegate
    func refresh(refreshView: RefreshView, didClickButton button: UIButton) {
        downloadData(urlString: "http://m2.qiushibaike.com/article/list/imgrank?count=20&page=")
    }
}

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

想要源码吗?猛击这里

时间: 2025-01-19 02:57:07

Swift实现糗事百科Demo(实战项目)的相关文章

【IOS】高仿糗事百科客户端(基本实现了除注册,评论之外的功能)

 一直挺喜欢看糗事百科的,所以最近写了一个 iphone版的客户端,自己用着玩,图片资源是扒的官方的.基本实现了除了登陆评论之外的功能,接口是找到了,但是用不来这个接口...囧.. 下面是最终实现的demo的截图:     下面把源码和大家分享一下: http://download.csdn.net/detail/toss156/4381732 仅供学习交流使用,禁止商用,素材版权归糗事百科所有..

用Python编写网络爬虫(八):糗事百科的网络爬虫(v0.2)源码及解析

项目内容: 用Python写的糗事百科的网络爬虫. 使用方法: 新建一个Bug.py文件,然后将代码复制到里面后,双击运行. 程序功能: 在命令提示行中浏览糗事百科. 原理解释: 首先,先浏览一下糗事百科的主页:http://www.qiushibaike.com/hot/page/1 可以看出来,链接中page/后面的数字就是对应的页码,记住这一点为以后的编写做准备. 然后,右击查看页面源码: 观察发现,每一个段子都用center标记,其中class必为content,title是发帖时间,我

浅析垂直网站糗事百科的成功之道

中介交易 SEO诊断 淘宝客 云主机 技术大厅 作为一个资深的"糗友",每天除了工作,必去的网站就是糗事百科了.这也是唯一一个就算是出门在外不方便上网的时候也会拿手机上去看一看的网站,这是一个让更多的人一个人对着手机傻笑的网站,也是一个拿别人的糗事来寻开心的网站----虽然拿别人的糗事来寻开心似乎不是一件什么光彩的事情,但是糗百的文化 却是大家都乐于分享自己所遇到的糗事,并以越糗被顶得越高为荣. 就是这样一个网站,潜藏的商机却不容忽视.根据阿里妈妈每周发布的橱窗推广周榜发布排行榜top

Python 制作糗事百科爬虫实例_python

早上起来闲来无事做,莫名其妙的就弹出了糗事百科的段子,转念一想既然你送上门来,那我就写个爬虫到你网站上爬一爬吧,一来当做练练手,二来也算找点乐子. 其实这两天也正在接触数据库的内容,可以将爬取下来的数据保存在数据库中,以待以后的利用.好了,废话不多说了,先来看看程序爬取的数据结果 值得一提的是,我在程序中想一下子爬取糗事百科 30 页的内容,但是出现了连接错误,当我把页数降到 20 页的时候,程序就可以正常的跑起来了,不知道是什么原因,渴望知道的大神可以告诉我一声,感激不尽. 程序非常简单,直接

零基础写python爬虫之抓取糗事百科代码分享_python

项目内容: 用Python写的糗事百科的网络爬虫. 使用方法: 新建一个Bug.py文件,然后将代码复制到里面后,双击运行. 程序功能: 在命令提示行中浏览糗事百科. 原理解释: 首先,先浏览一下糗事百科的主页:http://www.qiushibaike.com/hot/page/1 可以看出来,链接中page/后面的数字就是对应的页码,记住这一点为以后的编写做准备. 然后,右击查看页面源码: 观察发现,每一个段子都用div标记,其中class必为content,title是发帖时间,我们只需

万万达小丹浅谈——从糗事百科看到的UEO 模式

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 毫不夸张的说,现在糗友应该已经遍布中国了,糗事百科的成功浅而易见.万万达小丹从糗事百科不仅找到了乐趣,更是看到了UEO的发展前景. 糗事百科是典型的UEO模式,也就是用户体验优化.UEO也是网络营销的一种手段,但是现在大部分的人并没有意识到用户体验优化的帮助.小丹以为,现在大部分的优化仍旧是以SEO为主.但是据现在的市场前景和搜索引擎不断的发

下载糗事百科的内容_python版_python

复制代码 代码如下: #coding:utf-8 import urllib.request import xml.dom.minidom import sqlite3 import threading import time class logger(object): def log(self,*msg): for i in msg: print(i) Log = logger() Log.log('测试下') class downloader(object): def __init__(se

糗事百科的运营哲学 从无名小站成长为日PV过千万

中介交易 SEO诊断 淘宝客 云主机 技术大厅 [核心提示] 糗百从2005年成立,至今已经发展了7年多,积累了大量的优质内容和用户,从一个默默无闻的小站成长为日PV过千万,成为中文社区有独特文化的娱乐网站,糗百有自己独特的运营策略和手段. 糗事百科是以网友真实糗事为主题的笑话网站,话题轻松休闲,目前在年轻人中十分流行.在糗事百科中可以查看他人发布的糗事并与网友分享自己亲身经历或听说到的油菜.杯具.草蛋.有意思的.不顺心的等各类情形的生活糗事,并且可以左右排名. 糗事百科拥有极高的人气和访问量,

[Python]爬取糗事百科

# coding=utf-8 import urllib2 import urllib import re class QiuShi: def _init_(self): self.page = 1 # 从网页获取糗事 def GetQiuShis(self,page): #网址 url = "http://www.qiushibaike.com/hot/page/"+page #伪装浏览器 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5