上一篇小说源代码,commandLineTools的装置也很简短

截图

上一篇小说swift实现叁个与智能机器人聊天的app(一)完成了聊天appUI的输入框部分,接下去作者会教咱们如何贯彻聊天窗口部分,也正是下图的第②片段:

您在本体系小说少校会学到

  • 怎样设置和利用cocoapods来集成第③方库
  • 何以搭建三个像样于iOS短信app的界面,以及接纳Snap基特来用代码设置autolayout
  • 怎么行使Parse云服务平台存款和储蓄和一道聊天新闻,学习相应地数据库知识
  • 何以运用Parse的长途推送功能
  • 什么样使用Alamofire完成与智能机器人聊天功效
    先导项目下载地址:
    百度网盘下载地址

UI结构

布置开端项目

1.cocoapods的安装
cocoapods的设置是通过ruby,幸运的是Mac电脑都以私下认可安装ruby的,所以安装ruby的进程就节约了,唯一的前提正是设置Xcode的CommandLineTools。
commandLineTools的设置也很粗大略,只要在终点输入以下命令:

$ xcode-select --install

万一的确尚未设置commandLineTools会提示您要安装它,点设置就足以开头下载,然后等待下载完结后装置即可
上边开首安装cocoapods,本来只供给简单地在极端输入以下命令即可:

$ sudo gem install cocoapods

而是出于中国的网络是”自由的”。。咳咳,所以呢,你要改变gem的暗中同意下载源:

$ gem sources -a https://ruby.taobao.org

看到以下结果

http://ruby.taobao.org added to sources

将天猫商城的ruby源加入进来,看来Tmall也不只是卖东西哈,也是对开发者做了一些进献的~
剔除原来的下载源:

$ gem sources -r https://rubygems.org/

见状以下结果就表明已经成功

https://rubygems.org/ removed from sources

接下来呢,就能够热情洋溢地设置上cocoapods了!

$ sudo gem install cocoapods

Password:(你的管理员密码,这里不会显示出来)
Fetching: cocoapods-core-0.38.2.gem (100%)
Successfully installed cocoapods-core-0.38.2
Fetching: claide-0.9.1.gem (100%)
Successfully installed claide-0.9.1
Fetching: xcodeproj-0.26.3.gem (100%)
Successfully installed xcodeproj-0.26.3
Fetching: cocoapods-downloader-0.9.3.gem (100%)
Successfully installed cocoapods-downloader-0.9.3
Fetching: cocoapods-stats-0.5.3.gem (100%)
Successfully installed cocoapods-stats-0.5.3
Fetching: cocoapods-try-0.4.5.gem (100%)
Successfully installed cocoapods-try-0.4.5
Fetching: cocoapods-trunk-0.6.4.gem (100%)
Successfully installed cocoapods-trunk-0.6.4
Fetching: molinillo-0.3.1.gem (100%)
Successfully installed molinillo-0.3.1
Fetching: cocoapods-0.38.2.gem (100%)
Successfully installed cocoapods-0.38.2
Parsing documentation for cocoapods-core-0.38.2
Installing ri documentation for cocoapods-core-0.38.2
Parsing documentation for claide-0.9.1
Installing ri documentation for claide-0.9.1
Parsing documentation for xcodeproj-0.26.3
Installing ri documentation for xcodeproj-0.26.3
Parsing documentation for cocoapods-downloader-0.9.3
Installing ri documentation for cocoapods-downloader-0.9.3
Parsing documentation for cocoapods-stats-0.5.3
Installing ri documentation for cocoapods-stats-0.5.3
Parsing documentation for cocoapods-try-0.4.5
Installing ri documentation for cocoapods-try-0.4.5
Parsing documentation for cocoapods-trunk-0.6.4
Installing ri documentation for cocoapods-trunk-0.6.4
Parsing documentation for molinillo-0.3.1
Installing ri documentation for molinillo-0.3.1
Parsing documentation for cocoapods-0.38.2
Installing ri documentation for cocoapods-0.38.2
9 gems installed

OK,cocoapods顺遂安装收尾!
2.cocoapods的使用
那么cocoapods怎么用吗,当然首先次利用会觉得它可怜辛苦,然而呢慢慢地你会发觉这是三个老大好用的工具,能够说是iOS开发者须求!
第二创制大家的Xcode工程:File/New/Project…/Single View Application
起名叫图灵聊天。

作者们将要利用图灵机器人的api举行支付:
图灵机器人官网

开辟项目,新建七个空文件:File/New/File…/ iOS/Others/Empty
起名叫Podfile,那点尤其重庆大学,因为那是cocoapods的配备文件,也正是钦命你要选择什么第叁方库!
笔者们要选用以下多少个库:

  • Alamofire,网络请求库,用来调用图灵机器人的api
  • SnapKit,用代码进行autolayout设置
  • Parse ,Parse云服务平台的SDK
  • ParseUI,Parse提供的便捷UI组件
    在Podfile中,输入以下代码:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.4'
