用 Swift 做一个智能机器人聊天 App (4)

终于有时间继续写我的文章了,这段时间在赶学校的软件课程设计,可算弄完了!
下面继续我们的创造之旅~

本篇文章你会学到

  • 用KVO方法优化键盘弹出动画
  • 将同步下载消息改为异步,以减轻主线程的压力。
  • 实现app登录、注册的功能
    首先下载本章源代码:
    百度网盘地址
    在上一章结尾我提到:
    我们的app在键盘弹出时有一些问题:
  • 在我们点出键盘时会遮挡消息:

    iOS Simulator Screen Shot 2015年9月8日 下午4.14.55.png
  • 键盘弹出时把tableView拉到底部会有一个很难看的空白:

    iOS Simulator Screen Shot 2015年9月8日 下午4.15.21.png

    下面我们来解决它,我们需要在键盘弹出时修改tableView的一些属性和约束条件,所以我们需要在键盘弹出时得到通知,要做到这个,我们要使用KVO(Key-Value Observing)方法。
    viewDidLoad()中的结尾添加以下代码来添加键值监控:

    首先获取通知中心的实例,然后添加两个观察者,第一个用来监控UIKeyboardWillShowNotification键值的变化,这是系统提供的键值,当键盘将要弹出时会改变;第二个监控 UIKeyboardDidShowNotification,同样地,这也是系统提供的,当键盘完全弹出时会改变。
    当这两个键值改变时,会向通知中心发送通知,然后由我们自定义的两个selector方法处理通知,下面定义这两个方法。
    首先第一个方法:

    很难懂?不着急,我们一步一步解释这些代码!
    首先取出通知的userifno,键盘的所有属性都在这里面,他是一个字典类型的数据:

    然后通过UIKeyboardFrameEndUserInfoKeykey取出键盘的位置、大小信息,也就是frame,并将其的参考view设置为tableView,记录下它的高度

    然后我们需要计算一些数据:

    insetChange指的是那部分呢?我画出一个图大家就明白了:

insetChange

tableview的contentInset所指的是所图的红框部分。
overflow指的是所有消息的总高度和键盘弹出前contentInset的差值,实际上就是没有显示部分的高度,也就是溢出的部分。
然后通过UIKeyboardAnimationDurationUserInfoKey
key来得到键盘弹出动画的持续时间,设置自定义的动画闭包:

我们看一下动画闭包内部做了些什么。
首先判断tableView的滚动是否停止了,如果没有停止滚动就不做任何事情。
tableView的滚动有两种情况:

  1. 手指点击tableView,开始滚动,即tracking
  2. 手指抬起,tableView还会有一段减速滚动,也就是decelerating

    如果溢出大于0,则将tableView当前位置contentOffset向下移动,也就对应着手指向上拖动insetChange的高度,这样可以保证消息和键盘同时向上移动,但是如果滚动之后仍然是负值,且超出insetOld.top的距离,也就是导航栏的高度,就把tableView的当前位置设置成屏幕之上一个导航栏的高度。
    如果溢出是负值,但是绝对值小于insetChange,则contenOffset.y增加两者的差值。
    当时长大于0时真正执行我们的动画闭包,否则就即时执行闭包:

    其中要注意的是,我们的动画曲线要和键盘弹出动画的曲线相同,所以要用 UIKeyboardAnimationCurveUserInfoKeykey得到曲线信息,这里的类型转换比较麻烦,要进行左移16的位运算,因为没有对应的 as类型转换可用,只能用最底层的方式。
    为什么要这样呢,其实我也不知道。。我也是查来的= =
    stackoverflow
    第二个方法,是用来防止出现底下的白边,原理就是限制显示出的高度,将底部切掉一部分,也就是将contenInset.bottom值变大一些,变大为键盘的高度:

    这样就好了,运行一下,是不是感觉舒服多了?
    好的,下面我们解决下一个问题,在我们打开app的时候,会看到控制台显示如下内容:

    意思是有一个长运行时间的操作在主线程执行,由于主线程主要用于UI显示,所以如果有其他占用cpu的线程也在其中运行的话会使得UI显示变得很卡。
    虽然没有什么感觉,但是如果我们去看cpu的负荷图的话,如下图所示:

