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

(1)消息可以是文本消息也可以是图片消息
(2)消息背景为气泡状图片,同时消息气泡可根据内容自适应大小
(3)每条消息旁边有头像,在左边表示发送方,在右边表示接收方
2,实现思路
(1)需要定义一个数据结构保存消息内容 MessageItem
(2)继承UITableViewCell实现自定义单元格,这里面放入头像和消息体
(3)继承UITableView实现自定义表格,通过读取数据源,进行页面的渲染
(4)消息体根据内容类型不同,用不同的展示方法
(5)每个单元格的高度需要根据内容计算出来
(6)数据由ViewController来提供初始化数据
3,效果图
4,代码结构
5,主要代码
(1)主页面 ViewController.swift
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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(0, 20,
            self.view.frame.size.width, self.view.frame.size.height - 20))
         
        //创建一个重用的单元格
        self.tableView!.registerClass(TableViewCell.self, forCellReuseIdentifier: "MsgCell")
         
        var me = "xiaoming.png"
         
        var you = "xiaohua.png"
         
        var first =  MessageItem(body:"嘿,这张照片咋样,我周末拍的呢!", logo:me,
            date:NSDate(timeIntervalSinceNow:-600), mtype:ChatType.Mine)
         
         
        var second =  MessageItem(image:UIImage(named:"luguhu.jpeg")!,logo:me,
            date:NSDate(timeIntervalSinceNow:-290), mtype:ChatType.Mine)
         
        var third =  MessageItem(body:"太赞了,我也想去那看看呢!",logo:you,
            date:NSDate(timeIntervalSinceNow:-60), mtype:ChatType.Someone)
         
         var fouth =  MessageItem(body:"嗯,下次我们一起去吧!",logo:me,
            date:NSDate(timeIntervalSinceNow:-20), mtype:ChatType.Mine)
         
        var fifth =  MessageItem(body:"好的,一定!",logo:you,
            date:NSDate(timeIntervalSinceNow:0), 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

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
    {
        return UIEdgeInsets(top:5, left:10, bottom:11, right:17)
    }
     
    //设置他人的文本消息边距
    class func getTextInsetsSomeone() -> UIEdgeInsets
    {
        return UIEdgeInsets(top:5, left:15, bottom:11, right:10)
    }
     
    //设置我的图片消息边距
    class func getImageInsetsMine() -> UIEdgeInsets
    {
        return UIEdgeInsets(top:11, left:13, bottom:16, right:22)
    }
     
    //设置他人的图片消息边距
    class func getImageInsetsSomeone() -> UIEdgeInsets
    {
        return UIEdgeInsets(top:11, left:13, bottom:16, right:22)
    }
     
    //构造文本消息体
    convenience init(body:NSString, logo:String, date:NSDate, mtype:ChatType)
    {
        var font =  UIFont.boldSystemFontOfSize(12)
         
        var width =  225, 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(0, 0, size.size.width, size.size.height))
         
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.ByWordWrapping
        label.text = (body.length != 0 ? 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
        //等比缩放
        if (size.width > 220)
        {
            size.height /= (size.width / 220);
            size.width = 220;
        }
        var imageView = UIImageView(frame:CGRectMake(0, 0, 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

1
2
3
4
5
6
7
8
9
10
11
12
import Foundation
/*
  数据提供协议
*/
protocol ChatDataSource
{  
    /*返回对话记录中的全部行数*/
    func rowsForChatTable( tableView:TableView) -> Int
    /*返回某一行的内容*/
    func chatTableView(tableView:TableView, dataForRow:Int)-> MessageItem
}

(4)自定义表格 TableView.swift

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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 =  0
        if ((self.chatDataSource != nil))
        {
            count = self.chatDataSource.rowsForChatTable(self)
             
            if(count > 0)
            {  
                 
                for (var i = 0; i < count; i++)
                {
                     
                    var object =  self.chatDataSource.chatTableView(self, dataForRow:i)
                    bubbleSection.append(object)
                     
                }
                 
                //按日期排序方法
                bubbleSection.sort({$0.date.timeIntervalSince1970 < $1.date.timeIntervalSince1970})
            }
        }
        super.reloadData()
    }
     
    //第一个方法返回分区数,在本例中,就是1
    func numberOfSectionsInTableView(tableView:UITableView)->Int
    {
        return 1
    }
     
    //返回指定分区的行数
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        if (section >= self.bubbleSection.count)
        {
            return 1
        }
         
        return self.bubbleSection.count+1
    }
         
    //用于确定单元格的高度,如果此方法实现得不对,单元格与单元格之间会错位
    func tableView(tableView:UITableView,heightForRowAtIndexPath indexPath:NSIndexPath) -> CGFloat
    {
         
        // Header
        if (indexPath.row == 0)
        {
            return 30.0
        }
         
        var data =  self.bubbleSection[indexPath.row - 1]
         
        return max(data.insets.top + data.view.frame.size.height + data.insets.bottom, 52)
    }
     
    //返回自定义的 TableViewCell
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
        -> UITableViewCell
    {
           
        var cellId = "MsgCell"
        if(indexPath.row > 0)
        {
            var data =  self.bubbleSection[indexPath.row-1]
         
            var cell =  TableViewCell(data:data, reuseIdentifier:cellId)
         
            return cell
        }
        else
        {
             
            return UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellId)
        }
    }
}

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

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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) ? 0 : self.frame.size.width - width -
            self.msgItem.insets.left - self.msgItem.insets.right
         
        var y:CGFloat =  0
        //显示用户头像
        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) ? 2 : self.frame.size.width - 52
             
            //头像居于消息底部
            var avatarY =  height
            //set the frame correctly
            self.avatarImage.frame = CGRectMake(avatarX, avatarY, 50, 50)
            self.addSubview(self.avatarImage)
             
             
            var delta =  self.frame.size.height - (self.msgItem.insets.top + self.msgItem.insets.bottom
                + self.msgItem.view.frame.size.height)
            if (delta > 0)
            {
                y = delta
            }
            if (type == ChatType.Someone)
            {
                x += 54
            }
            if (type == ChatType.Mine)
            {
                x -= 54
            }
        }
         
        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:("yoububble.png"))!.stretchableImageWithLeftCapWidth(21,topCapHeight:14)
             
        }
        else {
            self.bubbleImage.image =
                UIImage(named:"mebubble.png")!.stretchableImageWithLeftCapWidth(15, topCapHeight:14)
        }
        self.bubbleImage.frame = CGRectMake(x, y, width + self.msgItem.insets.left
            + self.msgItem.insets.right, height + self.msgItem.insets.top + self.msgItem.insets.bottom)
    }
}

