自定义单元格实现微信聊天界面

屏幕快照 2017-03-07 下午2.24.10.png

ViewController

import UIKit

class ViewController: UIViewController,chatDataSourch {

    var tableView :TableView!
    var messageArr :Array<messageModel>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        buildTable()
        
        
        
        
    }
    func buildTable()
    {
        self.tableView = TableView(frame: CGRect(x: 0, y: 20, width: self.view.frame.size.width, height: self.view.frame.height-20), style: .plain)
        self.tableView.register(tableViewCell.self, forCellReuseIdentifier: "GPLcell")
        
        let me = "xiaoming.png"
        let you = "xiaohua.png"
        
        let first =  messageModel(body:"嘿,这张照片咋样,我周末拍的呢!", logo:me,
                                 date:NSDate(timeIntervalSinceNow:-600), mtype:chatType.Mine)
        
        let second = messageModel(img: UIImage(named:"luguhu.jpeg")!, logo: me, date: NSDate(timeIntervalSinceNow:-290), mtype: chatType.Mine)
        
        let third =  messageModel(body:"太赞了,我也想去那看看呢!",logo:you,
                                 date:NSDate(timeIntervalSinceNow:-60), mtype:chatType.Other)
        
        let fouth =  messageModel(body:"嗯,下次我们一起去吧!",logo:me,
                                 date:NSDate(timeIntervalSinceNow:-20), mtype:chatType.Mine)
        
        let fifth =  messageModel(body:"好的,一定!",logo:you,
                                 date:NSDate(timeIntervalSinceNow:0), mtype:chatType.Other)
        self.messageArr = [first,second,third,fouth,fifth]
        
        //数据协议
        //代理方法
        self.tableView.chatDataSource = self
        
        self.tableView.reloadData()
        self.view.addSubview(self.tableView)
        
    }
    //实现chatDataSourch代理方法
    func rowsForChatTable(tableView: TableView) -> Int {
        return self.messageArr.count
    }
    func chatTableView(tableView: TableView, dataForRow: Int) -> messageModel {
        return self.messageArr[dataForRow]
        
    }

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


}

messageModel

import Foundation

import UIKit

//枚举,判断当前消息用户
enum chatType
{
    case Mine
    case Other
}

class messageModel {
    //头像
    var logo:String
    //日期
    var date:NSDate
    //消息类型
    var mytype :chatType
    //内容视图
    var view :UIView
    //边距
    var insets:UIEdgeInsets
    
    /*
     class func 类方法
     
     */
    //设置我的文本消息边距
    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)
    }
    init(logo:String, date:NSDate, mytype:chatType, view:UIView, insets:UIEdgeInsets) {
        self.view = view
        self.logo = logo
        self.date = date
        self.mytype = mytype
        self.insets = insets
    }
    /*
     convenience 优先级比较低
      所有的 convenience 初始化方法都必须调用同一个类中的 designated 初始化完成设置
     */
    //文字消息
    convenience init(body:NSString, logo:String, date:NSDate, mtype:chatType)
    {
        let font = UIFont.boldSystemFont(ofSize: 12)
        
        let width =  225.0, height = 10000.0
        
        let atts =  [NSFontAttributeName: font]
        
        //计算字符串的高度
        let size = body.boundingRect(with:CGSize(width: width, height: height), options: .usesLineFragmentOrigin, attributes: atts, context: nil)
        
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.size.width, height: size.size.height))
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.byWordWrapping
        label.text = (body.length != 0 ? body as String : "")
        label.font = font
        label.backgroundColor = UIColor.clear
        
        let insets1:UIEdgeInsets =  (mtype == chatType.Mine ?
            messageModel.getTextInsetsMine() : messageModel.getTextInsetsSomeone())

        self.init(logo:logo,date:date,mytype:mtype,view:label,insets:insets1)
        
    }
    //图片消息
    convenience init(img:UIImage, logo:String, date:NSDate, mtype:chatType)
    {
        var size = img.size
        //等比缩放
        if size.width>220 {
            size.height = (size.height)/(size.width / 220)
            size.width=220
        }
        let imageView = UIImageView(frame:CGRect(x: 0, y: 0, width: size.width, height: size.height))
        imageView.image = img
        imageView.layer.cornerRadius = 5.0
        imageView.layer.masksToBounds = true
        
        let insets:UIEdgeInsets =  (mtype == chatType.Mine ?
            messageModel.getImageInsetsMine() : messageModel.getImageInsetsSomeone())
        
        self.init(logo:logo,date:date,mytype:mtype,view:imageView,insets:insets)
    }
    
    
    
}

chatDataSourch

import Foundation
import UIKit
//数据协议
protocol chatDataSourch
{
    /*返回对话记录中的全部行数*/
    func rowsForChatTable( tableView:TableView) -> Int
    /*返回某一行的内容*/
    func chatTableView(tableView:TableView, dataForRow:Int)-> messageModel
}
/*
 
 protocol Student {
 //类方法
 static func study()
 //实例方法
 func changeName()
 }
 struct CollageStudent: Student {
 //类方法实现
 static func study() {
 }
 //实例方法实现
 func changeName() {
 }
 }
 //方法的调用
 CollageStudent.study()
 var c1 = CollageSt
 
 */

