模拟简化的qq聊天的界面
需要注意的有:计算字符串所占的大小,扩展的使用,图片的 stretchableImageWithLeftCapWidth
方法的使用,通知的使用,键盘的通知事件的使用, TableView的一些方法和属性
代码
数据模型类
//
// Message.swift
// QQ
//
// Created by zhang on 16/1/7.
// Copyright © 2016年 jin. All rights reserved.
//
//时间lable字体大小
let timeFontSize:CGFloat = 15
//文本lable字体大小
let textFontSize:CGFloat = 16
//按钮内边距
let buttonEdgeInsets:CGFloat = 20
import UIKit
class Message: NSObject {
var text:NSString!
var time:NSString!
var type:NSNumber!
{
//变量发生改变之后执行这个方法
didSet
{
if self.type == 0
{
self.icon = "me"
}
else
{
self.icon = "other"
}
}
}
var hiddenTime:Bool = true
var icon:String!
convenience init(dic:[String : AnyObject]) {
self.init()
self.setValuesForKeysWithDictionary(dic)
}
}
cell位置信息类
//
// MessageFrame.swift
// QQ
//
// Created by zhang on 16/1/7.
// Copyright © 2016年 jin. All rights reserved.
//
import UIKit
class MessageFrame: NSObject {
let iconFrame:CGRect
let timeFrame:CGRect
let textFrame:CGRect
let cellHeight:CGFloat
let message:Message
//快速实例话的方法
class func instanceWithFile()->[MessageFrame]
{
let path = NSBundle.mainBundle().pathForResource("messages", ofType: "plist")
let data = NSArray(contentsOfFile: path!)
var messageFrames:[MessageFrame] = []
//这个变量用于记录上一个消息时间,用这个判断当前messageCell是否显示时间lable
var flag:NSString = ""
for var i = 0;i < data?.count;i++
{
let message = Message(dic: data![i] as! [String : AnyObject])
//判断是否显示时间
if flag.isEqualToString(message.time as String)
{
message.hiddenTime = false
}
flag = message.time
messageFrames.append(MessageFrame.init(message: message))
}
return messageFrames
}
convenience init(message:Message) {
let margin:CGFloat = 10
//计算头像frame
var iconFrame:CGRect = CGRect.zero
let iconW:CGFloat = 50
let iconH:CGFloat = 50
var iconX:CGFloat = margin
let iconY:CGFloat = margin
//当是自己的消息的时候,更改坐标位置
if message.type == 0
{
iconX = UIScreen.mainScreen().bounds.size.width - margin - iconW
}
iconFrame = CGRectMake(iconX, iconY, iconW, iconH)
//时间lable
var timeFrame:CGRect = CGRect.zero
if message.hiddenTime
{
let timeW = UIScreen.mainScreen().bounds.size.width
//计算文字所需要的大小
let timeH = message.time.boundingRectWithSize(CGSizeMake(CGFloat.max, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(timeFontSize)], context: nil).size.height
timeFrame = CGRectMake(0, 0, timeW, timeH)
}
//消息Button
//计算消息文本需要的大小
let textSize = message.text.boundingRectWithSize(CGSize(width: UIScreen.mainScreen().bounds.size.width - (iconFrame.size.width * 2) - (margin * 4),height: CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(textFontSize)], context: nil).size
var textX = CGRectGetMaxX(iconFrame) + margin
let textY = CGRectGetMaxY(timeFrame) + margin
//当是自己的消息的时候,更改横坐标
if message.type == 0
{
textX = iconFrame.origin.x - textSize.width - margin - (buttonEdgeInsets * 2)
}
//因为设置了Button内边距,所以需要加上内边距
let textFrame = CGRectMake(textX, textY, textSize.width + (buttonEdgeInsets * 2), textSize.height + 40)
//cell高度
let cellHeight = (CGRectGetMaxY(textFrame) > CGRectGetMaxY(iconFrame) ? CGRectGetMaxY(textFrame) : CGRectGetMaxY(iconFrame)) + margin
//执行构造方法
self.init(iconFrame:iconFrame,timeFrame:timeFrame,textFrame:textFrame,cellHeight:cellHeight,message:message)
}
init(iconFrame:CGRect,timeFrame:CGRect,textFrame:CGRect,cellHeight:CGFloat,message:Message) {
self.iconFrame = iconFrame
self.timeFrame = timeFrame
self.textFrame = textFrame
self.cellHeight = cellHeight
self.message = message
}
}
自定义的cell模型类
//
// MessageCell.swift
// QQ
//
// Created by zhang on 16/1/7.
// Copyright © 2016年 jin. All rights reserved.
//
import UIKit
class MessageCell: UITableViewCell {
var messageFrame:MessageFrame!
{
didSet
{
self.assignSubViewsContent()
self.assignSubViewsFrame()
}
}
var iconView:UIImageView!
var timeView:UILabel!
var textView:UIButton!
//便捷构造器
class func instanceWithTableView(tableView:UITableView)->MessageCell
{
let identifier = "qqcell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier)
if cell == nil
{
cell = MessageCell(style: UITableViewCellStyle.Default, reuseIdentifier: identifier)
}
let temp = cell as! MessageCell
return temp
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
//设置头像
self.iconView = UIImageView()
self.iconView.layer.cornerRadius = 25
self.iconView.layer.masksToBounds = true
//设置时间
self.timeView = UILabel()
self.timeView.font = UIFont.systemFontOfSize(timeFontSize)
self.timeView.textAlignment = NSTextAlignment.Center
//设置内容
self.textView = UIButton()
self.textView.titleLabel?.numberOfLines = 0
self.textView.titleLabel?.font = UIFont.systemFontOfSize(textFontSize)
self.textView.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
self.textView.titleEdgeInsets = UIEdgeInsets(top: buttonEdgeInsets, left: buttonEdgeInsets, bottom: buttonEdgeInsets, right: buttonEdgeInsets)
//添加进父控件
self.addSubview(self.iconView)
self.addSubview(self.timeView)
self.addSubview(self.textView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//给子控件赋值
func assignSubViewsContent()
{
self.iconView.image = UIImage(imageLiteral: self.messageFrame.message.icon)
self.timeView.text = self.messageFrame.message.time as String
self.textView.setTitle(self.messageFrame.message.text as String, forState: UIControlState.Normal)
//好友和自己发送的图片是不一样的,这例做下处理
var normalImageName = ""
var higlightedImageName = ""
if self.messageFrame.message.type == 0
{
normalImageName = "chat_send_nor"
higlightedImageName = "chat_send_press_pic"
}
else
{
normalImageName = "chat_recive_nor"
higlightedImageName = "chat_recive_press_pic"
}
//实例化图片对象并调用扩展的方法的到需要的图片
let normalImage = UIImage(imageLiteral: normalImageName).changeImage()
let highlightedImage = UIImage(imageLiteral: higlightedImageName).changeImage()
//设置不同状态的背景图片
self.textView.setBackgroundImage(normalImage, forState: UIControlState.Normal)
self.textView.setBackgroundImage(highlightedImage, forState: UIControlState.Highlighted)
//清除当前cell背景颜色
self.backgroundColor = UIColor.clearColor()
//设置消息主题内容
self.textView.setTitle(self.messageFrame.message.text as String, forState: UIControlState.Highlighted)
}
//设置位置
func assignSubViewsFrame()
{
self.iconView.frame = self.messageFrame.iconFrame
self.timeView.frame = self.messageFrame.timeFrame
self.textView.frame = self.messageFrame.textFrame
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
扩展image
//
// ImageExtension.swift
// QQ
//
// Created by admin on 16/1/8.
// Copyright © 2016年 jin. All rights reserved.
//
import Foundation
import UIKit
//扩展 UIImage
extension UIImage
{
func changeImage()->UIImage
{
//这个方法处理之后的图片在改变大小的时候边缘不会变化
return self.stretchableImageWithLeftCapWidth(Int(self.size.width / 2), topCapHeight: Int(self.size.height / 2))
}
}
最后就是在控制器中的调用了
//
// ViewController.swift
// QQ
//
// Created by zhang on 16/1/7.
// Copyright © 2016年 jin. All rights reserved.
//
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate {
lazy var messageFrames:[MessageFrame] = MessageFrame.instanceWithFile()
@IBOutlet var sendMessageView: UIView!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//去掉cell的分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
self.tableView.allowsSelection = false
//设置背景色
self.tableView.backgroundColor = UIColor(red: 125/255, green: 125/255, blue: 125/255, alpha: 1)
// Do any additional setup after loading the view, typically from a nib.
//订阅键盘位置改变的通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardChange:", name: UIKeyboardWillChangeFrameNotification, object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
//
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messageFrames.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = MessageCell.instanceWithTableView(self.tableView)
cell.messageFrame = self.messageFrames[indexPath.row]
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.messageFrames[indexPath.row].cellHeight
}
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
return nil
}
// table代理方法
func scrollViewDidScroll(scrollView: UIScrollView) {
self.view.endEditing(true)
}
func keyboardChange(not:NSNotification)
{
//键盘位置没有发生变化的时候的位置信息,注意这里的转换
// let beginRect = not.userInfo!["UIKeyboardFrameEndUserInfoKey"]!.CGRectValue
//也可以直接取得,不过是一个可选类型
let beginRect = not.userInfo![UIKeyboardFrameEndUserInfoKey]!.CGRectValue
//键盘位置没有发生变化后的位置信息
let endRect = not.userInfo!["UIKeyboardFrameBeginUserInfoKey"]!.CGRectValue
let y = beginRect.origin.y - endRect.origin.y
//动画效果,注意这里的类型转换
UIView.animateWithDuration(NSTimeInterval(not.userInfo![UIKeyboardAnimationDurationUserInfoKey]!.floatValue), animations: {
self.view.transform = CGAffineTransformTranslate(self.view.transform, 0, y)
})
}
//文章本框键盘发送方法
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.sendMessage(textField.text!, type: 0)
//使用dispatch延时发送恢复消息
let minseconds = 2 * Double(NSEC_PER_SEC)
let dtime = dispatch_time(DISPATCH_TIME_NOW, Int64(minseconds))
dispatch_after(dtime, dispatch_get_main_queue(), {
self.sendMessage("哈哈哈",type: 1)
})
return true
}
//发送消息
func sendMessage(text:NSString,type:NSNumber)
{
//初始化信息数据
let message = Message()
message.text = text
message.type = type
//获取时间
let data = NSDate()
let dataFormat = NSDateFormatter()
dataFormat.dateFormat = "HH:mm"
message.time = dataFormat.stringFromDate(data)
//判断是否显示时间 lable
if message.time.isEqualToString((self.messageFrames.last?.message.time)! as String)
{
message.hiddenTime = false
}
//添加进数据来源数组
self.messageFrames.append(MessageFrame.init(message: message))
//获得消息的 indexPath
let indexPath = NSIndexPath(forRow: self.messageFrames.count - 1, inSection: 0)
//插入cell
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
//动画效果
UIView.animateWithDuration(2, animations: {
//滚动到当前发送的信息位置
self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
})
}
}