cpu负荷图.png

会看到一个瞬间cpu负荷暴涨到了32%!这样很不酷对不对?
我们的解决办法就是,将这个占用cpu很多使用量的操作放在另一个线程中,但首先我们要找到这是哪个操作,细心的你一定注意到,当加载聊天界面的时候会比较慢,没错就是那个操作在作怪!
所以呢,我们对initData()方法进行一些优化。
首先改变我们从Parse服务器下载数据的方法query.findObjects(),这是同步下载数据,会占据我们很大一部分cpu负载,所以我们要改为异步下载,也就是放到其他线程执行,将以下代码修改一下:

修改为以下使用findObjectsInBackgroundWithBlock的版本:

由于这是异步下载,所以tableView仍然会继续加载cell而不会去管messages里有没有值,这时一定会崩溃,所以为了防止这种情况发生,我们要首先给messages赋一个欢迎消息,在方法开头加上这一行代码:

然后运行一下,同时看一下cpu的负荷率表:

屏幕快照 2015-09-14 下午10.25.58.png

仅有7%了!干的漂亮!
下面我们来为我们的app增加一个登录的功能,因为没有办法去区分聊天信息,所有人的聊天信息都是共享的,真正的聊天app可不会是这样的。
要做到这个,我们要为我们数据库上的聊天消息类增加一个新属性:

添加新属性

选择属性类型

)
User类是Parse默认的用户类,我们的类型用指针,指向用户类,将信息与用户进行绑定,这样就能知道该条信息属于哪个用户了。
幸运的是Parse已经提供了登录的视图控制器,同样还有注册的视图控制器:
PFLogInViewControllerPFSignUpViewController
虽然它本身的语言是英文,但是我在初始项目里对他们进行了一下汉化修改,其实有更好的办法进行国际化,但这个只是为了演示。
首先我们创建一个欢迎页面:

屏幕快照 2015-09-15 上午8.55.18.png

还有登录页面,注册页面:

登录页面

注册页面

都加上

LogInViewController.swift中的viewDidLoad()方法里添加以下代码来自定义logo:

同样地,在SignUpViewController.swift中的viewDidLoad()方法里添加以下代码:

WelcomeViewController.swift增加import模块:

使WelcomeViewController遵循PFSignUpViewControllerDelegate
PFLogInViewControllerDelegate代理:

增加属性,登录视图控制器和注册视图控制器,还有欢迎界面的logowelcomeLabel用来显示logo和欢迎语:

我们来实现一些代理方法,首先是登录的代理方法:

第一个方法是执行我们自定义的用户名密码的合法性检查方法;第二个是在登录之后执行,可以通过user参数知道登录的是哪个用户;第三个是如果登录出现错误,错误信息可以在这里找到。
同样地,实现注册相应的三个方法:

下面我们在viewDidLoad()中配置一下欢迎界面:

我们在viewWillAppear()方法中实现欢迎页面逻辑,当已经登录时,显示欢迎语欢迎某某某,然后2s后进入聊天界面,否则显示未登录,进入登录界面:

定义这个延时方法,在import下面:

运行之前还有一步,就是在AppDelegate.swiftapplication()方法里修改我们的初始视图控制器:

还有一件事,我们要在读取数据的时候只读取当前登录用户的信息,而不是全部,所以我们要加上一个限制,在query.findObjectsInBackgroundWithBlock执行前加上以下代码:

同样地,我们保存消息的时候,将当前用户赋值给createdBy属性,修改一下saveMessage()方法:

至此我们的登录注册功能就集成进我们的app了,当然这只是一个演示,为了演示如何用ParseUI库实现登录功能,并没有太多的自定义,更复杂的应用这里先不进行扩展了。

登录.gif

到此我们的app已经有一些正式的样子了,下一章还会对其进行功能的扩充和优化!请持续关注!
本章完成源代码下载
如果我的文章对你有帮助,请点一下喜欢,大家的支持是我继续写作的动力!

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部