【swift】吹き出しでLINEぽいメッセージアプリのUIを作ったのでメモ
はじめに
今回やりたかったのはこんなかんじの画面です。
iosで吹き出しのライブラリとか実装方法とか検索するとちょいちょい出てくるのですが、ちょっと難しかったりで結局自分で実装することにしました。
最初は吹き出しの画像を用意してそれを背景にしたUILabelとかで実装しようとしたのですが、なんかきれいにできず画像なしでつくることにしました。
UITableViewを使って、1メッセージを1行にするようなイメージです。
実装方法
あんまりスマートなやり方ではないのですが、こんなかんじでUIViewの中にUILabelをいれて吹き出しの部分は三角形の線を引いたUIViewを重ねてそれっぽくさせてみました。
ViewController.swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { var tableView: UITableView! var tableData: [Dictionary<String,AnyObject>]! var heightLeftCell: CustomLeftTableViewCell = CustomLeftTableViewCell() var heightRightCell: CustomRightTableViewCell = CustomRightTableViewCell() override func viewDidLoad() { super.viewDidLoad() // データを初期化します tableData = [ ["name": "にゃんこ1", "type": 1, "image": "image1.jpg", "created": "今日", "message": "おはようございます\nねむいにゃん"], ["name": "にゃんこ2", "type": 2, "image": "image2.jpg", "created": "今日", "message": "おはよー\nさむくなってきたね"], ・・・ ] // VIEWをセットします setView() } // VIEWをセットします func setView() { let statusBarHeight: CGFloat = UIApplication.sharedApplication().statusBarFrame.height let displayWidth = self.view.frame.width let displayHeight = self.view.frame.height tableView = UITableView(frame: CGRect(x:0, y:statusBarHeight, width:displayWidth, height:displayHeight - statusBarHeight)) tableView.registerClass(CustomLeftTableViewCell.self, forCellReuseIdentifier: "CustomLeftTableViewCell") tableView.registerClass(CustomRightTableViewCell.self, forCellReuseIdentifier: "CustomRightTableViewCell") tableView.delegate = self tableView.dataSource = self tableView.separatorColor = UIColor.clearColor() self.view.addSubview(tableView) } // テーブルセルの高さをかえします func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if 1 == self.tableData[indexPath.row]["type"] as! Int { return heightLeftCell.setData(tableView.frame.size.width - 20, data: self.tableData[indexPath.row]) } else { return heightRightCell.setData(tableView.frame.size.width - 20, data: self.tableData[indexPath.row]) } } // テーブルセルにデータをセットします func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { if 1 == self.tableData[indexPath.row]["type"] as! Int { var cell = tableView.dequeueReusableCellWithIdentifier("CustomLeftTableViewCell", forIndexPath: indexPath) as! CustomLeftTableViewCell cell.setData(tableView.frame.size.width - 20, data: self.tableData[indexPath.row]) return cell } else { var cell = tableView.dequeueReusableCellWithIdentifier("CustomRightTableViewCell", forIndexPath: indexPath) as! CustomRightTableViewCell cell.setData(tableView.frame.size.width - 20, data: self.tableData[indexPath.row]) return cell } } //・・・UITableViewに必要なメソッドとか }
CustomLeftTableViewCell.swift(1メッセージを1セルで)
class CustomLeftTableViewCell: UITableViewCell { var icon: UIImageView = UIImageView() // アイコン var name: UILabel = UILabel() // 名前 var created: UILabel = UILabel() // 投稿日時 var arrow: CustomLeftArrow = CustomLeftArrow() // 吹き出しの突起の部分 var message: UILabel = UILabel() // 吹き出しの文字を表示している部分 var viewMessage: UIView = UIView() // 吹き出しの枠部分 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.selectionStyle = UITableViewCellSelectionStyle.None } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) self.selectionStyle = UITableViewCellSelectionStyle.None self.viewMessage.layer.borderColor = UIColor.grayColor().CGColor self.viewMessage.layer.borderWidth = 0.5 self.viewMessage.layer.cornerRadius = 10.0 self.viewMessage.backgroundColor = UIColor.yellowColor() self.message.font = UIFont.systemFontOfSize(14) self.message.numberOfLines = 0 self.message.backgroundColor = UIColor.greenColor() self.viewMessage.addSubview(self.message) self.contentView.addSubview(self.viewMessage) self.arrow.backgroundColor = UIColor.whiteColor() self.arrow.backgroundColor = UIColor.redColor() self.addSubview(self.arrow) self.icon.layer.borderColor = UIColor.blackColor().CGColor self.icon.layer.borderWidth = 0.5 self.addSubview(self.icon) self.name.font = UIFont.boldSystemFontOfSize(13) self.name.textAlignment = NSTextAlignment.Left self.addSubview(self.name) self.created.font = UIFont.systemFontOfSize(13) self.created.textAlignment = NSTextAlignment.Right self.addSubview(self.created) } func setData(widthMax: CGFloat,data: Dictionary<String,AnyObject>) -> CGFloat { var marginLeft: CGFloat = 60 var marginRight: CGFloat = 0 var marginVertical: CGFloat = 30 // アイコン var xIcon: CGFloat = 3 var yIcon: CGFloat = 3 var widthIcon: CGFloat = 48 var heightIcon: CGFloat = 48 self.icon.frame = CGRectMake(xIcon, yIcon, widthIcon, heightIcon) self.icon.image = UIImage(named: (data["image"] as? String)!) self.icon.layer.cornerRadius = self.icon.frame.size.width * 0.5 self.icon.clipsToBounds = true // 名前 var xName: CGFloat = self.icon.frame.origin.x + self.icon.frame.size.width + 3 var yName: CGFloat = self.icon.frame.origin.y var widthName: CGFloat = widthMax - (self.icon.frame.origin.x + self.icon.frame.size.width + 3) var heightName: CGFloat = 30 self.name.text = data["name"] as? String self.name.frame = CGRectMake(xName, yName, widthName, heightName) // 投稿日時 var xCreated: CGFloat = self.icon.frame.origin.x + self.icon.frame.size.width + 3 var yCreated: CGFloat = self.icon.frame.origin.y var widthCreated: CGFloat = widthMax - (self.icon.frame.origin.x + self.icon.frame.size.width + 10) var heightCreated: CGFloat = 30 self.created.text = data["created"] as? String self.created.frame = CGRectMake(xCreated, yCreated, widthCreated, heightCreated) var paddingHorizon: CGFloat = 10 var paddingVertical: CGFloat = 10 var widthLabelMax: CGFloat = widthMax - (marginLeft + marginRight + paddingHorizon * 2) var xMessageLabel: CGFloat = paddingHorizon var yMessageLabel: CGFloat = paddingVertical self.message.frame = CGRectMake(xMessageLabel, yMessageLabel, widthLabelMax, 0) self.message.text = data["message"] as? String self.message.sizeToFit() var xMessageView: CGFloat = marginLeft var yMessageView: CGFloat = marginVertical var widthMessageView: CGFloat = self.message.frame.size.width + paddingHorizon * 2 var heightMessageView: CGFloat = self.message.frame.size.height + paddingVertical * 2 self.viewMessage.frame = CGRectMake(xMessageView, yMessageView, widthMessageView, heightMessageView) var widthArrow: CGFloat = 10 var heightArrorw: CGFloat = 10 var xArrow: CGFloat = marginLeft - widthArrow + 1 var yArrow: CGFloat = self.viewMessage.frame.origin.y + heightArrorw self.arrow.frame = CGRectMake(xArrow, yArrow, widthArrow, heightArrorw) var height: CGFloat = self.viewMessage.frame.height + marginVertical * 2 return height } }
CustomLeftArrow.swift(吹き出しの突起部分)
class CustomLeftArrow: UIView { override func drawRect(rect: CGRect) { var x: CGFloat = 0.0 var y: CGFloat = rect.size.height / 2 var x2: CGFloat = rect.size.width var y2: CGFloat = 0.0 var x3: CGFloat = rect.size.width var y3: CGFloat = rect.size.height UIColor.blackColor().setStroke() var line = UIBezierPath() line.lineWidth = 0.3 line.moveToPoint(CGPointMake(x, y)) line.addLineToPoint(CGPointMake(x2, y2)) line.stroke() var line2 = UIBezierPath() line2.lineWidth = 0.3 line2.moveToPoint(CGPointMake(x3, y3)) line2.addLineToPoint(CGPointMake(x, y)) line2.stroke() } }
とりあえず左側の部分だけになりますがこんなかんじで実装しました。
実装方法はスマートじゃないけど、なんとなくLINEぽくなってよかったにゃん。
以上です