use_frameworks!

钦点下载源,钦定平台版本,使用framework实行合并

是因为swift的特殊性,有些第壹方库必须利用framework来集成,可是那样也有八个利益,作者也是近年才发现,正是Parse和ParseUI其实是OC编写的库,可是呢却不需求OC-斯维夫特的桥接文件了!能够直接当做swift库来利用!

pod 'Alamofire', '~> 1.3'
pod 'SnapKit', '~> 0.12.0'
pod 'Parse','~>1.7.1'
pod 'ParseUI','~>1.1.3'

分选钦命的第壹方库及其版本

开头安装第③方库,打开终端,将当前目录转到Podfile所在目录:

$ cd <Podfile所在目录>

输入以下命令早先布署第①方库:

$ pod install

Analyzing dependencies
Downloading dependencies
Using Alamofire (1.3.1)
Using Bolts (1.2.1)
Using Parse (1.7.5.3)
Using ParseUI (1.1.4)
Using SnapKit (0.12.0)
Generating Pods project
Integrating client project
Sending stats

接下来等待几分钟,假若一切符合规律,没有出现错误的话,打开项目文件后您会面到workspace的公文,今后都要动用那个文件来开辟项目。

图1

开拓项目,看一下类别的结构:

项目结构.png

点一下Pods项目,你会发觉所需的framework已经编译好了,只要在行使前import他俩就能够了:

Pods

OK,到此大家的档次就安插好了,在大家起先搭建UI在此以前,先掌握一下Parse的选拔和有些必需配置

你能够在此处下载上一篇小说的源代码:
上一篇作品源代码

配置Parse

第2打开Parse的官网:
点我
注册二个新的用户,点击右上角的sign up :

Parse注册.png

如上用户名只是示例,可是app名称输入TuringChat。
注册停止后,用你刚刚注册的用户名登陆,应该会见世以下界面:

主界面

然后导入大家的言传身教数据:
点本身下载
点击import按钮:

导入数据

选拔刚刚下载的公文:

数码导入成功.png

然后刚才导入的数额就会呈现出来,并自动新建了3个数目库类:Messages

数据.png

咱俩来看一眼Messages类里都有哪些:

名称 类型 含义 备注
objectId String 系统默认键 每一条数据都对应一个独一无二的id
incoming Boolean 用来确定该条信息是发送给我们的还是发送出去的 true就是发送来的反之就是我们发送出去的
sentDate Date 消息发送时间
text String 消息的内容
createdAt Date 系统默认键 数据创建时间
updatedAt Date 系统默认键 数据上一次更新的时间
ACL ACL 系统默认键 数据的读写模式

接下去大家来测试一下是不是读取到这几个多少,首先要获得该app的application
ID和Client Key:

获取Key

红线划掉的那两行正是我们要求的。
接下来打开项目中的AppDelegate.swift,扩张对Parse库的引用:

import Parse

找到以下办法

  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 

在中间添加代码:

    Parse.setApplicationId("CYdFL9mvG8jHqc4ZA5PJsWMInBbMMun0XCoqnHgf", clientKey: "6tGOC1uIKeYp5glvJE6MXZOWG9pmLtMuIUdh2Yzo")

一而再Parse的服务器

        var query = PFQuery(className: "Messages")
        query.orderByAscending("sentDate")
        query.findObjectsInBackgroundWithBlock { (objects,error) -> Void in
            for object in objects as! [PFObject]{
            let incoming:Bool = object["incoming"] as! Bool
            let text:String = object["text"] as! String
            let sentDate:NSDate = object["sentDate"] as! NSDate
            println("\\(object.objectId!)\\n\\(incoming)\\n\\(text)\\n\\(sentDate)")
        }
     }

新建查询,查询大家刚刚所建的Messages类,用findObjectsInBackgroundWithBlock方法取出查询结果,并用3个循环往复全体打字与印刷出来。
cmd+奥迪Q7运维一下,假使没有毛病会输出接近下边包车型大巴始末:

oYtildSAOz
false
你叫什么名字?
2015-08-28 06:42:00 +0000
LX7kxmmiEp
true
我叫灵灵,聪明又可爱的灵灵
2015-08-28 06:43:00 +0000
p62dmgGIAS
false
你爱不爱我?
2015-08-28 06:43:00 +0000
oWReOM43Nf
true
爱你么么哒
2015-08-28 06:44:00 +0000
mtl2BGt3Mu
false
今天北京天气如何?
2015-08-29 03:59:00 +0000
DikAu5P2Nn
true
北京:08/29 周六,20-29° 28° 雷阵雨 微风小于3级;08/30 周日,19-27° 雷阵雨 微风小于3级;08/31 周一,19-27° 雷阵雨 微风小于3级;09/01 周二,20-26° 雷阵雨 微风小于3级;
2015-08-29 03:59:01 +0000

很好,大家的数据库连接没非凡,那么上边初叶搭建大家的UI。

先是打开大家的品类,你能够找到用于落到实处该部分的文书:
MessageBubbleTableViewCell.swiftMessageSentDateTableViewCell.swift,分别用来促成消息发送时间的cell和推来推去气泡的cell
第②完成音讯发送时间的cell,打开MessageBubbleTableViewCell.swift文本,扩张对SnapKit第叁方库的引用:

搭建UI

咱俩须求搭建的UI只是聊天页面,大家率先来看一看聊天页面的构造:
界面重要由以下四个部分构成

UI结构

那么那三片段怎么样去贯彻呢,作者先向大家做一些总结的介绍:
1.导航栏
这一局地实现比较不难,只要把视图控制器嵌套在三个导航控制器(UINavigationController)中即可,然后对其外观进行一些定制化操作。
2.摆龙门阵窗口
这一有个别用UITableView来创设。仔细考察您会发觉那里一起有三种UITableViewCell:

  • 用来显示音讯发送日期的cell
  • 发送消息气泡的cell
  • 吸收音讯气泡的cell
    但实际我们只要求三个,因为后三种cell分化只是是颜色和职位,大家只要判断一下该新闻是发送的要么收下的,然后相应进行拍卖即可!
    三种cell都是用的以下那么些材质:

    MessageBubble.png

但是,你会问,它为啥是黑色的!怎么让他变成图中的两种颜色呢?还有明明聊天气泡的大小是不定的,这样一张图怎么能满足所有尺寸呢?  
有疑问很好,因为它可以成为你学习的动力,我们会在接下来向大家解释这是如何实现的!Be
patient!  
**3.输入框**  
这里我们要通过重写`UIResponder`类的`inputAccessoryView`属性来自定义我们的输入框,这样做的好处是我们的输入框会和系统的键盘结合起来,可以让其成为第一响应者(first
responder),一旦它成为第一响应者,我们自定义的输入框会跟随键盘一同弹出和收回,就像真正的短信app那样,这个方法比我有一篇文章所写的[实现类似微信的输入框跟随键盘弹出的效果](https://www.jianshu.com/p/4e755fe09df7)的方法还要更好一些,所以说方法不是绝对的,因为你总是能够找到更好的方法,所以,编程的时候要经常在脑子里想"嗯,一定还有更好的方法"。

哦好嘞,废话不多说,上面我们就来一步一步地逐条达成它们!

首先从最简便的做起,达成自定义导航栏:
开辟起初项目你会看出模板文件已经整整建好:
找到AppDelegate.swift文件中的以下措施:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool

在里头添加如下代码:

        var ChatVC:ChatViewController = ChatViewController()
        ChatVC.title = "灵灵"

        UINavigationBar.appearance().tintColor = UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0)
        UINavigationBar.appearance().barTintColor = UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0)
        UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
        UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
        var navigationVC:UINavigationController = UINavigationController(rootViewController: ChatVC)

        let frame = UIScreen.mainScreen().bounds
        window = UIWindow(frame: frame)
        window!.rootViewController = navigationVC
        window!.makeKeyAndVisible()

设置app运营时展现大家自定义的视图控制器,并设置一下导航栏的外观。
ok,第③部分成功。
接下去大家来落实一下第叁片段:输入框,大家要把最难的第1部分留在最终( ⊙ o
⊙ )
打开ChatViewController.swift文件:
加上一些大局常量,在import下边class的概念之上:

let messageFontSize: CGFloat = 17
let toolBarMinHeight: CGFloat = 44

先是个是消息所用的字体大小,第二个是大家输入框的中度。
丰裕一些重组输入框的零部件:

    var toolBar: UIToolbar!
    var textView: UITextView!
    var sendButton: UIButton!

toolBar用来承载输入框中的组件,之所以用UIToolbar是因为它暗中认可出现在荧屏最下方,仿佛你的短信输入框那样。
textView是大家输入文字的地方,而sendButton则是大家的出殡按钮。
上面完成我们重写的inputAccessoryView,在那前边先让大家的视图控制器遵从UITextViewDelegate协议:

class ViewController: UIViewController,UITextViewDelegate {
....
....
}

上边添加以下代码来声称对inputAccessoryView的重写:

 override var inputAccessoryView: UIView! {

}

用get的艺术将输入框的组件实行配备:
在大括号内部添加代码:

        get {
            if toolBar == nil {

                toolBar = UIToolbar(frame: CGRectMake(0, 0, 0, toolBarMinHeight-0.5))

                textView = InputTextView(frame: CGRectZero)
                textView.backgroundColor = UIColor(white: 250/255, alpha: 1)
                textView.delegate = self
                textView.font = UIFont.systemFontOfSize(messageFontSize)
                textView.layer.borderColor = UIColor(red: 200/255, green: 200/255, blue: 205/255, alpha:1).CGColor
                textView.layer.borderWidth = 0.5
                textView.layer.cornerRadius = 5
                //            textView.placeholder = "Message"
                textView.scrollsToTop = false
                textView.textContainerInset = UIEdgeInsetsMake(4, 3, 3, 3)
                toolBar.addSubview(textView)

                sendButton = UIButton.buttonWithType(.System) as! UIButton
                sendButton.enabled = false
                sendButton.titleLabel?.font = UIFont.boldSystemFontOfSize(17)
                sendButton.setTitle("发送", forState: .Normal)
                sendButton.setTitleColor(UIColor(red: 142/255, green: 142/255, blue: 147/255, alpha: 1), forState: .Disabled)
                sendButton.setTitleColor(UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0), forState: .Normal)
                sendButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
                sendButton.addTarget(self, action: "sendAction", forControlEvents: UIControlEvents.TouchUpInside)
                toolBar.addSubview(sendButton)

                // 对组件进行Autolayout设置
                textView.setTranslatesAutoresizingMaskIntoConstraints(false)
                sendButton.setTranslatesAutoresizingMaskIntoConstraints(false)

                toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Left, relatedBy: .Equal, toItem: toolBar, attribute: .Left, multiplier: 1, constant: 8))
                toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Top, relatedBy: .Equal, toItem: toolBar, attribute: .Top, multiplier: 1, constant: 7.5))
                toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Right, relatedBy: .Equal, toItem: sendButton, attribute: .Left, multiplier: 1, constant: -2))
                toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Bottom, relatedBy: .Equal, toItem: toolBar, attribute: .Bottom, multiplier: 1, constant: -8))
                toolBar.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .Right, relatedBy: .Equal, toItem: toolBar, attribute: .Right, multiplier: 1, constant: 0))
                toolBar.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .Bottom, relatedBy: .Equal, toItem: toolBar, attribute: .Bottom, multiplier: 1, constant: -4.5))
            }
            return toolBar
        }

你会意识有三个破绽百出,这是因为我们的InputTextView是贰个独门定义的类,它还不曾概念,咱们在随后会对他做一些操作,最近先不用管它,不过大家先把它定义出来,在视图控制器类之外定义该类:

class InputTextView: UITextView {



}

还有八个难点,用系统暗许的代码完毕autolayout看起来很难知晓,所以那边能够用第1方库SnapKit来促成,把地点设置autolayout的代码替换来以下代码:

textView.setTranslatesAutoresizingMaskIntoConstraints(false)             
sendButton.setTranslatesAutoresizingMaskIntoConstraints(false)

 textView.snp_makeConstraints({ (make) -> Void in

                    make.left.equalTo(self.toolBar.snp_left).offset(8)
                    make.top.equalTo(self.toolBar.snp_top).offset(7.5)
                    make.right.equalTo(self.sendButton.snp_left).offset(-2)
                   make.bottom.equalTo(self.toolBar.snp_bottom).offset(-8)


                })
                sendButton.snp_makeConstraints({ (make) -> Void in
                    make.right.equalTo(self.toolBar.snp_right)
                     make.bottom.equalTo(self.toolBar.snp_bottom).offset(-4.5)

                })

是或不是看起来不难多了?大家来解释一下那段代码:
每一个零部件都有贰个
snp_makeConstraints的闭包方法,用来设置约束,textView.snp_makeConstraints哪怕来设置textView的牢笼
闭包中make.left.equalTo(self.toolBar.snp_left).offset(8)那行代码能够用公式来代表:
也就是textView.left = self.toolBar.left + 8,这样一看就很直观了,文字框的右侧距输入框左侧8点。
make.top.equalTo(self.toolBar.snp_top).offset(7.5)能够用公式
textView.top = self.toolBar.top +7.5代表,剩下的代码以此类推,如下图所示:

autoLaout

sendButton的局地也是这样:
make.right.equalTo(self.toolBar.snp_right)意味着发送按钮右边间接贴输入框的动手,没有位移
make.bottom.equalTo(self.toolBar.snp_bottom).offset(-4.5)出殡按钮底部距离输入框底部4.5点
那般是或不是让autoLayout变得简单很多了?后边的种类大家就径直利用它来展开autoLayout设置了!

import SnapKit

明天未曾不当了,cmd+奥迪Q5运维一下,啊哦,为什么是空白!我你骗人!( ⊙ o ⊙ )

=好吧,我们还差一步,记得呢,它要改成第①响应者才能弹出键盘哦,大家要重写3个办法它才能奏效!在视图控制器类中追加以下方法:

   override func canBecomeFirstResponder() -> Bool {
        return true
    }

告诉大家的种类大家自定义的输入框能够改为第壹响应者,我们也是有身份证的!
然后在运作一下,假诺没有错误,应该会有以下职能:

chat.gif

忽略黑洞洞的背景,因为我们还平昔不拉长内容。。。
只是你会发现2个标题,键盘怎么回来呀。。不管怎么点都尚未反应啊!

好啊,下边大家来用贰个高超的章程来消除它。由于聊天页面是二个UITableView,所以大家得以选拔UITableViewContoller来替代大家的UIViewContoller,那样我们的页面中就暗许有了2个UITableView,然后它有二个格外实用的性子—keyboardDismissMode,大家把它设置为.Interactive也便是键盘的弹出和注销状态能够依照你对tableView的拖拽进行变更,也正是您的手指头拖到何地你的键盘就到哪儿,是否很酷。
变动视图控制器的类型:

class ChatViewController:UITableViewController,UITextViewDelegate {
....
....
....
}

在viewDidLoad里添加一行代码来设置keyboardDismissMode:

tableView.keyboardDismissMode = .Interactive

再次运营,你会意识黑洞洞的背景不见了,取而代之的是空手的TableView!而且键盘也促成了炫酷的效益!

chat.gif

作品本有的源代码
好的,第②部分顺畅贯彻!第三有个别是我们的重心,内容较多,所以笔者把它放到教程的第③局部中。
其次有个别学科已经出炉,欢迎围观!
swift完成二个与智能机器人聊天的app(二)

在类里增加叁个UILabel的性子,用来呈现时间:

比方该作品对您有辅助,请点一下兴奋!您的援救是本人三番五次写作的重力!

  let sentDateLabel: UILabel

在override init()方法中添加代码:

        sentDateLabel = UILabel(frame: CGRectZero)
        sentDateLabel.backgroundColor = UIColor.clearColor()
        sentDateLabel.font = UIFont.systemFontOfSize(10)
        sentDateLabel.textAlignment = .Center
        sentDateLabel.textColor = UIColor(red: 142/255, green: 142/255, blue: 147/255, alpha: 1)

安装时间标签的背景象、字体,文字居中对齐、文字颜色。

        super.init(style: style, reuseIdentifier: reuseIdentifier)
        selectionStyle = .None
        contentView.addSubview(sentDateLabel)

调用父类的构造方法。
我们将该cell设置为不可选,因为大家唯有必要体现时间而已。
最后将标签添加到cell的视图

   sentDateLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        sentDateLabel.snp_makeConstraints { (make) -> Void in
            make.centerX.equalTo(contentView.snp_centerX)
            make.top.equalTo(contentView.snp_top).offset(13)
            make.bottom.equalTo(contentView.snp_bottom).offset(-4.5)
        }

将标签左右居中,顶部离开cell视图顶部13点,底部距离cell视图尾部4.5点。关于SnapKit的采纳作者在上一篇文章提到了一些,真的很是地好用,上手也非常的慢,只要您想出一个公式,比如上边那段代码能够转化为:

sentDateLabel.centerX = contentView.centerX
sentDateLabel.top = contentView.top + 13
sentDateLabel.bottom = contentView.bottom - 4.5

ok,突显消息发送时间的cell就设置好了。

接下去打开MessageBubbleTableViewCell.swift文件,扩张新的性质:

    let bubbleImageView: UIImageView
    let messageLabel: UILabel

在import上面扩展全局变量,用来标示cell的项目(接受或发送的音讯):

let incomingTag = 0, outgoingTag = 1
let bubbleTag = 8

在类外扩大部分主意,在文件结尾添加以下代码:

let bubbleImage = bubbleImageMake()

func bubbleImageMake() -> (incoming: UIImage, incomingHighlighed: UIImage, outgoing: UIImage, outgoingHighlighed: UIImage) {
   let maskOutgoing = UIImage(named: "MessageBubble")!
   let maskIncoming = UIImage(CGImage: maskOutgoing.CGImage, scale: 2, orientation: .UpMirrored)!

   let capInsetsIncoming = UIEdgeInsets(top: 17, left: 26.5, bottom: 17.5, right: 21)
   let capInsetsOutgoing = UIEdgeInsets(top: 17, left: 21, bottom: 17.5, right: 26.5)

   let incoming = coloredImage(maskIncoming, 229/255, 229/255, 234/255, 1).resizableImageWithCapInsets(capInsetsIncoming)
   let incomingHighlighted = coloredImage(maskIncoming, 206/255, 206/255, 210/255, 1).resizableImageWithCapInsets(capInsetsIncoming)
   let outgoing = coloredImage(maskOutgoing,  0.05 ,0.47,0.91,1.0).resizableImageWithCapInsets(capInsetsOutgoing)
   let outgoingHighlighted = coloredImage(maskOutgoing, 32/255, 96/255, 200/255, 1).resizableImageWithCapInsets(capInsetsOutgoing)

   return (incoming, incomingHighlighted, outgoing, outgoingHighlighted)
}

归来1个结构体包蕴4种图片:发送音讯气泡的健康和高亮(被点击后)图片,接收音信气泡的常规和高亮图片,以供调用。

MessageBubble.png

那是图形的原型,不难精通那是发送新闻对应的扯淡气泡,所以直接调用即可

let maskOutgoing = UIImage(named: "MessageBubble")!

可是接受音信的血泡和它的关联是水平镜像,所以大家要用二个办法赢得它的品位镜像图片:

 let maskIncoming = UIImage(CGImage: maskOutgoing.CGImage, scale: 2, orientation: .UpMirrored)!

