zoukankan      html  css  js  c++  java
  • Swift

    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)增加消息发送框,可以发送和展示消息

       

  • 相关阅读:
    软件工程实践2017结对项目——第一次作业
    软件工程实践2017第二次作业
    软件工程实践2017第一次作业
    [LeetCode] 72. Edit Distance(最短编辑距离)
    [LeetCode] 342. Power of Four(位操作)
    [LeetCode] 477. Total Hamming Distance(位操作)
    [LeetCode] 421. Maximum XOR of Two Numbers in an Array(位操作)
    [LeetCode] 260. Single Number III(位操作)
    [LeetCode] 137. Single Number II (位操作)
    IntelliJ IDEA快捷键
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4838230.html
Copyright © 2011-2022 走看看