6,源码下载:WeiXinChart.zip

7,功能改进版下载:WeiXinChart_advance.zip
(1)消息按天分组展示
(2)增加消息发送框,可以发送和展示消息

Swift - 自定义单元格实现微信聊天界面相关推荐

  1. 企业微信每日给女友推送早安,5分钟快速部署,腾讯云部署版本,每日定时发送,天气,鸡汤,纪念日等信息,可自定义通知提醒名称,聊天界面可置顶,内容可查图片。

    企业微信每日给女朋友推送早安,5分钟快速部署,每日定时发送,天气,鸡汤,纪念日等信息,可自定义通知提醒名称,聊天界面可置顶,内容可查图片. 先看效果 直接开搞 准备工作 1.企业id 2.企业应用se ...

  2. 【UIKit】表格自定义单元格(UITableViewCll)

    自定义表格单元格(Cell)操作 使用NIb文件自定义单元格(Cell) 1.          创建UITableViewCell的子类,创建的同时记得选择生成xib文件(当然你也可以将控件通过代码 ...

  3. qtableview设置单元格颜色_一键解锁Excel自定义单元格格式!

    实际工作中,常需要我们对单元格格式进行自定义设置,来突出显示我们需要看到的数据. 在自定义单元格格式时,我们常用如下方式先打开[设置单元格格式]对话框,再点击数字分类下的自定义: [开始]选项卡下的[ ...

  4. android 仿微信聊天界面 以及语音录制功能,Android仿微信录制语音功能

    本文实例为大家分享了Android仿微信录制语音的具体代码,供大家参考,具体内容如下 前言 我把录音分成了两部分 1.UI界面,弹窗读秒 2.一个类(包含开始.停止.创建文件名功能) 第一部分 由于6 ...

  5. android模拟微信聊天功能,android仿微信聊天界面 语音录制功能

    本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI. 1先看效果图: 第一:chat.xml设计 android:layout_width="fill_parent" and ...

  6. excel格式设置:自定义单元格让数据大变身

    "自定义单元格格式"允许用户创建一些特殊规则的格式,用于强调显示某些重要数据或信息.设置显示条件等.我相信大多数人都用过单元格格式设置,按快捷键ctrl+1就可以调出如下所示对话框 ...

  7. Excel 的自定义单元格格式

    Excel中可设置每个单元格显示数字和文本的格式: Excel设置单元格数字格式的界面 用来自定义单元格格式的字符串的完整格式为: [条件1][颜色1]数字格式1;[条件2][颜色2]数字格式2;[颜 ...

  8. php写的微信聊天界面,浅谈 聊天界面 核心架构设计

    本文以一个小例子简单的演示在微信小程序中使用环信SDK收发消息.官网demo 下载后把整个utils目录下的文件复制到咱自己工程的目录下.在WebIMConfig.js中将AppKey替换成自己应用的 ...

  9. iOS开发那些事--自定义单元格实现

    自定义单元格 当苹果公司提供给的单元格样式不能我们的业务需求的时候,我们需要自定义单元格.在iOS 5之前,自定义单元格可以有两种实现方式:代码实现和用xib技术实现.用xib技术实现相对比较简单,创 ...

最新文章

  1. 《学习opencv》笔记——矩阵和图像处理——cvMinManLoc,cvMul,cvNot,cvNorm and cvNormalize...
  2. 最新 MSDN Library for Visual Studio 2008 SP1
  3. 快速使用Tensorflow读取7万数据集!
  4. Google Analytics使用说明
  5. python获取页面隐藏元素_selenium操作隐藏的元素(python+Java)
  6. URI 和 URL 的区别
  7. amap vueamap 与_vue 使用高德地图vue-amap组件过程解析
  8. 数据挖掘与数据化运营实战. 3.2 目标客户的预测(响应、分类)模型
  9. python3入门代码-Python3 教程 | 菜鸟教程
  10. mysql 窗口函数_7、MySQL高级功能(窗口函数)
  11. vs配置opencv
  12. 最新Jrebel激活码,Jrebel激活激活服务,Jrebel激活码,Jrebel破解
  13. linux测试硬盘速度命令,如何测试Linux磁盘的读写速度
  14. c语言字体取模软件下载,非常好用的lcd汉字取模软件
  15. 计算机考研复试_数据库
  16. pdf转word ocr_最强PDF转WORD软件:ABBYY FineReader
  17. Linux 简介 ------ 带你简单了解Linux
  18. Google搜索又变聪明了 Baidu你还能HOLD住吗
  19. Docker Jar项目启动慢
  20. 中国没有真正意义上的海滩比基尼(图)

热门文章

  1. Java多线程系列——深入重入锁ReentrantLock
  2. 详细讲解Android的网络通信(HttpUrlConnection和HttpClient)
  3. 「OC」类的深入研究、description方法和sel
  4. ubuntu12.04安装教程
  5. VMware workstation 8.0上安装VMware ESXI5.0
  6. 【短语学习】盈余量分析(earned value analysis)
  7. 网站前端设计,从960框架开始
  8. Linux环境变量PSI指什么,PSI 文件扩展名: 它是什么以及如何打开它?
  9. 下滑加载更多js_专治:卫生间免砸砖,房顶漏水,JS堵漏王水不漏,厂家三包产品,免费成熟配方(点开看更多)...
  10. 五大板块(1)—— 数组的定义,赋值与应用