可是那三个图片并不可能用,因为它的分寸是固定的,可是大家的新闻的尺寸是不定的,所以,要把它们做成大小可变的图片,首先设置可拉伸区域:

 let capInsetsIncoming = UIEdgeInsets(top: 17, left: 26.5, bottom: 17.5, right: 21)
 let capInsetsOutgoing = UIEdgeInsets(top: 17, left: 21, bottom: 17.5, right: 26.5)

那正是说它是怎么规定可拉伸区域的吧,这些示意图能够分解一切:

可拉伸区域

实质上这些可拉伸区域只有1×1像素,可是也够大家用了,因为这一片段能够无限地横向或纵向拉伸,接收新闻气泡和出殡和埋葬音讯气泡可拉伸区域唯一的不相同就是程度方向上,所以把right和left的值相互调换即可。
下一场经过UIImageresizableImageWithCapInsets()格局,获取可拉伸图片:

 let incoming = coloredImage(maskIncoming, 229/255, 229/255, 234/255, 1).resizableImageWithCapInsets(capInsetsIncoming)
    let incomingHighlighted = coloredImage(maskIncoming, 206/255, 206/255, 210/255, 1).resizableImageWithCapInsets(capInsetsIncoming)
    let outgoing = coloredImage(maskOutgoing,  0.05 ,0.47,0.91,1.0).resizableImageWithCapInsets(capInsetsOutgoing)
    let outgoingHighlighted = coloredImage(maskOutgoing, 32/255, 96/255, 200/255, 1).resizableImageWithCapInsets(capInsetsOutgoing)

自然这么些图片还调用了二个主意coloredImage()进展染色处理,正是上面的这一个主意:

func coloredImage(image: UIImage, red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> UIImage! {
    let rect = CGRect(origin: CGPointZero, size: image.size)
    UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
    let context = UIGraphicsGetCurrentContext()
    image.drawInRect(rect)
    CGContextSetRGBFillColor(context, red, green, blue, alpha)
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop)
    CGContextFillRect(context, rect)
    let result = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result
}

取得图片大小

  let rect = CGRect(origin: CGPointZero, size: image.size)

创设位图绘图上下文

  UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)

赢得位图绘图上下文,并初叶开始展览渲染操作

   let context = UIGraphicsGetCurrentContext()
    image.drawInRect(rect)
    CGContextSetRGBFillColor(context, red, green, blue, alpha)
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop)
    CGContextFillRect(context, rect)

取得到绘图结果,停止位图绘图上下文并赶回绘图结果

    let result = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return result

帮衬方法写完,下边初阶展开cell的安插,在init()方法中添加以下代码:

        bubbleImageView = UIImageView(image: bubbleImage.incoming, highlightedImage: bubbleImage.incomingHighlighed)
        bubbleImageView.tag = bubbleTag
        bubbleImageView.userInteractionEnabled = true // #CopyMesage

        messageLabel = UILabel(frame: CGRectZero)
        messageLabel.font = UIFont.systemFontOfSize(messageFontSize)
        messageLabel.numberOfLines = 0
        messageLabel.userInteractionEnabled = false   // #CopyMessage

设置气泡视图和新闻标签

        super.init(style: .Default, reuseIdentifier: reuseIdentifier)
        selectionStyle = .None

        contentView.addSubview(bubbleImageView)
        bubbleImageView.addSubview(messageLabel)

初始化cell

        bubbleImageView.setTranslatesAutoresizingMaskIntoConstraints(false)
        messageLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        bubbleImageView.snp_makeConstraints { (make) -> Void in
            make.left.equalTo(contentView.snp_left).offset(10)
            make.top.equalTo(contentView.snp_top).offset(4.5)
            make.width.equalTo(messageLabel.snp_width).offset(30)
            make.bottom.equalTo(contentView.snp_bottom).offset(-4.5)


        }
        messageLabel.snp_makeConstraints { (make) -> Void in
            make.centerX.equalTo(bubbleImageView.snp_centerX).offset(3)
            make.centerY.equalTo(bubbleImageView.snp_centerY).offset(-0.5)
            messageLabel.preferredMaxLayoutWidth = 218
            make.height.equalTo(bubbleImageView.snp_height).offset(-15)

        }

进行autolayout设置

只是那样只是一种聊天气泡,而且尚未安装音信内容,我们要依据音讯内容和花色对cell举行安顿,在那在此以前大家先是周密大家的消息模型Message,打开Message.swift,在类中添加如下代码:

    let incoming: Bool
    let text: String
    let sentDate: NSDate

    init(incoming: Bool, text: String, sentDate: NSDate) {
        self.incoming = incoming
        self.text = text
        self.sentDate = sentDate
    }

然后回到大家的MessageBubbleTableViewCell.swift,添加以下的配置格局:

    func configureWithMessage(message: Message) {
           //1
            messageLabel.text = message.text
           //2
            let constraints: NSArray = contentView.constraints()
            let indexOfConstraint = constraints.indexOfObjectPassingTest { (var constraint, idx, stop) in
                return (constraint.firstItem as! UIView).tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.Left || constraint.firstAttribute == NSLayoutAttribute.Right)
            }
            contentView.removeConstraint(constraints[indexOfConstraint] as! NSLayoutConstraint)
            //3
            bubbleImageView.snp_makeConstraints({ (make) -> Void in
                if message.incoming {
                    tag = incomingTag
                    bubbleImageView.image = bubbleImage.incoming
                    bubbleImageView.highlightedImage = bubbleImage.incomingHighlighed
                    messageLabel.textColor = UIColor.blackColor()
                  make.left.equalTo(contentView.snp_left).offset(10)
                    messageLabel.snp_updateConstraints { (make) -> Void in
                        make.centerX.equalTo(bubbleImageView.snp_centerX).offset(3)
                    }

                } else { // outgoing
                    tag = outgoingTag
                    bubbleImageView.image = bubbleImage.outgoing
                    bubbleImageView.highlightedImage = bubbleImage.outgoingHighlighed
                    messageLabel.textColor = UIColor.whiteColor()
                     make.right.equalTo(contentView.snp_right).offset(-10)
                    messageLabel.snp_updateConstraints { (make) -> Void in
                        make.centerX.equalTo(bubbleImageView.snp_centerX).offset(-3)
                    }


                }
            })


    }

//1
安装音信内容。
//2
剔除聊天气泡的left或right约束,以便于遵照音信类型重新开始展览安装。
//3
依照新闻类型进行相应的设置,包罗利用的图纸还有约束原则。由于发送音信的聊气候泡是靠右的,而接受消息的聊天气泡是靠左的,所以发送消息的聊天气泡距离cell左边缘10点:

 make.right.equalTo(contentView.snp_right).offset(-10)

接受音信的聊天气泡距离cell左侧缘10点:

make.left.equalTo(contentView.snp_left).offset(10)

对应地,消息内容的Label也呼应右移或左移3点:

  messageLabel.snp_updateConstraints { (make) -> Void in
                        make.centerX.equalTo(bubbleImageView.snp_centerX).offset(3)
                    }

   messageLabel.snp_updateConstraints { (make) -> Void in
                        make.centerX.equalTo(bubbleImageView.snp_centerX).offset(-3)
                    }

ok,到近期截至我们早就完毕了三种tableViewCell,下边我们来探望哪些体现出来那几个消息!

将聊天内容显示到主界面

那里大家将采纳假数据,只是为了演示如何兑现,大家将在下一篇文章珍视介绍怎么将忠实的数据呈现出来!
打开ChatViewController.swift文本,在类里添加如下属性,用于存放我们的闲聊数据:

var messages:[[Message]] = [[]]

那是一个Message类型的数组,数组的成分也是1个Message类型的数组。为啥要那样定义呢,那是为了不一致聊天产生的时光,同一段日子爆发的扯淡打包到共同组成2个数组成分,超过这一段时间的谈天放到新开辟的数组成分中,那样做也有益于大家的tableView明确分区(section)和行(row),同一段日子的闲聊放在同一个section,超越那段时光的闲话放在下叁个section,每一分区(section)中有几个消息,就有几行(row)。
找到viewDidLoad()方法,在super.viewDidLoad()那行代码下添加如下代码:

        tableView.registerClass(MessageSentDateTableViewCell.self, forCellReuseIdentifier: NSStringFromClass(MessageSentDateTableViewCell))

注册tableViewCell

        self.tableView.keyboardDismissMode = .Interactive
        self.tableView.estimatedRowHeight = 44
        self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom:toolBarMinHeight, right: 0)
        self.tableView.separatorStyle = .None

对tableView进行一些必备的设置,由于tableView底部有一个输入框,由此会遮挡cell,所以要将tableView的剧情inset增添一些底层位移:

self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom:toolBarMinHeight, right: 0)

       messages = [
            [
                Message(incoming: true, text: "你叫什么名字?", sentDate: NSDate(timeIntervalSinceNow: -12*60*60*24)),
                Message(incoming: false, text: "我叫灵灵,聪明又可爱的灵灵", sentDate: NSDate(timeIntervalSinceNow:-12*60*60*24))
            ],
            [
                Message(incoming: true, text: "你爱不爱我?", sentDate: NSDate(timeIntervalSinceNow: -6*60*60*24 - 200)),
                Message(incoming: false, text: "爱你么么哒", sentDate: NSDate(timeIntervalSinceNow: -6*60*60*24 - 100))
            ],
            [
                Message(incoming: true, text: "北京今天天气", sentDate: NSDate(timeIntervalSinceNow: -60*60*18)),
                Message(incoming: false, text: "北京:08/30 周日,19-27° 21° 雷阵雨转小雨-中雨 微风小于3级;08/31 周一,18-26° 中雨 微风小于3级;09/01 周二,18-25° 阵雨 微风小于3级;09/02 周三,20-30° 多云 微风小于3级", sentDate: NSDate(timeIntervalSinceNow: -60*60*18))
            ],
            [
                Message(incoming: true, text: "你在干嘛", sentDate: NSDate(timeIntervalSinceNow: -60)),
                Message(incoming: false, text: "我会逗你开心啊", sentDate: NSDate(timeIntervalSinceNow: -65))
            ],
        ]

