TableView自定义聊天界面

 
1,下面是一个放微信聊天界面的消息展示列表,实现的功能有:

  • (1)消息可以是文本消息也可以是图片消息
  • (2)消息背景为气泡状图片,同时消息气泡可根据内容自适应大小
  • (3)每条消息旁边有头像,在左边表示发送方,在右边表示接收方
 
2,实现思路
  • (1)需要定义一个数据结构保存消息内容 MessageItem
  • (2)继承UITableViewCell实现自定义单元格,这里面放入头像和消息体
  • (3)继承UITableView实现自定义表格,通过读取数据源,进行页面的渲染
  • (4)消息体根据内容类型不同,用不同的展示方法
  • (5)每个单元格的高度需要根据内容计算出来
  • (6)数据由ViewController来提供初始化数据
 
3,效果图
  
 
4,代码结构
  
 
5,主要代码
(1)主页面 ViewController.swift
 import UIKit

 class ViewController: UIViewController, ChatDataSource {

     var Chats:Array<MessageItem>!
     var tableView:TableView!

     override func viewDidLoad() {
         super.viewDidLoad()
         // Do any additional setup after loading the view, typically from a nib.

         setupChatTable()
     }

     /*创建表格及数据*/
     func setupChatTable()
     {
         self.tableView = TableView(frame:CGRectMake(, ,
             self.view.frame.size.width, self.view.frame.size.height - ))

         //创建一个重用的单元格
         self.tableView!.registerClass(TableViewCell.self, forCellReuseIdentifier: "MsgCell")

         var me = "xiaoming.png"

         var you = "xiaohua.png"

         var first =  MessageItem(body:"嘿,这张照片咋样,我周末拍的呢!", logo:me,
             date:NSDate(timeIntervalSinceNow:-), mtype:ChatType.Mine)

         var second =  MessageItem(image:UIImage(named:"luguhu.jpeg")!,logo:me,
             date:NSDate(timeIntervalSinceNow:-), mtype:ChatType.Mine)

         var third =  MessageItem(body:"太赞了,我也想去那看看呢!",logo:you,
             date:NSDate(timeIntervalSinceNow:-), mtype:ChatType.Someone)

          var fouth =  MessageItem(body:"嗯,下次我们一起去吧!",logo:me,
             date:NSDate(timeIntervalSinceNow:-), mtype:ChatType.Mine)

         var fifth =  MessageItem(body:"好的,一定!",logo:you,
             date:NSDate(timeIntervalSinceNow:), mtype:ChatType.Someone)

         Chats = [first,second, third, fouth, fifth]

         self.tableView.chatDataSource = self

         self.tableView.reloadData()

         self.view.addSubview(self.tableView)
     }

     override func didReceiveMemoryWarning() {
         super.didReceiveMemoryWarning()
         // Dispose of any resources that can be recreated.
     }

     /*返回对话记录中的全部行数*/
     func rowsForChatTable(tableView:TableView) -> Int
     {
         return self.Chats.count
     }

     /*返回某一行的内容*/
     func chatTableView(tableView:TableView, dataForRow row:Int) -> MessageItem
     {
         return Chats[row]
     }
 }

