这里面使用到第三方库,因此需要使用mix and match特性。
请大家指出其中的错误,谢谢!
rss 阅读器,很简单的代码,只是为了学习swift语言而写。
1、BaseViewController.swift:
import Foundation import UIKit // // @brief Each controller must directly or indirectly inherit from BaseViewController to // configure. // @author huangyibiao // class BaseViewController : UIViewController { // use to config ios7 or later and below ios7 var _originX: CGFloat = 0.0 var _screenHeight: CGFloat = 480.0 var _isIOS7Version = true override func viewDidLoad() { super.viewDidLoad() _isIOS7Version = UIDevice.currentDevice().systemVersion >= "7.0" // 难道坐标系统变化了?这么写法反面适配失败了 //_originX = _isIOS7Version ? 64.0 : 0.0 // 获取到的屏幕的高度怎么也只有self.view的高度,不是整个屏幕的高度了? _screenHeight = UIScreen.mainScreen().bounds.size.height } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) // config navigations let dict = [UITextAttributeTextColor: UIColor.whiteColor()] self.navigationController.navigationBar.titleTextAttributes = dict self.navigationController.navigationBar.barTintColor = UIColor.orangeColor() } }
2. need a model: FeedModel.swift
import Foundation // // @brief Feed data model, just stores datas // @author huangyibiao // class FeedModel : NSObject, NSCoding { var _name: String? var _url: String? // designated initializer init(name: String?, url: String?) { super.init() _name = name _url = url } // // The following functions are NSCoding protocol methods // // encode func encodeWithCoder(aCoder: NSCoder!) { aCoder.encodeObject(_name, forKey: "_name") aCoder.encodeObject(_url, forKey: "_url") } // decode init(coder aDecoder: NSCoder!) { _name = aDecoder.decodeObjectForKey("_name") as? String _url = aDecoder.decodeObjectForKey("_url") as? String } }
3. write an extension: Extension.swift
import Foundation import UIKit //------------------------------------------------------------------------------------------ // @brief extension UIAlertView, making easily to use // @author huangyibiao // extension UIAlertView { // @brief Shows an alert view, default showing with "cancel" and "ok" buttons // @param title: non-nil String object, shows the title // @param message: non-nil String object, shows the message // @return return non-nil UIAlertView object class func showAlertView(title: String!, message: String!) -> UIAlertView! { // in fact, swift provides UIAlerController to do the same thing // in the future, it may be replaced by UIAlertController let alert = UIAlertView() alert.title = title alert.message = message alert.addButtonWithTitle("Cancel") alert.addButtonWithTitle("Ok") alert.show() return alert } } //------------------------------------------------------------------------------------------ // @brief 扩展String结构体,追加获取feed缓存路径的方法 // @author huangyibiao // extension String { // @brief 获取缓存路径 // @param check: default value is true, meaning that needs to check is exist // @return Optional String type, if nil, means no value at all, otherwise, returns path // static func feedCachePath(isCheck check: Bool = true) -> String? { let path = NSHomeDirectory() + "/Documents/data" if check { if NSFileManager.defaultManager().fileExistsAtPath(path) { return path } } return path } }
4. need a feed manager: FeedManager.swift
import Foundation // // @brief Here FeedManager is a singleton, use to manage feed data objects // @author huangyibiao // // never use this global variable directly, because directly using it may not // has a value. // I don't know how to make it invisible outside in swift var g_sharedFeedManager: FeedManager! class FeedManager { var _dataSource = NSMutableArray() // use to store FeedModel objects // singleton class func sharedFeedManager() -> FeedManager! { if g_sharedFeedManager == nil { // no value g_sharedFeedManager = FeedManager() } return g_sharedFeedManager } // designated initializer init() { } // return the numbers of feed models func count() -> Int { return _dataSource.count } // add model func addFeedModel(feedModel: FeedModel!) { // found whether is exist for model: AnyObject in _dataSource { let feedModelObject = model as FeedModel if feedModelObject._name == feedModel._name { return } } _dataSource.addObject(feedModel) saveAllFeeds() } // remove model func removeFeedModel(atIndex index: Int) { assert(index >= 0 && index < count(), "out of range in array", file: "Extension.swift", line: 57) _dataSource.removeObjectAtIndex(index) saveAllFeeds() // update local datas } // obtain func feedModel(atIndex index: Int) -> FeedModel { assert(index >= 0 && index < count(), "out of range in array", file: "Extension.swift", line: 57) return _dataSource[index] as FeedModel } func saveAllFeeds() { let path = String.feedCachePath(isCheck: false) if path { // 若已经存在,先清除 NSFileManager.defaultManager().removeItemAtPath(path, error: nil) } // 归档 NSKeyedArchiver.archiveRootObject(_dataSource, toFile: path) } func loadAllFeeds() { let path = String.feedCachePath()// default isCheck is true if path { let feedModels = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? NSArray if feedModels { _dataSource.addObjectsFromArray(feedModels) } } } }
5.need a HttpRequest: HttpRequest.swift
import Foundation @objc protocol HttpRequestDelegate { // @brief when request finished, and will call this delegate method if comforms to // @param request: an non-nil HttpRequest object // @param downloadedData: you can do something with the parameter, // which is the data downloaded from ns service @optional func requestFinished(request: HttpRequest!, downloadedData: NSMutableData!) // @brief fail to download @optional func requestFailed(request: HttpRequest!) } // // @brief 网络请示类,这里只是简单实现,没有做缓存处理 // @author huangyibiao // class HttpRequest : NSObject, NSURLConnectionDelegate { var _delegate: HttpRequestDelegate? var _urlString: String? var _connection: NSURLConnection? var _downloadData: NSMutableData? init() { super.init() _downloadData = NSMutableData() } // begin to request data func startRequest(delegate: HttpRequestDelegate?, urlString: String!) { cancelRequest() // cancel current request before a new request _delegate = delegate _urlString = urlString // clear _downloadData _downloadData!.resetBytesInRange(NSRange(location: 0, length: _downloadData!.length)) _downloadData!.length = 0 // update length var url = NSURL(string: _urlString) var request = NSURLRequest(URL: url) _connection = NSURLConnection(request: request, delegate: self) _connection!.start() } func cancelRequest() { if _connection { _connection!.cancel() _connection = nil } } // // The following funcs are NSURLConnectionDelegate methods // download fail func connection(connection: NSURLConnection!, didFailWithError error: NSError!){ _downloadData!.resetBytesInRange(NSRange(location: 0,length: _downloadData!.length)); _downloadData!.length = 0; _delegate?.requestFailed?(self) } // receive data and append to the downloaded data func connection(connection: NSURLConnection!, didReceiveData data: NSData!){ _downloadData!.appendData(data); } // download finished func connectionDidFinishLoading(connection: NSURLConnection!){ _delegate?.requestFinished!(self, downloadedData: _downloadData!); } }
7.write a root controller: RootViewController.swift
import Foundation import UIKit class RootViewController : BaseViewController, UITableViewDelegate, UITableViewDataSource { var _tableView: UITableView? override func viewDidLoad() { self.title="网易Rss" let addButton = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 40)) addButton.setTitle("添加", forState: UIControlState.Normal) addButton.titleLabel.font = UIFont.systemFontOfSize(12); addButton.setTitleColor(UIColor.blackColor(), forState: .Normal); addButton.addTarget(self, action: "onAddButtonClicked:", forControlEvents: UIControlEvents.TouchUpInside) var addButtonItem = UIBarButtonItem(customView: addButton); super.navigationItem.rightBarButtonItem = addButtonItem; var aboutButton = UIButton(frame: CGRect(x: 0,y: 0,width: 50,height: 40)) aboutButton.setTitle("关于", forState: .Normal) aboutButton.titleLabel.font = UIFont.systemFontOfSize(12); aboutButton.setTitleColor(UIColor.blackColor(), forState: .Normal); aboutButton.addTarget(self, action: "onAboutButtonClicked:", forControlEvents: .TouchUpInside) var aboutButtonItem = UIBarButtonItem(customView: aboutButton); super.navigationItem.leftBarButtonItem = aboutButtonItem; _tableView = UITableView(frame: self.view.bounds); _tableView!.dataSource = self; _tableView!.delegate = self; self.view.addSubview(_tableView); } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) _tableView!.reloadData() } // // The following are UIButton event methods // // clicke add button func onAddButtonClicked(sender: UIButton!) { let rss = FeedRssListViewController() self.navigationController.pushViewController(rss, animated: true) } // clicked about button func onAboutButtonClicked(sender: UIButton!) { // call extension method UIAlertView.showAlertView("关于", message: "参考thilong,欢迎学习swift语言,请勿用于商业用途") } // // The following are UITableViewDataSource protocol methods // func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{ return FeedManager.sharedFeedManager().count() } func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{ let cellIdentifier = "cellIdentifier" ; var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell; if cell == nil { cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier); cell!.selectionStyle = UITableViewCellSelectionStyle.None; } let model = FeedManager.sharedFeedManager().feedModel(atIndex: indexPath.row) cell!.textLabel.text = model._name; return cell; } // // The following are UITableViewDelegate protocol methods // func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){ let model = FeedManager.sharedFeedManager().feedModel(atIndex: indexPath.row) let detail = RssDetailViewController(model) detail.hidesBottomBarWhenPushed = true self.navigationController.pushViewController(detail, animated:true); } }
8.FeedRssListViewController.swift
import Foundation import UIKit // // @brief Rss contents for selected // @author huangyibiao // class FeedRssListViewController : BaseViewController, UITableViewDelegate, UITableViewDataSource { var _tableView: UITableView? var _dataSources: Array<FeedModel> = [ FeedModel(name:"网易新闻", url:"http://news.163.com/special/00011K6L/rss_newstop.xml"), FeedModel(name:"网易科技", url:"http://tech.163.com/special/000944OI/headlines.xml"), FeedModel(name:"网易NBA", url:"http://sports.163.com/special/00051K7F/rss_sportslq.xml"), FeedModel(name:"网易英超", url:"http://sports.163.com/special/00051K7F/rss_sportsyc.xml"), FeedModel(name:"网易娱乐", url:"http://ent.163.com/special/00031K7Q/rss_toutiao.xml"), FeedModel(name:"网易电影", url:"http://ent.163.com/special/00031K7Q/rss_entmovie.xml"), FeedModel(name:"网易互联网", url:"http://tech.163.com/special/000944OI/hulianwang.xml"), FeedModel(name:"网易IT界", url:"http://tech.163.com/special/000944OI/kejiyejie.xml"), FeedModel(name:"网易汽车", url:"http://auto.163.com/special/00081K7D/rsstoutiao.xml"), FeedModel(name:"网易数码", url:"http://tech.163.com/digi/special/00161K7K/rss_digixj.xml"), FeedModel(name:"网易笔记本", url:"http://tech.163.com/digi/special/00161K7K/rss_diginote.xml"), FeedModel(name:"网易手机", url:"http://mobile.163.com/special/001144R8/mobile163_copy.xml"), FeedModel(name:"网易时尚", url:"http://lady.163.com/special/00261R8C/ladyrss1.xml"), FeedModel(name:"网易星运", url:"http://lady.163.com/special/00261R8C/ladyrss4.xml"), FeedModel(name:"网易游戏", url:"http://game.163.com/special/003144N4/rss_gametop.xml"), FeedModel(name:"网易旅游", url:"http://travel.163.com/special/00061K7R/rss_hline.xml") ] override func viewDidLoad() { super.viewDidLoad() self.title = "Feed Models" // config tableview _tableView = UITableView(frame: self.view.bounds); self.view.addSubview(_tableView); _tableView!.dataSource = self _tableView!.delegate = self; } // // @brief The following funcs are UITableViewDataSource protocol methods // func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{ return _dataSources.count; } func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{ let cellIdentifier = "cellIdentifier" ; var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell; if cell == nil { cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier); cell!.selectionStyle = UITableViewCellSelectionStyle.None; } let model = _dataSources[indexPath.row] as FeedModel cell!.textLabel.text = model._name; return cell; } // // @brief The following funcs are UITableViewDelegate protocol methods // func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){ let model = _dataSources[indexPath.row] as FeedModel; FeedManager.sharedFeedManager().addFeedModel(model) self.navigationController.popViewControllerAnimated(true); } }
9.RssDetailViewController.swift
import Foundation import UIKit // // @brief Detail rss content // @author huangyibiao // class RssDetailViewController : BaseViewController, HttpRequestDelegate { var _httpRequest: HttpRequest? var _webView: UIWebView? var _feedModel: FeedModel? init(_ feedModel: FeedModel) { super.init(nibName: nil, bundle: nil) _feedModel = feedModel } override func viewDidLoad() { super.viewDidLoad() self.title = _feedModel!._name _webView = UIWebView(frame: self.view.bounds) self.view.addSubview(_webView) _httpRequest = HttpRequest() _httpRequest!.startRequest(self, urlString: _feedModel!._url) } // // @brief The following funcs are HttpRequestDelegate protocol methods // func requestFinished(request: HttpRequest!, downloadedData: NSMutableData!) { let doc = GDataXMLDocument(data: downloadedData, options: 0, error: nil) if doc == nil { UIAlertView.showAlertView("error", message: "read xml file error") return; } var rootElement = doc.rootElement(); var titleNode = rootElement.nodeForXPath("/rss/channel/title", error: nil); var linkNode = rootElement.nodeForXPath("/rss/channel/link", error: nil); var channelNode = rootElement.nodeForXPath("/rss/channel", error: nil); var items = doc.nodesForXPath("/rss/channel/item", error:nil); var entrysBuilder : NSMutableString = NSMutableString(); var baseHTMLPath : NSString = NSBundle.mainBundle().pathForResource("FeedEntryList",ofType: "html"); var finalHTML : NSString = NSString.stringWithContentsOfFile(baseHTMLPath, encoding: NSUTF8StringEncoding,error: nil); for (var i = 0; i < items.count; i++){ var item = items[i] as GDataXMLElement; var itemTitleNode = item.elementsForName("title")[0] as GDataXMLElement; var itemDescriptionNode = item.elementsForName("description")[0] as GDataXMLElement; var itemUrlNode : GDataXMLElement = item.elementsForName("guid")[0] as GDataXMLElement; var entryBuilder : NSMutableString = NSMutableString(); entryBuilder.appendString("<a href='"); // link here var urlString : NSString! = itemUrlNode.stringValue(); var stringS = urlString.componentsSeparatedByString("/"); var finalString : NSString? = stringS[stringS.count - 1] as? NSString; if finalString && finalString!.hasSuffix(".html"){ urlString = NSString(string:"http://3g.163.com/touch/article.html?docid="); var docid : NSString = NSString(string:finalString!.stringByReplacingOccurrencesOfString(".html", withString:"")); urlString = urlString.stringByAppendingFormat("%@",docid); } entryBuilder.appendString(urlString); entryBuilder.appendString("'><div class='entry'><div class='entryTitle'>"); //title here entryBuilder.appendString(itemTitleNode.stringValue()); entryBuilder.appendString("</div><div class='entryDescription'>"); //description here var description : NSString = itemDescriptionNode.stringValue(); entryBuilder.appendString(description); entryBuilder.appendString("</div></div></a>"); entrysBuilder.appendString(entryBuilder); } finalHTML = finalHTML.stringByReplacingOccurrencesOfString("[[ENTRYLIST]]", withString: entrysBuilder); _webView!.loadHTMLString(finalHTML,baseURL: nil); } // fail to download func requestFailed(request: HttpRequest!) { println("request failed" + _feedModel!._url!) } }
#import "GDataXMLNode.h" #import "JSONKit.h"
11.源代码: