首页 文章 精选 留言 我的

精选列表

搜索[Swift开发],共10000篇文章
优秀的个人博客,低调大师

Swift: 用Alamofire做http请求,用ObjectMapper解析JSON

跟不上时代的人突然间走在了时代的前列,果然有别样的风景。首先鄙视一下AFNetworking。这个东西实在太难用了。不想封装都不行,要不写一大堆代码。 1 2 3 4 5 6 7 8 9 10 11 NSURL *URL = [ NSURL URLWithString:@ "http://example.com/resources/123.json" ]; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; [manager GET:URL.absoluteString parameters: nil progress: nil success:^( NSURLSessionTask *task, id responseObject) { NSLog (@ "JSON: %@" , responseObject); } failure:^( NSURLSessionTask *operation, NSError *error) { NSLog (@ "Error: %@" , error); } ]; Http请求 但是用alamofire就简单的很多了,如: 1 2 3 4 Alamofire . request (. GET , "https://httpbin.org/get" , parameters : [ "foo" : "bar" ]) . response { request , response , data , error in print ( response ) } 都是一个GET请求,但是可见的是Alamofire代码量少很多。这也是和AFNetworking3.x比较了,如果你用的是AFNetworking2.x的话代码量的对比更加明显。对于程序员来说调用方法的API简单方便就是用户体验。Developer们也是需要满足UE的需要的。 下面开始进入正题。下面用请求微博的time line来做栗子。 1 2 3 4 5 6 parameters = [ "access_token" : weiboUserInfo . accessToken ?? "" , "source" : ConstantUtil . WEIBO_APPKEY ] //1 Alamofire . request (. GET , "https://api.weibo.com/2/statuses/friends_timeline.json" // 2 , parameters : parameters , encoding : . URL , headers : nil ) . responseString ( completionHandler : { response in print ( "response:- \( response )" ) //3 }) 这里用Alamofire请求微博的time line。 1. 请求微博的time line就需要SSO或者网页方式登录微博之后从服务器返回的access_token。另外一个必须的输入参数就是添加微博应用的时候生成的app key。 2.https://api.weibo.com/2/statuses/friends_timeline.json请求的url。 这个url返回的就是你follow的好友的微博。就是你一打开微博客户端看到的那些。 3. 我们知道Alamofire可以把请求返回的数据转化为JSON、String和NSData。如果是作为JSON来处理,也就是使用了responseJSON方法的话,JSON数据会被自动转化为NSDictionary。我们后面需要用到字符串来实现json字符串和Model对象的匹配,所以我们用方法responseString。 如果一切设置正确,你会看到这样的结果: 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 { "statuses" : [ { "created_at" : "Tue May 31 17:46:55 +0800 2011" , "id" : 11488058246 , "text" : "求关注。" , "source" : "<a href=" http : //weibo.com" rel="nofollow">新浪微博</a>", "favorited" : false , "truncated" : false , "in_reply_to_status_id" : "" , "in_reply_to_user_id" : "" , "in_reply_to_screen_name" : "" , "geo" : null , "mid" : "5612814510546515491" , "reposts_count" : 8 , "comments_count" : 9 , "annotations" : [], "user" : { "id" : 1404376560 , "screen_name" : "zaku" , "name" : "zaku" , "province" : "11" , "city" : "5" , "location" : "北京 朝阳区" , "description" : "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。" , "url" : "http://blog.sina.com.cn/zaku" , "profile_image_url" : "http://tp1.sinaimg.cn/1404376560/50/0/1" , "domain" : "zaku" , "gender" : "m" , "followers_count" : 1204 , ... } }, ... ], "ad" : [ { "id" : 3366614911586452 , "mark" : "AB21321XDFJJK" }, ... ], "previous_cursor" : 0 , // 暂时不支持 "next_cursor" : 11488013766 , // 暂时不支持 "total_number" : 81655 } 以上是微博给出来的例子的一部分,我们来看看我们需要什么。我们需要一部分文字和一部分的图片。之后要显示的内容主要就是文字或者图片。 解析 我们用ObjectMapper解析json。ObjectMapper是一个双向的转化工具。可以把json字符串转化成model也可以把model转化成json字符串。 安装ObjectMapper: pod 'ObjectMapper', '~> 1.1' ObjectMapper对于json的解析都是从外往内进行的,这个层层解析的过程中一般没有特殊指定的话每一层都不能少(可以通过制定解析路径减少)。每一层都需要配备一个实体类。 最外面的一层是: 1 2 3 4 5 6 7 8 { "statuses" : [ ... ], "previous_cursor" : 0 , "next_cursor" : 11488013766 , "total_number" : 81655 } 所以对应的model定义是这样的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import ObjectMapper class BaseModel : Mappable { // 1 var previousCursor : Int ? var nextCursor : Int ? //var statuses var totalNumber : Int ? required init ?( _ map : Map ) { // 2 } func mapping ( map : Map ) { // 3 previousCursor < - map [ "previous_cursor" ] nextCursor < - map [ "next_cursor" ] //hasVisible <- map["hasvisible"] statuses < - map [ "..." ] // 4 totalNumber < - map [ "total_number" ] } } 最重要的是先import ObjectMapper。没有这个什么都干不了。 1.BaseModel类需要实现Mappable接口。后面就是这个protocol的实现。 2. 返回可能为空对象的初始化方法,法暂时用不到。 3. 这个方法最关键了。在这个方法里指定json的值对应的是model里的哪个属性。这部分功能可以自动实现,哪位有心人可以fork出来写一个,也方便大家使用。 4. 请看下文。 在深入一层 上问的标签4的内容我们在这里详细介绍。我们要展示的内容都是在statuses下的。那么我们应该如何处理这部分的内容呢?statuses的json格式是这样的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "statuses" : [ { "created_at" : "Tue May 31 17:46:55 +0800 2011" , "id" : 11488058246 , "text" : "求关注。" , "source" : "<a href=" http : //weibo.com" rel="nofollow">新浪微博</a>", "favorited" : false , "truncated" : false , "in_reply_to_status_id" : "" , "in_reply_to_user_id" : "" , "in_reply_to_screen_name" : "" , "geo" : null , ... } ], } 可以有两个方式来处理深层的json数据。一个是在mapping方法里指定json数据和属性的对应关系。比如在BaseMode类中映射statuses中的text可以这样写: 1 2 3 4 5 6 7 8 9 10 class BaseModel { var text : String ? required init ?( _ map : Map ) { } func mapping ( map : Map ) { self . text < - map [ "statuses.text" ] } } 但是这样是错误的!因为statuses是一个数组,而不是一个对象。只有statuses对应的是一个对象的时候才适用于这个情况。 对上面的代码进行修改,让其适用于数据的情况。 1 2 3 4 5 6 7 8 9 10 class BaseModel { var text : String ? required init ?( _ map : Map ) { } func mapping ( map : Map ) { self . text < - map [ "status.0.text" ] } } self.text <- map["statuses.0.text"]中间的数字零说明text属性对应的是json中的statuses数组的第一个元素的text的值。但是在statuses下会有很多个json对象,一个一个的挨个解析的方式显然是不适合的。更不用说这才两层,有多少奇葩的API返回的是三层甚至更多的? 那么就剩下最后的一种方法了。内层json的model类继承外层的json的model类。按照这个方法那么我们为statuses对应的json对象定义一个model类为StatusModel。由于StatusModel对应的是内层的json对象,那么就需要继承外层的json对象的类,也就是BaseModel。刚开始就命名为BaseModel应该是已经露馅了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class StatusModel : BaseModel { // 1 var statusId : String ? var thumbnailPic : String ? var bmiddlePic : String ? var originalPic : String ? var weiboText : String ? var user : WBUserModel ? required init ?( _ map : Map ) { super . init ( map ) // 2 } override func mapping ( map : Map ) { super . mapping ( map ) // 2 statusId < - map [ "id" ] thumbnailPic < - map [ "thumbnail_pic" ] bmiddlePic < - map [ "bmiddle_pic" ] originalPic < - map [ "original_pic" ] weiboText < - map [ "text" ] } } 也就是我们说的json对象嵌套时的model类的继承关系。 在这种继承关系中需要十分注意的是。在Mappable协议的方法的调用中需要先调用基类的对应方法,super.init(map)和super.mapping(map)。至于说mapping方法的映射关系,每个json对象对应的model类只管这一个对象的就可以。 那么在最外层的BaseModel类中的statuses属性也就可以给出一个正确的完整的写法了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class BaseModel : Mappable { var previousCursor : Int ? var nextCursor : Int ? var hasVisible : Bool ? var statuses : [ StatusModel ]? // 1 var totalNumber : Int ? required init ?( _ map : Map ) { } func mapping ( map : Map ) { previousCursor < - map [ "previous_cursor" ] nextCursor < - map [ "next_cursor" ] hasVisible < - map [ "hasvisible" ] statuses < - map [ "statuses" ] // 2 totalNumber < - map [ "total_number" ] } } 内层的statuses数组直接调用内层json对象对应的model类的数组,也即是var statuses: [StatusModel]?。 在mapping方法中指定属性和json对象的关系,这里是statuses <- map["statuses"]。 这样ObjectMapper就知道应该如何解析json字符串到对应的类对象中了。除了上面提到的,ObjectMapper还有很多其他的功能。如果需要了解更多可以查看官方文档。 那么从http请求,到返回数据,到解析json串的一系列动作就可以完整的联结起来了。最开始介绍使用Alamofire请求并成功返回之后,我们只是把字符串打印了出来。现在可以调用map方法来匹配json串和我们定义好的model类了。 1 2 3 4 5 6 7 8 9 10 11 12 parameters = [ "access_token" : weiboUserInfo . accessToken ?? "" , "source" : ConstantUtil . WEIBO_APPKEY ] Alamofire . request (. GET , "https://api.weibo.com/2/statuses/friends_timeline.json" , parameters : parameters , encoding : . URL , headers : nil ) . responseString ( completionHandler : { response in print ( "response:- \( response )" ) let statuses = Mapper < BaseModel > (). map ( response . result . value ) // 1 print ( "total number: \( statuses !. totalNumber )" ) if let timeLine = statuses where timeLine . totalNumber > 0 { // 2 self . timeLineStatus = timeLine . statuses self . collectionView ?. reloadData () } }) 使用Mapper<BaseModel>().map(response.result.value)方法来映射json串。这里需要分开来看。Mapper<BaseModel>()初始化了一个Mapper对象。Mapper是一个泛型,类型参数就是我们定义的最外层的json对象对应的model类BaseModel。之后我们调用了这个初始化好的Mapper对象的map方法。这个方法的参数就是一个json串,也就是字符串类型的,但是这个字符串必须是json格式的。response.result.value取出了http请求之后返回的json串。 map方法返回的是可空类型的。所以需要用if-let的方式检查一下返回的值是否可用。在可用的情况下用where语句判断返回的timeLine总数是否大于零。大于零才是有意义的,才刷新collection view。 to be continued… 本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5170372.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