tableViewCell

import Foundation
import UIKit

class tableViewCell: UITableViewCell {
    //消息内容视图
    var customView:UIView!
    //消息背景
    var bubbleImage:UIImageView!
    //头像
    var avatarImage:UIImageView!
    //消息数据结构
    var msgModel:messageModel!
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init(data:messageModel,reuseIdentifier cellId:String) {
        self.msgModel = data
        super.init(style: .default, reuseIdentifier: cellId)
        rebuildUserInterface()
    }
    func rebuildUserInterface()
    {
        self.selectionStyle = UITableViewCellSelectionStyle.none
        if (self.bubbleImage == nil)
        {
            self.bubbleImage = UIImageView()
            self.addSubview(self.bubbleImage)
        }
        let type =  self.msgModel.mytype
        let width =  self.msgModel.view.frame.size.width
        
        let height =  self.msgModel.view.frame.size.height
        
        var x =  (type == chatType.Other) ? 0 : self.frame.size.width - width -
            self.msgModel.insets.left - self.msgModel.insets.right
        
        var y:CGFloat =  0
        
        if self.msgModel.logo != ""
        {
            let logo =  self.msgModel.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
            
            //别人头像,在左边,我的头像在右边
            let avatarX =  (type == chatType.Other) ? 2 : self.frame.size.width - 52
            
            //头像居于消息底部
            let avatarY =  height
            //set the frame correctly
            self.avatarImage.frame = CGRect(x: avatarX, y: avatarY, width: 50, height: 50)
            self.addSubview(self.avatarImage)
            
            let delta =  self.frame.size.height - (self.msgModel.insets.top
                + self.msgModel.insets.bottom + self.msgModel.view.frame.size.height)
            if (delta > 0) {
                y = delta
            }
            if (type == chatType.Other) {
                x += 54
            }
            if (type == chatType.Mine) {
                x -= 54
            }
        }
        self.customView = self.msgModel.view

        self.customView.frame = CGRect(x: (x + self.msgModel.insets.left), y: (y + self.msgModel.insets.top),  width: width, height: height)
        
        self.addSubview(self.customView)
        
        //如果是别人的消息,在左边,如果是我输入的消息,在右边
        if (type == chatType.Other)
        {
            //拉伸图片
            self.bubbleImage.image = UIImage(named:("yoububble.png"))!
                .stretchableImage(withLeftCapWidth: 21,topCapHeight:14)
            
        }
        else {
            self.bubbleImage.image = UIImage(named:"mebubble.png")!
                .stretchableImage(withLeftCapWidth: 15, topCapHeight:14)
        }

        self.bubbleImage.frame = CGRect(x: x, y: y, width: (width + self.msgModel.insets.left + self.msgModel.insets.right), height: (height + self.msgModel.insets.top + self.msgModel.insets.bottom))
        
    }
    //让单元格宽度始终为屏幕宽
    override var frame: CGRect {
        get {
            return super.frame
        }
        set (newFrame) {
            var frame = newFrame
            frame.size.width = UIScreen.main.bounds.width
            super.frame = frame
        }
    }
        

    
    
    
}

TableView

import Foundation
import UIKit

class TableView: UITableView,UITableViewDelegate,UITableViewDataSource {
    //存储消息
    var bubbleSection:Array<messageModel>!
    //数据源,用于与 ViewController 交换数据
    var chatDataSource:chatDataSourch!
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    //override覆盖  重写初始化方法
    override init(frame: CGRect, style: UITableViewStyle) {
        //初始化
        super.init(frame: frame, style: style)
        self.bubbleSection = Array<messageModel>()
        self.backgroundColor = UIColor.clear
        self.separatorStyle = UITableViewCellSeparatorStyle.none
        self.delegate = self
        self.dataSource = self
    }
    
    //重写reloadData方法
    override func reloadData() {
        //设置不显示滚动条
        self.showsVerticalScrollIndicator = false
        self.showsHorizontalScrollIndicator = false
        
        var count = 0
        if self.chatDataSource != nil {
            count = self.chatDataSource.rowsForChatTable(tableView: self)
            
            if count>0 {
                for i in 0..<count {
                    let object = self.chatDataSource.chatTableView(tableView: self, dataForRow: i)
                    self.bubbleSection.append(object)
                }
                //按日期排序方法
                bubbleSection.sort(by: {
                    $0.date.timeIntervalSince1970 < $1.date.timeIntervalSince1970
                })
            }
        }
        super.reloadData()
    }
    //区数
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if (section >= self.bubbleSection.count)
        {
            return 0
        }
        
        return self.bubbleSection.count
    }
    
    //用于确定单元格的高度,如果此方法实现得不对,单元格与单元格之间会错位
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let data =  self.bubbleSection[indexPath.row]
        return max(data.insets.top + data.view.frame.size.height + data.insets.bottom, 52)

    }

    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellID = "GPLcell"
        let data = self.bubbleSection[indexPath.row]
        
        let cell = tableViewCell(data: data, reuseIdentifier: cellID)
        return cell
        
        
    }
        
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容