(2)消息体数据结构 MessageItem.swift

 import UIKit

 //消息类型,我的还是别人的
 enum ChatType
 {
     case Mine
     case Someone
 }

 class MessageItem
 {
     //头像
     var logo:String
     //消息时间
     var date:NSDate
     //消息类型
     var mtype:ChatType
     //内容视图,标签或者图片
     var view:UIView
     //边距
     var insets:UIEdgeInsets

     //设置我的文本消息边距
     class func getTextInsetsMine() -> UIEdgeInsets
     {
         , left:, bottom:, right:)
     }

     //设置他人的文本消息边距
     class func getTextInsetsSomeone() -> UIEdgeInsets
     {
         , left:, bottom:, right:)
     }

     //设置我的图片消息边距
     class func getImageInsetsMine() -> UIEdgeInsets
     {
         , left:, bottom:, right:)
     }

     //设置他人的图片消息边距
     class func getImageInsetsSomeone() -> UIEdgeInsets
     {
         , left:, bottom:, right:)
     }

     //构造文本消息体
     convenience init(body:NSString, logo:String, date:NSDate, mtype:ChatType)
     {
         var font =  UIFont.boldSystemFontOfSize()

         var width =  , height = 10000.0

         var atts =  NSMutableDictionary()
         atts.setObject(font,forKey:NSFontAttributeName)

         var size =  body.boundingRectWithSize(CGSizeMake(CGFloat(width), CGFloat(height)),
             options:NSStringDrawingOptions.UsesLineFragmentOrigin, attributes:atts, context:nil)

         var label =  UILabel(frame:CGRectMake(, , size.size.width, size.size.height))

         label.numberOfLines =
         label.lineBreakMode = NSLineBreakMode.ByWordWrapping
         label.text = (body.length !=  ? body : "")
         label.font = font
         label.backgroundColor = UIColor.clearColor()

         var insets:UIEdgeInsets =  (mtype == ChatType.Mine ?
             MessageItem.getTextInsetsMine() : MessageItem.getTextInsetsSomeone())

         self.init(logo:logo, date:date, mtype:mtype, view:label, insets:insets)
     }

     //可以传入更多的自定义视图
     init(logo:String, date:NSDate, mtype:ChatType, view:UIView, insets:UIEdgeInsets)
     {
         self.view = view
         self.logo = logo
         self.date = date
         self.mtype = mtype
         self.insets = insets
     }

     //构造图片消息体
     convenience init(image:UIImage, logo:String,  date:NSDate, mtype:ChatType)
     {
         var size = image.size
         //等比缩放
         )
         {
             size.height /= (size.width / );
             size.width = ;
         }
         var imageView = UIImageView(frame:CGRectMake(, , size.width, size.height))
         imageView.image = image
         imageView.layer.cornerRadius = 5.0
         imageView.layer.masksToBounds = true

         var insets:UIEdgeInsets =  (mtype == ChatType.Mine ?
             MessageItem.getImageInsetsMine() : MessageItem.getImageInsetsSomeone())

         self.init(logo:logo,  date:date, mtype:mtype, view:imageView, insets:insets)
     }
 }

(3)表格数据协议 ChatDataSource.swift

 import Foundation

 /*
   数据提供协议
 */
 protocol ChatDataSource
 {
     /*返回对话记录中的全部行数*/
     func rowsForChatTable( tableView:TableView) -> Int
     /*返回某一行的内容*/
     func chatTableView(tableView:TableView, dataForRow:Int)-> MessageItem
 }

(4)自定义表格 TableView.swift

 import UIKit

 class TableView:UITableView,UITableViewDelegate, UITableViewDataSource
 {
     //用于保存所有消息
     var bubbleSection:Array<MessageItem>!
     //数据源,用于与 ViewController 交换数据
     var chatDataSource:ChatDataSource!

     required init(coder aDecoder: NSCoder) {

         super.init(coder: aDecoder)
     }

     override init(frame:CGRect)
     {
         self.bubbleSection = Array<MessageItem>()

         super.init(frame:frame,  style:UITableViewStyle.Grouped)

         self.backgroundColor = UIColor.clearColor()

         self.separatorStyle = UITableViewCellSeparatorStyle.None
         self.delegate = self
         self.dataSource = self

     }

     override func reloadData()
     {

         self.showsVerticalScrollIndicator = false
         self.showsHorizontalScrollIndicator = false

         var count =
         if ((self.chatDataSource != nil))
         {
             count = self.chatDataSource.rowsForChatTable(self)

             )
             {   

                 ; i < count; i++)
                 {

                     var object =  self.chatDataSource.chatTableView(self, dataForRow:i)
                     bubbleSection.append(object)

                 }

                 //按日期排序方法
                 bubbleSection.sort({$.date.timeIntervalSince1970 < $.date.timeIntervalSince1970})
             }
         }
         super.reloadData()
     }

     //第一个方法返回分区数,在本例中,就是1
     func numberOfSectionsInTableView(tableView:UITableView)->Int
     {

     }

     //返回指定分区的行数
     func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
     {
         if (section >= self.bubbleSection.count)
         {

         }

     }

     //用于确定单元格的高度,如果此方法实现得不对,单元格与单元格之间会错位
     func tableView(tableView:UITableView,heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat
     {

         // Header
         )
         {
             return 30.0
         }

         var data =  self.bubbleSection[indexPath.row - ]

         )
     }

     //返回自定义的 TableViewCell
     func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
         -> UITableViewCell
     {

         var cellId = "MsgCell"
         )
         {
             var data =  self.bubbleSection[indexPath.row-]

             var cell =  TableViewCell(data:data, reuseIdentifier:cellId)

             return cell
         }
         else
         {

             return UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellId)
         }
     }
 }