【swift学习笔记】三.使用xib自定义UITableViewCell

使用xib自定义tableviewCell看一下效果图 1.自定义列 新建一个xib文件 carTblCell,拖放一个UITableViewCell,再拖放一个图片和一个文本框到tableviewcell上 并给我们的xib一个标识 为了学习,我这里的xib和后台的class是分开建的。我们再建一个cocoa touch class文件名称为CarCellTableViewCell继承自UITableViewCell 并把我们的xib和新建的CarCellTableViewCell建立联接 在CarCellTableViewCell里建立和xib的图片和文本框的输出 import UIKit class CarCellTableViewCell: UITableViewCell { @IBOutlet weak var cellImg: UIImageView! @IBOutlet weak var lbCell: UILabel! override func awakeFromNib() { super.awakeFromNib() // Initialization code cellImg.layer.borderWidth = 1 cellImg.layer.masksToBounds = true //cellImg.layer.cornerRadius = 31 } override func setSelected(selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } } 2.关联cell和tableview 1. 在main.storyboard上拖放一个uitableview,并在后台代码建立输出联接 1.在load事件里注册xib 2.在tableveiw的方法里得到当前的列,指定数据源。 import UIKit class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet var tableView: UITableView! var tableData: [String] = ["BMW", "Ferrari", "Lambo"] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let cellNib = UINib(nibName: "carTblCell", bundle: nil) tableView.registerNib(cellNib, forCellReuseIdentifier: "cell") } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell: CarCellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! CarCellTableViewCell cell.lbCell.text = tableData[indexPath.row] cell.cellImg.image = UIImage(named: tableData[indexPath.row]) return cell } func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { print("\(indexPath.row)") } func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 70 } } 本文转自lpxxn博客园博客,原文链接: http://www.cnblogs.com/li-peng/p/5543415.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

【swift学习笔记】五.使用枚举优雅的管理Segue

在做页面转跳的时候,我们要给Segue命名,如果Segue多了,管理他们就是一个恶梦。我们可以枚举更优雅的管理这些Segue。 1.我们先来建立一个protocol,他的功能就是让实现类实现一个SegueIdentifier别名,这个SegueIdentifier必需为RawRepresentable类型,在后边我们就会用 protocol SegueHandlerType { associatedtype SegueIdentifier: RawRepresentable } 2.我们要对上边的protocol扩展,并且实现protocol必须为UIViewControl,SegueIdentifier的原值要为String类型 这个扩展有两个方法一个是用要实现的别名来调用执行Segue方法performSegueWithIdentifier。另一个方法segueIdentifierForSegue通过Segue的identifier来得到SegueIdentifier. extension SegueHandlerType where Self: UIViewController, SegueIdentifier.RawValue == String { func performSegueWithIdentifier(segueIdentifier: SegueIdentifier, sender: AnyObject) { performSegueWithIdentifier(segueIdentifier.rawValue, sender: sender) } func segueIdentifierForSegue(segue: UIStoryboardSegue) -> SegueIdentifier { guard let identifier = segue.identifier, segueIdentifier = SegueIdentifier(rawValue: identifier) else { fatalError("invalid segue identifier \(segue.identifier)") } return segueIdentifier } } 3.用ViewController实现SegueHandlerType。再用一个枚举来实现SegueIdentifier并且为String,里面有两个case这两个就要我们要打开的新窗体的名称。 下边有两个按钮Action是用SegueIdentifier枚举来打开相应的窗体。 class ViewController: UIViewController, SegueHandlerType { enum SegueIdentifier: String { case ShowView1 case ShowView2 } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func openView1(sender: AnyObject) { performSegueWithIdentifier(.ShowView1, sender: self) } @IBAction func openView2(sender: AnyObject) { performSegueWithIdentifier(.ShowView2, sender: self) } } 4.新建两个新窗体,分别用Segue联接这两个窗体。这两个Segue分别命名为我们的枚举类型的两个值:“ShowView1” “ShowView2”。再来两个Button连接后台的两个窗体的Action 源代码:segueTypeDemo.zip 本文转自lpxxn博客园博客,原文链接:http://www.cnblogs.com/li-peng/p/5561149.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

iOS解决App启动时闪屏问题(swift)

创建一个工具类MTAppHelper import UIKit fileprivate let isIphone4 = Int(kScreenH) == 480 fileprivate let isIphone5 = Int(kScreenH) == 568 fileprivate let isIphone6 = Int(kScreenH) == 667 fileprivate let isIphonePlus = Int(kScreenH) == 736 /// 工具类 class MTAppHelper { /// 根据机型获取指定机型的图片 /// /// - returns: 指定手机类型的图片 static func fetchLaunchImage() -> UIImage? { var imageName: String! if isIphonePlus { imageName = "Default-736h" } else if isIphone6 { imageName = "Default-667h" } else if isIphone5 { imageName = "Default-568h@2x~iphone" } else { imageName = "Default@2x~iphone" } return UIImage(named: imageName) } } 加载UI时调用方法 showSplashScreenImage(); /// 闪屏图片视图 lazy var splashImageView: UIImageView = UIImageView(frame: kScreenBounds) /// 展示闪屏图片 private func showSplashScreenImage() { //使用工具类方法 splashImageView.image = ZSAppHelper.fetchLaunchImage() UIApplication.shared.keyWindow?.addSubview(splashImageView) }

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册