填充假的闲话数据

重写tableView的代办方法,设置tableView的分区数和行数:

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return messages.count

    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return messages[section].count + 1
    }

重写tableView设置cell的代理方法

   override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        if indexPath.row == 0{

            let cellIdentifier = NSStringFromClass(MessageSentDateTableViewCell)
            var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier,forIndexPath: indexPath) as! MessageSentDateTableViewCell
            let message = messages[indexPath.section][0]


            cell.sentDateLabel.text = "\(message.sentDate)"

            return cell

        }else{
            let cellIdentifier = NSStringFromClass(MessageBubbleTableViewCell)
            var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! MessageBubbleTableViewCell!
            if cell == nil {

                cell = MessageBubbleTableViewCell(style: .Default, reuseIdentifier: cellIdentifier)
            }



            let message = messages[indexPath.section][indexPath.row - 1]

            cell.configureWithMessage(message)




            return cell
        }

    }

要是没有不当,cmd+CR-V运维一下,应该能出现上面包车型地铁成效:

iOS Simulator Screen Shot 2015年9月5日.png

音讯是常规显示出来了,可是音信的发送时间看起来很别扭,所以大家必要对其举办格式化,在类中添加如下方法:

    func formatDate(date: NSDate) -> String {
        let calendar = NSCalendar.currentCalendar()
        var dateFormatter = NSDateFormatter()
        dateFormatter.locale = NSLocale(localeIdentifier: "zh_CN")

        let last18hours = (-18*60*60 < date.timeIntervalSinceNow)
        let isToday = calendar.isDateInToday(date)
        let isLast7Days = (calendar.compareDate(NSDate(timeIntervalSinceNow: -7*24*60*60), toDate: date, toUnitGranularity: .CalendarUnitDay) == NSComparisonResult.OrderedAscending)

        if last18hours || isToday {
            dateFormatter.dateFormat = "a HH:mm"
        } else if isLast7Days {
            dateFormatter.dateFormat = "MM月dd日 a HH:mm EEEE"
        } else {
            dateFormatter.dateFormat = "YYYY年MM月dd日 a HH:mm"

        }
        return dateFormatter.stringFromDate(date)
    }

你会感觉到看到了一些奇怪的东西,所以小编来解释一下这一个代码:

let calendar = NSCalendar.currentCalendar()

获得当前的日历,大家要运用在那之中的一部分措施

    var dateFormatter = NSDateFormatter()
        dateFormatter.locale = NSLocale(localeIdentifier: "zh_CN")

新建日期格式化器,设置地区为华夏次大陆

        let last18hours = (-18*60*60 < date.timeIntervalSinceNow)
        let isToday = calendar.isDateInToday(date)
        let isLast7Days = (calendar.compareDate(NSDate(timeIntervalSinceNow: -7*24*60*60), toDate: date, toUnitGranularity: .CalendarUnitDay) == NSComparisonResult.OrderedAscending)

安装有个别布尔变量用来判定新闻发送时间相对于近期时光有多短时间

 if last18hours || isToday {
            dateFormatter.dateFormat = "a HH:mm"
        } else if isLast7Days {
            dateFormatter.dateFormat = "MM月dd日 a HH:mm EEEE"
        } else {
            dateFormatter.dateFormat = "YYYY年MM月dd日 a HH:mm"

        }

据书上说新闻新旧来设置日期格式,那么些格式由局地占位符和UTF-8字符构成,以下是常用占位符表:

占位符 含义
YYYY 年份
MM 月份
dd
HH 小时
mm 分钟
ss
a 表示上午、下午等
EEEE 星期几

之所以在此间日期就被代表为(以二零一六年6月二11日中午10点为例):
a HH:mm 对应上午 10:10
MM月dd日 a HH:mm EEEE对应 9月3日 上午 10:00 星期四
YYYY年MM月dd日 a HH:mm对应2015年9月3日 上午 10:00
最近,在给日期赋值前,调用该措施进行格式化,修改下边这一行代码:

cell.sentDateLabel.text = "\(message.sentDate)"

 cell.sentDateLabel.text = formatDate(message.sentDate)

然后重国民党的新生活运动行:

iOS Simulator Screen Shot 2015年9月5日.png

看!那样就极漂亮貌了吗?
到此处大家的第①片段学科就到位了,第壹部分将会落到实处出殡和埋葬音信、用Alamofire互连网请求实行聊天讯息的申报,从Parse服务器收到和封存聊天音讯,真正实现和智能机器人聊天!敬请期待!
本篇小说源代码放在了百度网盘里:
下载地址

若是该小说对您有扶持,请点一下欢乐!您的支撑是自个儿继续写作的重力!