(5)自定义单元格 TableViewCell.swift

 import UIKit

 class TableViewCell:UITableViewCell
 {
     //消息内容视图
     var customView:UIView!
     //消息背景
     var bubbleImage:UIImageView!
     //头像
     var avatarImage:UIImageView!
     //消息数据结构
     var msgItem:MessageItem!

     required init(coder aDecoder: NSCoder) {

         super.init(coder: aDecoder)
     }

     //- (void) setupInternalData
     init(data:MessageItem, reuseIdentifier cellId:String)
     {
         self.msgItem = data
         super.init(style: UITableViewCellStyle.Default, reuseIdentifier:cellId)
         rebuildUserInterface()
     }

     func rebuildUserInterface()
     {

         self.selectionStyle = UITableViewCellSelectionStyle.None
         if (self.bubbleImage == nil)
         {
             self.bubbleImage = UIImageView()
             self.addSubview(self.bubbleImage)

         }

         var type =  self.msgItem.mtype
         var width =  self.msgItem.view.frame.size.width

         var height =  self.msgItem.view.frame.size.height

         var x =  (type == ChatType.Someone) ?  : self.frame.size.width - width -
             self.msgItem.insets.left - self.msgItem.insets.right

         var y:CGFloat =
         //显示用户头像
         if (self.msgItem.logo != "")
         {

             var logo =  self.msgItem.logo

             self.avatarImage = UIImageView(image:UIImage(named:(logo != "" ? logo : "noAvatar.png")))

             self.avatarImage.layer.cornerRadius = 9.0
             self.avatarImage.layer.masksToBounds = true
             self.avatarImage.layer.borderColor = UIColor(white:0.0 ,alpha:0.2).CGColor
             self.avatarImage.layer.borderWidth = 1.0

             //别人头像,在左边,我的头像在右边
             var avatarX =  (type == ChatType.Someone) ?  : self.frame.size.width - 

             //头像居于消息底部
             var avatarY =  height
             //set the frame correctly
             self.avatarImage.frame = CGRectMake(avatarX, avatarY, , )
             self.addSubview(self.avatarImage)

             var delta =  self.frame.size.height - (self.msgItem.insets.top + self.msgItem.insets.bottom
                 + self.msgItem.view.frame.size.height)
             )
             {
                 y = delta
             }
             if (type == ChatType.Someone)
             {
                 x +=
             }
             if (type == ChatType.Mine)
             {
                 x -=
             }
         }

         self.customView = self.msgItem.view
         self.customView.frame = CGRectMake(x + self.msgItem.insets.left, y
             + self.msgItem.insets.top, width, height)

         self.addSubview(self.customView)

         //如果是别人的消息,在左边,如果是我输入的消息,在右边
         if (type == ChatType.Someone)
         {
             self.bubbleImage.image =
                 UIImage(named:(,topCapHeight:)

         }
         else {
             self.bubbleImage.image =
                 UIImage(named:, topCapHeight:)
         }
         self.bubbleImage.frame = CGRectMake(x, y, width + self.msgItem.insets.left
             + self.msgItem.insets.right, height + self.msgItem.insets.top + self.msgItem.insets.bottom)
     }
 }
(1)消息按天分组展示
(2)增加消息发送框,可以发送和展示消息
   

