Swift - 自定义单元格实现微信聊天界面
1,下面是一个放微信聊天界面的消息展示列表,实现的功能有:
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
Swift - 自定义单元格实现微信聊天界面的更多相关文章
- 浅谈DevExpress<五>:TreeList简单的美化——自定义单元格,加注释以及行序号
今天就以昨天的列表为例,实现以下效果:预算大于110万的单元格突出显示,加上行序号以及注释,如下图:
- android 仿微信聊天界面,以及语音录制功能
extends:http://104zz.iteye.com/blog/1709840 本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI. 1先看效果图: 第一:chat.xml设计 ...
- HTML5仿手机微信聊天界面
HTML5仿手机微信聊天界面 这篇文章主要为大家详细介绍了HTML5仿手机微信聊天界面的关键代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 给大家带来的是HTML5仿手机微信聊天界面, ...
- jQuery MiniUI自定义单元格
监听处理"drawcell"事件 使用"drawcell"事件,可以自定义单元格内容.样式.行样式等. grid.on("drawcell" ...
- Android:日常学习笔记(8)———开发微信聊天界面
Android:日常学习笔记(8)———开发微信聊天界面 只做Nine-Patch图片 Nine-Patch是一种被特殊处理过的PNG图片,能够指定哪些区域可以被拉升,哪些区域不可以.
- 使用VUE组件创建SpreadJS自定义单元格(一)
作为近五年都冲在热门框架排行榜首的Vue,大家一定会学到的一部分就是组件的使用.前端开发的模块化,可以让代码逻辑更加简单清晰,项目的扩展性大大加强.对于Vue而言,模块化的体现集中在组件之上,以组件为 ...
- 使用VUE组件创建SpreadJS自定义单元格(二)
在上篇中,我们介绍了如何通过设置runtimeCompiler为true,在Vue中实现了动态创建电子表格组件.想了解具体内容可看点击查看使用VUE组件创建SpreadJS自定义单元格(一). 但是在 ...
- 剖析:如何用 SwitchUI 5天写一个微信 —— 聊天界面篇
前置资源 GitHub: SwiftUI-WeChatDemo 第零章:用 SwiftUI 五天组装一个微信 - wavky - 博客园 整体结构 UI 部分代码分布如上图所示,App 的主入口类为 ...
- NPOI 自定义单元格背景颜色-Excel
NPOI针对office2003使用HSSFWorkbook,对于offce2007及以上使用XSSFWorkbook:今天我以HSSFWorkbook自定义颜色为例说明,Office2007的未研究 ...
随机推荐
- 最新Cocos2d-x3.2开发环境搭建(windows环境下)
原文地址:http://cache.baiducontent.com/c?m=9d78d513d9921cfe05ac837f7d16c067690297634d9dc7150ed58449e3735 ...
- 信号与槽(可以与第三方库混用,首次见到QObject::destroyed的使用)
信号与槽用于对象之间的通信.信号与槽机制是Qt的核心特性和区别于其他框架的特性. Introduction 在GUI程序中,当我们改变一个widget,经常需要其他的widget得到通知.更普遍的是, ...
- QLinkedList和std::forward_list(都是双向链表,不支持operator[],好处可能是插入和删除都比较快)
forward_list forward_list是C++11版本才有的.forward_list被实现为单链表,而list是一个双向链表,所以forward_list要比list高效一些.forwa ...
- Linux命令之chown
chown 更改文件全部者和组 语法: chown [OPTION] [OWNER][:[GROUP]] FILE chown [OPTION] --reference=RFILE FILE 描写 ...
- 读TIJ -2 一切都是对象
<第2 章一切都是对象> 1.一切都是对象.不是Bruce Eckel说的,而是Alan Kay 总结的Smalltalk 五大基本特征的第一条. 从程序设计者或源码的角度,我觉得:&qu ...
- Session 转台服务器的使用方法
Session的缺陷:为了保持自身的稳定,IIS在访问量大的时候,可能会不自觉的重启,这时候Session就会丢失用户就会被迫下线 解决方案1:将Session放到一个专门的转台服务器上 方案2:将S ...
- wcf 出现 IsContentTypeSupported 错误
查看添加的服务地址是不是https开头的,而 *.config 文件里面自动添加的链接变成了http,当前的bindbing类型为basicHttpBinding, 解决方法:在config文件里面手 ...
- premake在Ubuntu和GCC环境下创建简单的C++工程
由于premake基于lua脚本,为了方便编辑lua脚本,我在emacs24中利用package system安装了lua-mode. 然后创建config.lua文件,填入下面这段,主要来自:htt ...
- CentOS下利用sshpass不用手动输入密码远程执行命令
在测试的时候要同时操作多台机器,每次都要挨个去执行几乎相同的命令或者修改一些设置,这样很影响工作效率也很烦,所以就想写一个脚本,远程自动去做这些操作.远程执行命令很简单,但是不能在执行命令加上命 ...
- libevent简单介绍和使用
<pre class="html" name="code">libevent接口的使用是简单easy的.关键还是一些其他技术须要深入了解.如epol ...