iOS开发——UI_swift篇&TableView自定义聊天界面的更多相关文章

  1. iOS开发——UI_swift篇&TableView实现页眉和页脚

    TableView实现页眉和页脚   在UItableView中header和footer是很常见的,而且他能让你实现很复杂的功能,我们见过最多的就是下拉刷新和上啦加载更多,当然你还可以在上面添加一个 ...

  2. iOS开发UI篇—Quartz2D(自定义UIImageView控件)

    iOS开发UI篇—Quartz2D(自定义UIImageView控件) 一.实现思路 Quartz2D最大的用途在于自定义View(自定义UI控件),当系统的View不能满足我们使用需求的时候,自定义 ...

  3. iOS开发多线程篇 11 —自定义NSOperation

    iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...

  4. IOS开发UI篇之──自定义UIActionSheet

    转载自:http://www.cnblogs.com/pengyingh/articles/2343200.html UIActionSheet类系IOS开发中实现警告框的重要的类,而在好多应用中,都 ...

  5. iOS开发——UI_swift篇&UITableView实现索引功能

    UITableView实现索引功能     关于UItableView的索引在平时项目中所见不多,最多的就是跟联系人有关的界面,虽然如此,但是作为一个swift开发的程序必须知道的一个技术点,所以今天 ...

  6. iOS开发——UI_swift篇&UITableView实现单元格展开与隐藏

    UITableView实现单元格展开与隐藏  关于UITableView的展开的收缩在前面的文章我已经结束,就是使用代理,通知,block传值的时候实现的,当时是使用一个Bool值来实现,最后使用着三 ...

  7. iOS开发——UI_swift篇&UItableView实现移动单元格

    UItableView实现移动单元格   1,下面的样例是给表格UITableView添加单元格移动功能: (1)给表格添加长按功能,长按后表格进入编辑状态 (2)在编辑状态下,可以看到单元格后面出现 ...

  8. iOS开发UI篇-tableView在编辑状态下的批量操作(多选)

    先看下效果图 直接上代码 #import "MyController.h" @interface MyController () { UIButton *button; } @pr ...

  9. IOS开发UI篇之──自定义加载等待框(MBProgressHUD)

    本文转载至 http://blog.csdn.net/xunyn/article/details/8064984   原文地址http://www.189works.com/article-89289 ...

随机推荐

  1. HDU 5762 Teacher Bo

    Teacher Bo Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Tota ...

  2. java中的getClass()函数

    Java反射学习 所谓反射,可以理解为在运行时期获取对象类型信息的操作.传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代 ...

  3. Android智能聊天机器人

    http://www.tuling123.com/       注册一个账号,申请一个KEY值.此网站也有文档,可以查看. package com.tulingdemo; import java.te ...

  4. UVA 11600-Masud Rana(状压,概率dp)

    题意: 有n个节点的图,开始有一些边存在,现在每天任意选择两点连一条边(可能已经连过),求使整个图联通的期望天数. 分析: 由于开始图可以看做几个连通分量,想到了以前做的一个题,一个点代表一个集合(这 ...

  5. web自动化框架之三获取数据库值与界面值比较~~

    数据库用到的是mysql,框架涉及数据库,主要包含两个方面,一个是每个案例执行完毕后,插入案例相关信息与数据:一个是web界面数据核对的时候,需要从sql中获取某行某列值与界面某个值做比较. 描述:w ...

  6. 在PC上测试移动端网站和模拟手机浏览器的5大方法

    在PC上测试移动端网站和模拟手机浏览器的5大方法 来源:互联网 作者:佚名 时间:03-19 10:14:54 [大 中 小]                最近公司要开发网站的移动版,让我准备准备知 ...

  7. 【JSONCpp】简介及demo

    一.JSON简介 JSON 一种轻量级的数据交换格式,易于阅读.编写.解析,全称为JavsScript ObjectNotation. JSON由两种基本结构组成 ①   名字/值 对的集合,可以理解 ...

  8. <转>配置DNS辅助服务器:DNS系列之四

    配置DNS辅助服务器   在前面的博文中,我们介绍了如何在DNS服务器中创建常用的DNS记录,本文中我们要为大家介绍如何配置DNS的辅助服务器,同时也要介绍一下和辅助区域类似的存根区域. DNS辅助服 ...

  9. g++安装 Gnome/Gtk+开发库 的 环境安装(安装widgets的必要条件)

    sudo yum install gcc-c++ 我在更新软件源后,看了看最新版的GTK开发版是3.0的. 所以,基本可以照搬. sudo yum -t install gtk3 sudo yum - ...

  10. C# 空值判断

    (1)NULL null 关键字是表示不引用任何对象的空引用的文字值.null 是引用类型变量的默认值.那么也只有引用型的变量可以为NULL,如果 int i=null,的话,是不可以的,因为Int是 ...