發(fā)布微博
課程目標
- 界面搭建
- 自定義文字輸入框
- 自定義顯示照片的View
- 底部 toolBar 自定義(UIStackView)
- 表情鍵盤(一個復雜的自定義 View 如何一步一步實現(xiàn)出來的)
界面搭建
導航欄內容
- 標題視圖懶加載
// MARK: - 懶加載
/// 頂部標題視圖
private lazy var titleView: UILabel = {
let label = UILabel()
// 設置多行
label.numberOfLines = 0
// 字體大小
label.font = UIFont.systemFontOfSize(14)
// 文字居中
label.textAlignment = NSTextAlignment.Center
// 如果有用戶昵稱
if let name = HMUserAccountViewModel.sharedInstance.userAccount?.name {
// 初始化一個帶有屬性的文字
var attr = NSMutableAttributedString(string: "發(fā)微博\n\(name)")
// 獲取到要添加的屬性的范圍
let range = (attr.string as NSString).rangeOfString(name)
// 添加屬性
attr.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(12), range: range)
attr.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGrayColor() ,range: range)
label.attributedText = attr
}else{
label.text = "發(fā)微博"
}
label.sizeToFit()
return label
}()
- 右邊按鈕懶加載
/// 右邊按鈕
private lazy var rightButton: UIButton = {
let button = UIButton()
// 添加點擊事件
button.addTarget(self, action: "send", forControlEvents: UIControlEvents.TouchUpInside)
// 設置文字屬性
button.titleLabel?.font = UIFont.systemFontOfSize(13)
button.setTitle("發(fā)送", forState: UIControlState.Normal)
// 設置不同狀態(tài)的文字
button.setTitleColor(UIColor.grayColor(), forState: UIControlState.Disabled)
button.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
// 設置不同狀態(tài)的背景圖片
button.setBackgroundImage(UIImage(named: "common_button_white_disable"), forState: UIControlState.Disabled)
button.setBackgroundImage(UIImage(named: "common_button_orange"), forState: UIControlState.Normal)
button.setBackgroundImage(UIImage(named: "common_button_orange_highlighted"), forState: UIControlState.Highlighted)
// 設置寬高
button.height = 30
button.width = 44
return button
}()
- 實現(xiàn)
send
方法
@objc private func send(){
printLog("發(fā)送")
}
- 設置導航欄內容
// 設置導航欄內容
private func setupNav(){
// 設置左邊 Item
navigationItem.leftBarButtonItem = UIBarButtonItem.item(title: "返回", target: self, action: "back")
// 設置中間 titleView
navigationItem.titleView = titleView
// 設置右邊 Item
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: rightButton)
// 默認為不可用狀態(tài)
navigationItem.rightBarButtonItem?.enabled = false
}
運行測試
文字輸入框
- 帶有占位文字
- 可以像 UITextView 一樣輸入多行
- 自定義一個輸入框繼承于 UITextView亩冬,向里面添加一個 label
- 代碼實現(xiàn)
class HMTextView: UITextView {
/// 重寫的是指定構造函數(shù)
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
// 添加占位控件
addSubview(placeholderLabel)
// 添加約束
placeholderLabel.snp_makeConstraints { (make) -> Void in
make.width.lessThanOrEqualTo(self.snp_width).offset(-10)
make.leading.equalTo(self.snp_leading).offset(5)
make.top.equalTo(self.snp_top).offset(8)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 占位文字控件
private lazy var placeholderLabel: UILabel = {
let label = UILabel()
// 設置文字顏色以及大小
label.font = UIFont.systemFontOfSize(12)
label.textColor = UIColor.lightGrayColor()
label.text = "請輸入文字"
// 多行
label.numberOfLines = 0
return label
}()
}
- 添加到 controller 中使用
// 懶加載控件
private lazy var textView: HMTextView = {
let textView = HMTextView()
return textView
}()
// setupUI 方法中添加子控件并設置約束
view.addSubview(textView)
textView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view.snp_edges)
}
運行測試
- 在
HMTextView
中提供給外界設置占位文字的屬性
// 添加 placeholder 屬性,代外界設置值
var placeholder: String? {
didSet{
placeholderLabel.text = placeholder
}
}
- 重寫
font
屬性茉盏,以讓占位文字與輸入的文字字體大小一樣
override var font: UIFont? {
didSet{
placeholderLabel.font = font
}
}
- 外界設置文字大小
textView.font = UIFont.systemFontOfSize(16)
運行測試:占位文字與輸入的文字一樣大
- 監(jiān)聽文字改變的時候鉴未,去執(zhí)行占位控件的隱藏與顯示邏輯
// 監(jiān)聽文字改變的通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "textDidChange", name: UITextViewTextDidChangeNotification, object: self)
- 文字改變之后調用的方法
/// 文字改變的時候會調用這個方法,當前如果有文字的話就隱藏占位 label
@objc private func textDidChange(){
placeholderLabel.hidden = hasText()
}
運行測試鸠姨。注:監(jiān)聽文字改變在這個地方不要使用代理铜秆,因為自己一般不成為自己的代理。
底部 ToolBar 初始化
//設置約束
toolBar.snp_makeConstraints { (make) -> Void in
make.left.right.bottom.equalTo(self.view)
}
var items = [UIBarButtonItem]()
//添加 UIBarButtonItem類型的對象到數(shù)據(jù)源數(shù)組中
let itemSettings = [["imageName": "compose_toolbar_picture","actionName": "selectPicture"],
["imageName": "compose_mentionbutton_background"],
["imageName": "compose_trendbutton_background"],
["imageName": "compose_emoticonbutton_background", "actionName": "selectEmoticon"],
["imageName": "compose_add_background"]]
for item in itemSettings {
let imageName = item["imageName"]
let btn = UIButton()
btn.setImage(UIImage(named: imageName!), forState: .Normal)
btn.setImage(UIImage(named: imageName! + "_highlighted"), forState: .Highlighted)
btn.sizeToFit()
if let actionName = item["actionName"] {
btn.addTarget(self, action: Selector(actionName), forControlEvents: .TouchUpInside)
}
let barItem = UIBarButtonItem(customView: btn)
//添加到數(shù)組中
items.append(barItem)
//添加彈簧類型的item FlexibleSpace: 可伸縮的彈簧
let space = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
items.append(space)
}
items.removeLast()
toolBar.items = items
- 在
HMComposeViewController
中懶加載控件
/// composeToolBar
private lazy var composeToolBar: HMComposeToolBar = HMComposeToolBar(frame: CGRectZero)
- 在
HMComposeViewController
的setupUI
方法中添加控件與約束
view.addSubview(composeToolBar)
// 添加約束
composeToolBar.snp_makeConstraints { (make) -> Void in
make.bottom.equalTo(self.view.snp_bottom)
make.width.equalTo(self.view.snp_width)
make.height.equalTo(44)
}
底部 ToolBar 跟隨鍵盤移動
- 監(jiān)聽鍵盤 frame 改變通知
// 監(jiān)聽鍵盤 frame 改變通知
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillChangeFrame:", name: UIKeyboardWillChangeFrameNotification, object: nil)
- 注銷通知
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
- 在鍵盤 frame 改變做更新約束的邏輯
/// 鍵盤 frame 改變通知調用的方法
@objc private func keyboardWillChangeFrame(noti: NSNotification){
let endFrame = (noti.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
// 更新約束
composeToolBar.snp_updateConstraints { (make) -> Void in
make.bottom.equalTo(self.view.snp_bottom).offset(endFrame.origin.y - self.view.height)
}
UIView.animateWithDuration(0.25) { () -> Void in
self.composeToolBar.layoutIfNeeded()
}
}
- 拖動 textView 的時候退下鍵盤:打開 textView 垂直方向彈簧效果讶迁,并設置代理
textView.alwaysBounceVertical = true
textView.delegate = self
- 實現(xiàn)協(xié)議连茧,并實現(xiàn)協(xié)議方法
func scrollViewDidScroll(scrollView: UIScrollView) {
self.view.endEditing(true)
}
- 實現(xiàn)
textViewDidChange
的方法,當textView有文字輸入的時候右邊按鈕可用
func textViewDidChange(textView: UITextView) {
//設置占位的文本的隱藏或者顯示
placeholderLabel.hidden = textView.hasText()
//設置 發(fā)布按鈕的 交互 和不可交互狀態(tài)
//有文本就允許交互
navigationItem.rightBarButtonItem?.enabled = textView.hasText()
}
選擇照片
目標
- 在獨立的項目中開發(fā)獨立的功能,或者直接切換根控制器
- 開發(fā)完畢后再整合到現(xiàn)有項目中
- 提高工作效率巍糯,專注開發(fā)品質 ??
- 選擇照片
- 重建控件布局
項目準備
- 新建文件夾
PictureSelector
- 新建
PictureSelectorViewController
繼承自UICollectionViewController
- 在
AppDelegate
添加以下代碼
window?.rootViewController = PictureSelectorViewController()
運行測試
代碼實現(xiàn)
設置布局
- 添加控制器構造函數(shù)啸驯,簡化外部調用
/// 可重用標識符號
private let WBPictureSelectorViewCellID = "WBPictureSelectorViewCellID"
/// 照片選擇控制器
class PictureSelectorViewController: UICollectionViewController {
// MARK: - 構造函數(shù)
init() {
let layout = UICollectionViewFlowLayout()
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// 注冊可重用 Cell
self.collectionView!.registerClass(UICollectionViewCell.self,
forCellWithReuseIdentifier: WBPictureSelectorViewCellID)
}
}
- 設置背景顏色
collectionView?.backgroundColor = UIColor.lightGrayColor()
注意在
CollectionViewController
中,collectionView
不是view
- 修改數(shù)據(jù)源方法
// MARK: 數(shù)據(jù)源
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(WBPictureSelectorViewCellID, forIndexPath: indexPath)
// Configure the cell
cell.backgroundColor = UIColor.redColor()
return cell
}
- 設置 cell 尺寸
init() {
let layout = UICollectionViewFlowLayout()
// 屏幕越大祟峦,顯示的內容應該越多
layout.itemSize = CGSize(width: 80, height: 80)
layout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 0, right: 20)
super.init(collectionViewLayout: layout)
}
從 iPhone 6 開始罚斗,就需要考慮越大的屏幕顯示越多的內容
自定義 Cell
添加素材
自定義 Cell
/// 照片選擇單元格
private class PictureSelectorViewCell: UICollectionViewCell {
// MARK: - 構造函數(shù)
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 設置界面
private func setupUI() {
// 添加控件
contentView.addSubview(addButton)
contentView.addSubview(removeButton)
// 自動布局
addButton.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(contentView.snp_edges)
}
removeButton.snp_makeConstraints { (make) -> Void in
make.top.equalTo(contentView.snp_top)
make.right.equalTo(contentView.snp_right)
}
}
// MARK: - 懶加載控件
/// 添加按鈕
private var addButton = UIButton(imageName: "compose_pic_add", backImageName: nil)
/// 刪除按鈕
private var removeButton = UIButton(imageName: "compose_photo_close", backImageName: nil)
}
- 修改注冊的 Cell
// 注冊可重用 Cell
collectionView!.registerClass(PictureSelectorViewCell.self,
forCellWithReuseIdentifier: WBPictureSelectorViewCellID)
- 修改數(shù)據(jù)源
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(WBPictureSelectorViewCellID, forIndexPath: indexPath) as! PictureSelectorViewCell
- 按鈕監(jiān)聽方法
// MARK: - 監(jiān)聽方法
/// 添加照片
@objc private func addPicture() {
print("添加照片")
}
/// 刪除照片
@objc private func removePicture() {
print("刪除照片")
}
- 添加監(jiān)聽方法
// 監(jiān)聽方法
addButton.addTarget(self, action: "addPicture", forControlEvents: .TouchUpInside)
removeButton.addTarget(self, action: "removePicture", forControlEvents: .TouchUpInside)
利用代理傳遞按鈕點擊事件
- 定義協(xié)議傳遞消息
/// 照片選擇單元格代理
private protocol PictureSelectorViewCellDelegate: NSObjectProtocol {
/// 添加照片
func pictureSelectorViewCellDidAdd(cell: PictureSelectorViewCell)
/// 刪除照片
func pictureSelectorViewCellDidRemove(cell: PictureSelectorViewCell)
}
- 設置代理
/// 照片選擇代理
weak var pictureDelegate: PictureSelectorViewCellDelegate?
- 修改監(jiān)聽方法
// MARK: - 監(jiān)聽方法
/// 添加照片
@objc private func addPicture() {
pictureDelegate?.pictureSelectorViewCellDidAdd(self)
}
/// 刪除照片
@objc private func removePicture() {
pictureDelegate?.pictureSelectorViewCellDidRemove(self)
}
- 在
extension
中實現(xiàn)協(xié)議方法
// MARK: - PictureSelectorViewCellDelegate
extension PictureSelectorViewController: PictureSelectorViewCellDelegate {
private func pictureSelectorViewCellDidAdd(cell: PictureSelectorViewCell) {
print("添加照片")
}
private func pictureSelectorViewCellDidRemove(cell: PictureSelectorViewCell) {
print("刪除照片")
}
}
- 在數(shù)據(jù)源方法中設置代理
cell.pictureDelegate = self
注意:如果協(xié)議是私有的,那么協(xié)議方法也必須是私有的
選擇照片
- 判斷是否支持訪問相冊
// 添加照片
private func pictureSelectorViewCellDidAdd(cell: PictureSelectorViewCell) {
// 判斷是否支持照片選擇
if !UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) {
print("無法訪問照片庫")
return
}
}
- 訪問相冊
// 訪問相冊
let picker = UIImagePickerController()
presentViewController(picker, animated: true, completion: nil)
- 設置代理
// 設置代理
picker.delegate = self
- 遵守協(xié)議并實現(xiàn)方法
// MARK: - UIImagePickerControllerDelegate, UINavigationControllerDelegate
extension PictureSelectorViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
/// 選中媒體代理方法
///
/// - parameter picker: 照片選擇器
/// - parameter info: 信息字典 allowsEditing = true 適合選擇頭像
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
print(info)
dismissViewControllerAnimated(true, completion: nil)
}
}
注意:一旦實現(xiàn)了代理方法宅楞,則需要用代碼
dismiss
控制器
設置圖片數(shù)據(jù)源
- 定義照片數(shù)組
/// 照片數(shù)組
private lazy var pictures = [UIImage]()
- 在代理方法中插入照片
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
pictures.append(image)
collectionView?.reloadData()
dismissViewControllerAnimated(true, completion: nil)
- 在 cell 中添加
image
屬性
/// 照片
private var image: UIImage? {
didSet {
addButton.setImage(image, forState: .Normal)
}
}
- 修改數(shù)據(jù)源中的圖像數(shù)量函數(shù)
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pictures.count + 1
}
保證末尾有一個加號按鈕添加照片
- 在數(shù)據(jù)源方法中設置圖像
cell.image = (indexPath.item == pictures.count) ? nil : pictures[indexPath.item]
- 擴展
image
屬性的didSet
函數(shù)
/// 照片
private var image: UIImage? {
didSet {
addButton.setImage(image ?? UIImage(named: "compose_pic_add"), forState: .Normal)
addButton.setImage(image ?? UIImage(named: "compose_pic_add_highlighted"), forState: .Highlighted)
}
}
細節(jié)處理
記錄用戶點擊按鈕的索引
- 定義當前選中照片索引
/// 當前選中照片索引
private var currentIndex = 0
- 在代理方法中記錄當前用戶點擊 cell 的索引
// 記錄當前用戶選中索引
currentIndex = collectionView!.indexPathForCell(cell)!.item
- 在照片選擇控制器的代理方法中設置對應的圖像
if currentIndex < pictures.count {
pictures[currentIndex] = image
} else {
pictures.append(image)
}
collectionView?.reloadData()
設置照片填充模式
// 設置照片填充模式
addButton.imageView?.contentMode = .ScaleAspectFill
刪除照片
- 刪除照片操作
// 刪除照片
private func pictureSelectorViewCellDidRemove(cell: PictureSelectorViewCell) {
guard let indexPath = collectionView?.indexPathForCell(cell) else {
return
}
pictures.removeAtIndex(indexPath.item)
collectionView?.deleteItemsAtIndexPaths([indexPath])
}
- 默認隱藏刪除按鈕
/// 照片
private var image: UIImage? {
didSet {
addButton.setImage(image ?? UIImage(named: "compose_pic_add"), forState: .Normal)
addButton.setImage(image ?? UIImage(named: "compose_pic_add_highlighted"), forState: .Highlighted)
removeButton.hidden = (image == nil)
}
}
設置最多選擇照片數(shù)量
- 定義最多照片常量
/// 最大照片數(shù)量
private let WBPictureSelectorViewMaxPictureCount = 9
- 修改數(shù)據(jù)源方法
// MARK: 數(shù)據(jù)源
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pictures.count + (pictures.count < WBPictureSelectorViewMaxPictureCount ? 1 : 0)
}
內存處理
- 縮放圖片
extension UIImage {
/// 將圖像縮放到指定寬度
///
/// - parameter width: 指定寬度针姿,如果圖片尺寸比指定寬度小袱吆,直接返回
///
/// - returns: 等比例縮放后的圖像
func scaleImage(width: CGFloat) -> UIImage {
// 1. 判斷圖像尺寸
if size.width < width {
return self
}
// 2. 計算比例
let height = size.height * width / size.width
let rect = CGRect(x: 0, y: 0, width: width, height: height)
// 3. 核心繪圖
// 1> 開啟上下文
UIGraphicsBeginImageContext(rect.size)
// 2> 繪圖
drawInRect(rect)
// 3> 獲得結果
let result = UIGraphicsGetImageFromCurrentImageContext()
// 4> 關閉上下文
UIGraphicsEndImageContext()
// 5> 返回結果
return result
}
}
- 修改照片選擇控制器代理方法
/// 選中媒體代理方法
///
/// - parameter picker: 照片選擇器
/// - parameter info: 信息字典 allowsEditing = true 適合選擇頭像
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
let scaleImage = image.scaleImage(300)
if currentIndex < pictures.count {
pictures[currentIndex] = scaleImage
} else {
pictures.append(scaleImage)
}
collectionView?.reloadData()
dismissViewControllerAnimated(true, completion: nil)
}
整合照片選擇控制器
準備 文件
- 將
PhotoSelector
拖拽至項目 - 將
UIImage+Extension.swift
拖拽至項目
整合照片選擇控制器
- 定義控制器屬性
/// 照片選擇控制器
private lazy var pictureSelectorViewController = PictureSelectorViewController()
- 準備照片視圖
/// 準備照片視圖
private func preparePictureView() {
// 添加視圖
view.addSubview(pictureSelectorViewController.view)
// 自動布局
pictureSelectorViewController.view.snp_makeConstraints { (make) -> Void in
make.bottom.equalTo(view.snp_bottom)
make.left.equalTo(view.snp_left)
make.right.equalTo(view.snp_right)
make.height.equalTo(view.snp_height).multipliedBy(0.6)
}
}
運行測試,發(fā)現(xiàn)選中照片結束后距淫,提示錯誤:
Presenting view controllers on detached view controllers is discouraged
- 添加子控制器
// 添加子控制器
addChildViewController(pictureSelectorViewController)
- 修改照片選擇視圖層次
// 添加視圖
view.insertSubview(pictureSelectorViewController.view, belowSubview: toolbar)
運行會發(fā)現(xiàn)照片選擇視圖跑到了 textView 和 toolBar 的后面
重建控件布局
- 修改照片選擇視圖的高度
make.height.equalTo(0)
- 在選擇照片監(jiān)聽方法中重建控件索引
// 選擇照片
@objc private func selectPhoto() {
if pictureSelectorViewController.view.bounds.height == 0 {
// 修改布局高度
pictureSelectorViewController.view.snp_remakeConstraints { (make) -> Void in
make.bottom.equalTo(view.snp_bottom)
make.left.equalTo(view.snp_left)
make.right.equalTo(view.snp_right)
make.height.equalTo(view.snp_height).multipliedBy(0.6)
}
// 修改文本視圖的底部約束
textView.snp_remakeConstraints { (make) -> Void in
make.top.equalTo(view.snp_top)
make.left.equalTo(view.snp_left)
make.right.equalTo(view.snp_right)
make.bottom.equalTo(pictureSelectorViewController.view.snp_top)
}
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
}
- 關閉鍵盤
textView.resignFirstResponder()
運行測試
- 調整
viewDidAppear
如果已經(jīng)顯示照片選擇視圖绞绒,則不再激活鍵盤
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if pictureSelectorViewController.imageList.count == 0 {
textView.becomeFirstResponder()
}
}
發(fā)布微博
發(fā)布文字微博
接口定義
-
文檔地址
-
接口地址
-
HTTP 請求方式
- POST
請求參數(shù)
參數(shù) | 說明 |
---|---|
access_token | 采用OAuth授權方式為必填參數(shù),其他授權方式不需要此參數(shù)榕暇,OAuth授權后獲得 |
status | 要發(fā)布的微博文本內容蓬衡,必須做URLencode,內容不超過140個漢字 |
連續(xù)兩次發(fā)布的微博不可以重復
- 在
HMNetworkTools
中添加update
方法
/// 發(fā)布文字微博
func update(accessToken: String, text: String, finished: HMRequestCallBack){
// 請求地址
let urlString = "https://api.weibo.com/2/statuses/update.json"
// 請求參數(shù)
let params = [
"access_token": accessToken,
"status": text
]
request(.POST, url: urlString, params: params, finished: finished)
}
- 在
HMComposeViewController
中調用
/// 發(fā)送文字微博
private func update(){
HMNetworkTools.shareTools.update(HMUserAccountViewModel.sharedUserAccount.accessToken!, text: textView.emoticonText) { (result, error) -> () in
if error != nil {
print(error)
SVProgressHUD.showErrorWithStatus("發(fā)表失敗")
return
}
print(result)
SVProgressHUD.showSuccessWithStatus("發(fā)表成功")
}
}
發(fā)布圖片微博
接口定義
文檔地址
http://open.weibo.com/wiki/2/statuses/upload
接口地址
https://upload.api.weibo.com/2/statuses/upload.json
HTTP 請求方式
- POST
請求參數(shù)
參數(shù) | 說明 |
---|---|
access_token | 采用OAuth授權方式為必填參數(shù)彤枢,其他授權方式不需要此參數(shù)狰晚,OAuth授權后獲得 |
status | 要發(fā)布的微博文本內容,必須做URLencode堂污,內容不超過140個漢字 |
pic | 要上傳的圖片家肯,僅支持JPEG龄砰、GIF狠角、PNG格式鼻吮,圖片大小小于5M |
請求必須用POST方式提交,并且注意采用multipart/form-data編碼方式
代碼實現(xiàn)
- 在
HMNetworkTools
中添加上傳圖片的方法
func upload(accessToken: String, text: String, image: UIImage, finished: HMRequestCallBack){
// url
let url = "https://upload.api.weibo.com/2/statuses/upload.json"
let params = [
"access_token": accessToken,
"status": text
]
POST(url, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
let data = UIImagePNGRepresentation(image)!
/**
1. data: 二進制數(shù)據(jù)
2. name: 服務器定義的字段名稱
3. fileName: 保存在服務器的文件名,通炒移可以亂寫,服務器自己會做處理
4. mimeType: 告訴服務器文件類型
- 大類型 / 小類型
image/jepg, image/png
text/plain, text/html
- 如果不想告訴服務器準確類型:
application/octet-stream
*/
formData.appendPartWithFileData(data, name: "pic", fileName: "xxaaa", mimeType: "image/jpeg")
}, success: { (_, response) -> Void in
guard let dict = response as? [String: AnyObject] else {
// 如果不是字典她混,返回錯誤
let error = NSError(domain: "com.itheima.error", code: -1001, userInfo: ["message": "The response data type isn't a [String: AnyObject]"])
finished(result: nil, error: error)
return
}
finished(result: dict, error: nil)
}) { (_, error) -> Void in
finished(result: nil, error: error)
}
}
表情鍵盤
在實際開發(fā)中對于比較獨立的模塊,可以直接新建一個項目,在新項目上演練,測試,等待模塊開發(fā)完畢之后再移植到項目中,方便項目的測試
實現(xiàn)效果
實現(xiàn)思路
- 從最簡單的 View 開始做起
- 底部切換表情類型的 View 可以使用
UIStackView
來實現(xiàn) - 表情顯示的 View 可以使用
UICollectionView
實現(xiàn) - 每一頁表情對應一個
Cell
來表示 - 每一種表情對應
UICollectionView
中的一組
自定義 HMEmoticonKeyboard
- 自定義
HMEmoticonKeyboard
繼承于UIView
class HMEmoticonKeyboard: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
private func setupUI(){
// 設置背景顏色
backgroundColor = UIColor(patternImage: UIImage(named: "emoticon_keyboard_background")!)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 在
HMComposeViewController
中添加切換鍵盤的方法switchKeyboard
/// 切換鍵盤
private func switchKeyboard(){
}
- 在點擊
HMComposeToolBar
上的表情按鈕的時候調用方法
// MARK: - HMComposeToolBarDelegate
func composeToolBarButtonDidSelected(type: ComposeToolBarButtonType) {
switch type {
case ...
case .Emoticon:
switchKeyboard()
}
}
- 懶加載鍵盤
/// 鍵盤
private lazy var emoticonKeyboard: HMEmoticonKeyboard = {
let keyboard = HMEmoticonKeyboard()
keyboard.size = CGSizeMake(SCREENW, 216)
return keyboard
}()
表情類型切換視圖
- 自定義
HMEmoticonToolBar
class HMEmoticonToolBar: UIStackView {
override init(frame: CGRect) {
super.init(frame: frame)
// 設置布局方向
axis = UILayoutConstraintAxis.Horizontal
// 設置子控件的分布方式 -> 填充拳话,大小相等
distribution = UIStackViewDistribution.FillEqually
setupUI()
}
private func setupUI(){
// 添加 4 個按鈕
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 提供添加 3 個按鈕的方法
private func addChildItem(title: String, bgImageName: String) {
let button = UIButton()
// 設置文字以及字體大小
button.titleLabel?.font = UIFont.systemFontOfSize(14)
button.setTitle(title, forState: UIControlState.Normal)
// 設置不同狀態(tài)的背景圖片
button.setBackgroundImage(UIImage(named: "\(bgImageName)_normal"), forState: UIControlState.Normal)
button.setBackgroundImage(UIImage(named: "\(bgImageName)_selected"), forState: UIControlState.Selected)
// 設置不同狀態(tài)的文字顏色
button.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
button.setTitleColor(UIColor.grayColor(), forState: UIControlState.Selected)
addArrangedSubview(button)
}
- 在
setupUI
中添加按鈕
private func setupUI(){
// 添加 3 個按鈕
addChildItem("默認", bgImageName: "compose_emotion_table_right")
addChildItem("Emoji", bgImageName: "compose_emotion_table_mid")
addChildItem("浪小花", bgImageName: "compose_emotion_table_right")
}
- 在
HMEmoiticonKeyboard
中添加HMEmoticonToolBar
// 懶加載控件
/// 底部切換表情類型的toolBar
private lazy var emoticonToolBar: HMEmoticonToolBar = HMEmoticonToolBar(frame: CGRectZero)
- 在
setupUI
方法中添加控件以及約束
// 添加子控件
addSubview(emoticonToolBar)
// 添加約束
emoticonToolBar.snp_makeConstraints { (make) -> Void in
make.bottom.equalTo(self.snp_bottom)
make.leading.equalTo(self.snp_leading)
make.right.equalTo(self.snp_right)
make.height.equalTo(37)
}
運行測試:按鈕背景圖片沒有拉伸方式有問題
-
更改拉伸方式:點擊Assets.xcassets --> 選中對應的背景圖片 --> 查看右邊屬性面板 --> 在
Slicing
區(qū)設置Slices
為Horizontal
,設置center
為Stretches
- 有些情況下 Xcode 會出 Bug夕玩,需要設置
Slices
為Horizontal And Vertical
- 有些情況下 Xcode 會出 Bug夕玩,需要設置
監(jiān)聽子按鈕點擊
private func addChildItem(title: String, bgImageName: String) {
let button = UIButton()
// 添加點擊事件
button.addTarget(self, action: "childButtonClick:", forControlEvents: UIControlEvents.TouchUpInside)
...
}
- 實現(xiàn)響應方法
/// 子控件點擊
///
/// - parameter button: 當前點擊的 button
@objc private func childButtonClick(button: UIButton){
// 按鈕點擊方法
}
-
實現(xiàn)選中一個按鈕的時候取消選中之前的按鈕
- 記錄當前選中的按鈕
- 當點擊下一個按鈕的時候取消選中記錄的按鈕你弦,選中當前按鈕
- 再次記錄當前選中的按鈕
定義
currentSelectedButton
屬性記錄當前選中的按鈕
/// 當前選中的按鈕
var currentSelectedButton: UIButton?
- 在
childButtonClick
實現(xiàn)按鈕點擊邏輯
/// 子按鈕點擊
///
/// - parameter button: 當前點擊的 button
@objc private func childButtonClick(button: UIButton){
// 如果當前選中的 button 與即將要選中的button相同,則直接返回
if button == currentSelectedButton {
return
}
// 取消選中之前的
currentSelectedButton?.selected = false
// 選中現(xiàn)在點擊的
button.selected = true
// 再次記錄現(xiàn)在選的按鈕
currentSelectedButton = button
}
運行測試
- 按鈕點擊的時候需要讓
HMEmoticonKeyboard
知道哪一個按鈕點擊了- 給按鈕添加tag
- 添加協(xié)議燎孟,在按鈕點擊的時候調用協(xié)議方法
- 更新
setupUI
方法中調用方式
private func setupUI(){
// 添加 3 個按鈕
addChildItem("默認", bgImageName: "compose_emotion_table_left", index: 0)
addChildItem("Emoji", bgImageName: "compose_emotion_table_mid", index: 1)
addChildItem("浪小花", bgImageName: "compose_emotion_table_right", index: 2)
}
- 定義協(xié)議
protocol HMEmoticonToolBarDelegate: NSObjectProtocol {
func emoticonToolBarButtonDidSelected(index: Int)
}
- 添加代理屬性
/// 代理
weak var delegate: HMEmoticonToolBarDelegate?
- 在按鈕點擊的時候調用代理身上的方法
/// 子按鈕點擊
///
/// - parameter button: 當前點擊的 button
@objc private func childButtonClick(button: UIButton){
...
// 調用代理方法
delegate?.emoticonToolBarButtonDidSelected(button.tag)
}
-
HMEmoticonKeyboard
繼承HMEmoticonToolBarDelegate
協(xié)議
class HMEmoticonKeyboard: UIView, HMEmoticonToolBarDelegate {
...
}
- 在
HMEmoticonKeyboard
中設置HMEmoticonToolBar
的代理為自己
/// 底部切換表情類型的toolBar
private lazy var emoticonToolBar: HMEmoticonToolBar = {
let toolBar = HMEmoticonToolBar(frame: CGRectZero)
toolBar.delegate = self
return toolBar
}()
- 實現(xiàn)代理方法
// MARK: - HMEmoticonToolBarDelegate
func emoticonToolBarButtonDidSelected(index: Int) {
print(index)
}
運行測試
表情顯示視圖
- 在
HMEmoticonKeyboard
中添加UICollectionView
/// 懶加載控件
/// 顯示表情的視圖
private lazy var emoticonCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = RandomColor()
return collectionView
}()
- 添加控件與約束
// 添加子控件
addSubview(emoticonCollectionView)
// 添加約束
emoticonCollectionView.snp_makeConstraints { (make) -> Void in
make.width.equalTo(self.snp_width)
make.top.equalTo(self.snp_top)
make.bottom.equalTo(self.emoticonToolBar.snp_top)
make.leading.equalTo(self)
}
運行測試
- 設置
emoticonCollectionView
的數(shù)據(jù)源以及注冊 cell
/// 顯示表情的視圖
private lazy var emoticonCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = RandomColor()
// 設置數(shù)據(jù)源
collectionView.dataSource = self
// 注冊 cell
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: HMEmoticonKeyboardCellId)
return collectionView
}()
- 繼承協(xié)議
class HMEmoticonKeyboard: UIView, HMEmoticonToolBarDelegate, UICollectionViewDataSource {
...
}
- 實現(xiàn)協(xié)議方法
extension HMEmoticonKeyboard {
/// 返回表情一共有多少頁
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// 為了測試禽作,先默認返回10個
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(HMEmoticonKeyboardCellId, forIndexPath: indexPath)
// 測試返回隨機顏色
cell.backgroundColor = RandomColor()
return cell
}
}
運行測試:
- 調整每一個 cell 的大小
- 因為每一個
cell
的大小與collectionView
一樣大 - 而調整完 collectionView 大小要調用的方法就是
layoutSubviews
- 所以在
layoutSubviews
調整每一個 cell 的大小
- 因為每一個
override func layoutSubviews() {
super.layoutSubviews()
// 設置每一個 cell 的大小
let layout = emoticonCollectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = emoticonCollectionView.size
}
運行測試:每一行之間有間距,而且滾動方向不對
- 在初始化
emoticonCollectionView
的時候設置滾動方向以及 cell 間距 (UICollectionViewFlowLayout
身上的屬性)
/// 顯示表情的視圖
private lazy var emoticonCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
// 設置滾動方向:水平滾動
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
// 設置每一個 cell 之間的間距
layout.minimumLineSpacing = 0
let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout)
collectionView.backgroundColor = RandomColor()
// 設置數(shù)據(jù)源
collectionView.dataSource = self
// 注冊 cell
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: HMEmoticonKeyboardCellId)
return collectionView
}()
- 開啟分頁 & 隱藏水平滾動條 & 關閉彈簧效果 (
UIScrollView
身上的屬性)
// 開啟分頁 & 隱藏水平滾動條
collectionView.pagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
// 關閉彈簧效果
collectionView.bounces = false
- 自定義
HMEmoticonPageCell
為表情鍵盤的 Cell
class HMEmoticonPageCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI(){
backgroundColor = RandomColor()
}
}
- 替換注冊的 cell
// 注冊 cell
collectionView.registerClass(HMEmoticonPageCell.self, forCellWithReuseIdentifier: HMEmoticonKeyboardCellId)
- 為了測試效果揩页,在
HMEmoticonPageCell
中添加一個測試的 label
class HMEmoticonPageCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
private func setupUI(){
contentView.addSubview(label)
label.snp_makeConstraints { (make) -> Void in
make.center.equalTo(contentView.snp_center)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 測試用的 label
private lazy var label: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(35)
return label
}()
}
- 提供
indexPath: NSIndexPath
屬性旷偿,顯示當前滾動到哪個位置
var indexPath: NSIndexPath? {
didSet{
label.text = "第\(indexPath!.section)組,第\(indexPath!.item)頁"
}
}
- 在返回 cell 的時候設置
indexPath
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(HMEmoticonKeyboardCellId, forIndexPath: indexPath) as! HMEmoticonPageCell
// 測試返回隨機顏色
cell.backgroundColor = RandomColor()
cell.indexPath = indexPath
return cell
}
運行測試
讀取表情數(shù)據(jù)
- 在iTunesStore中下載最新版本的新浪微博安裝包,獲取素材文件
- 支持iOS6.0的項目的素材是可以直接獲取的但是如果不支持iOS6.0的設置是無法獲取素材的,建議保存一些有些App的素材,大部分是沒有版權的
三種文件夾的區(qū)別
- 黃色文件夾: 編譯后爆侣,資源文件在 mainBundle 中萍程,源代碼程序需要通過這種方式拖拽添加, 效率高
- 藍色文件夾:編譯后,資源文件在 mainBundle 中的對應文件夾中兔仰,游戲文件的素材一般通過這種方式拖拽添加,用于換膚應用,游戲場景, 不同路徑下的相同文件名
*白色 Bundle:編譯后茫负,資源文件在 mainBundle 中仍然以包的形式存在,可以路徑形式訪問,拖拽文件更簡單,主要用于第三方框架包裝資源素材
- 新鍵
HMEmoticonManager
類乎赴,里面加載表情數(shù)據(jù),對外提供表情數(shù)據(jù),和一些配置信息
class HMEmoticonManager: NSObject {
static let shareEmoticonManager: EmoticonManager = EmoticonManager()
lazy var packages: [EmoticonPackages] = [EmoticonPackages]()
private override init() {
super.init()
loadEmoticons()
}
func loadEmoticons() {
let path = NSBundle.mainBundle().pathForResource("emoticons.plist", ofType: nil, inDirectory: "Emoticons.bundle")!
let dict = NSDictionary(contentsOfFile: path) as! [String : AnyObject]
let array = dict["packages"] as! [[String : AnyObject]]
for item in array {
//獲取id
let id = item["id"] as! String
loadPackages(id)
}
}
private func loadPackages(id: String) {
//通過id獲取 分組名稱
let path = NSBundle.mainBundle().pathForResource("info.plist", ofType: nil, inDirectory: "Emoticons.bundle/" + id)!
//通過分組名稱加載分組中的 info.plist 文件
let dict = NSDictionary(contentsOfFile: path)!
let group_name_cn = dict["group_name_cn"] as! String
//獲取表情數(shù)據(jù)
let array = dict["emoticons"] as! [[String : String]]
let p = EmoticonPackages(id: id, title: group_name_cn,array: array)
packages.append(p)
}
}
- 定義表情模型
class HMEmoticon: NSObject {
/// 表情文字描述
var chs: String?
/// 表情圖片名字 (僅對圖片表情有效)
var png: String?
/// Emoji表情的 code
var code: String?
/// 是否是Emoji表情
var isEmoji: Bool = false
init(dictionary: [String: AnyObject]) {
super.init()
setValuesForKeysWithDictionary(dictionary)
}
override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
}
- 與界面對應需要向上抽取一個package對應的模型
- title toolBar中顯示的title
- sectionEmoticon toolBar每個按鈕對應的大數(shù)組
class EmoticonPackages: NSObject {
var title: String?
lazy var sectionEmoticon = [[Emoticon]]()
}
- EmoticonPackages添加構造方法
init(id: String, title: String, array: [[String : String]]) {
super.init()
self.title = title
//遍歷數(shù)組 轉換為模型 再將模型轉換為
var emoticonArray: [Emoticon] = [Emoticon]()
for item in array {
let e = Emoticon(id: id, dict: item)
emoticonArray.append(e)
}
}
- 處理數(shù)據(jù), 將模型數(shù)組[HMEmoticon]類型處理為 [[HMEmoticon]]
private func sectionEmoticonArray(array: [Emoticon]) -> [[Emoticon]]{
//獲取表情數(shù)量 這些數(shù)組每頁21個 能裝多少組
let pageCount = (array.count - 1 ) / 21 + 1
var sectionEm = [[Emoticon]]()
for i in 0..<pageCount {
//每頁截取從大數(shù)組中截取21個表情 不足21個的會造成數(shù)組索引越界
let loc = i * SectionEmoticonCount
var length = SectionEmoticonCount
if loc + length > array.count {
length = array.count - loc
}
let subArray = (array as NSArray).subarrayWithRange(NSRange(location: loc, length: length))
sectionEm.append(subArray as! [Emoticon])
}
return sectionEm
}
- 返回
HMEmoticonKeyboard
中collectionView
所需要的數(shù)據(jù)- 數(shù)據(jù)結構分析如下
運行測試
底部 HMEmoticonToolBar
與 顯示表情的 collectionView
聯(lián)動
點擊底部表情類型按鈕忍法,切換到對應表情
- 在
HMEmoticonToolBar
的代表方法中使用collectionView
滾動到對應的組
// MARK: - HMEmoticonToolBarDelegate
func emoticonToolBarButtonDidSelected(index: Int) {
print(index)
let indexPath = NSIndexPath(forItem: 0, inSection: index)
self.collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: false)
}
運行測試
當滾動到某種表情頁時置吓,選中對應表情按鈕
實現(xiàn)思路
- 實現(xiàn)監(jiān)聽
collectionView
滾動的位置-
scrollView
的代理方法scrollViewDidScroll
-
- 獲取到
collectionView
的中心 - 定位滾動到對應cell的中心點
- 判斷當前屏幕中顯示的兩個cell 誰的frame包含了目標中心點
- 循環(huán)當前屏幕中顯示的cell對應的item(最多兩個)
- 當cell的frame 包含該了中心點的時候 就更新頁面信息
實現(xiàn)代碼
func scrollViewDidScroll(scrollView: UIScrollView) {
//確定cell的中心點
var center = collectionView.center
center.x = center.x + collectionView.contentOffset.x
let indexPaths = collectionView.indexPathsForVisibleItems()
for indexPath in indexPaths {
//最多兩個 最少一個
let cell = collectionView.cellForItemAtIndexPath(indexPath)!
if cell.frame.contains(center) {
toolBar.setBtnSelected(indexPath.section)
updatePageControlData(indexPath)
}
}
}
運行測試
- 在
HMEmoticonToolBar
中提示selectButtonWithSection
方法供選中按鈕方法
/// 通過 section 選中某一個按鈕
func selectButtonWithSection(section: Int) {
// 通過 section 獲取到對應的 button,讓其選中
let button = viewWithTag(section)! as! UIButton
childButtonClick(button)
}
- 在
HMEmoticonKeyboard
的scrollViewDidScroll
方法中調用此方法
func scrollViewDidScroll(scrollView: UIScrollView) {
...
emoticonToolBar.selectButtonWithSection(section)
}
運行測試:崩潰 Could not cast value of type 'WeiBo.HMEmoticonToolBar' (0x10bad2dc0) to 'UIButton' (0x10dcc2320). 原因是當前 section 為 0缔赠,調用 viewWithTag 方法取到的是 toolBar 自己衍锚,強轉出錯,所以把每一個按鈕對應的枚舉值給定一個基數(shù)
- 在調用
viewWithTag
方法的時候添加一個基數(shù)
/// 通過 section 選中某一個按鈕
func selectButtonWithSection(section: Int) {
// 通過 section 獲取到對應的 button嗤堰,讓其選中
let button = viewWithTag(section + 1000)! as! UIButton
childButtonClick(button)
}
運行測試:在從第0組滑動過一半的時候戴质,很快速的切換到第1組表情去了,原因就是調用
childButtonClick
方法會執(zhí)行代理方法踢匣,代理方法會回調滾動collectionView
告匠,所以在這個地方只需要切換 button 選中狀態(tài)
- 提取按鈕切換狀態(tài)的方法
changeButtonState
/// 改變按鈕狀態(tài),把當前選中的 button 取消選中离唬,把傳入的 button 設置選中
private func changeButtonState(button: UIButton){
// 如果當前選中的 button 與即將要選中的button相同后专,則直接返回
if button == currentSelectedButton {
return
}
// 取消選中之前的
currentSelectedButton?.selected = false
// 選中現(xiàn)在點擊的
button.selected = true
// 再次記錄現(xiàn)在選的按鈕
currentSelectedButton = button
}
- 在
selectButtonWithSection
調用changeButtonState
方法
/// 通過 section 選中某一個按鈕
func selectButtonWithSection(section: Int) {
// 通過 section 獲取到對應的 button,讓其選中
let button = viewWithTag(section + 1000)! as! UIButton
// 更改按鈕選中狀態(tài)
changeButtonState(button)
}
- 替換
childButtonClick
方法內實現(xiàn)
/// 子按鈕點擊
///
/// - parameter button: 當前點擊的 button
@objc private func childButtonClick(button: UIButton){
// 如果當前選中的 button 與即將要選中的button相同输莺,則直接返回
if button == currentSelectedButton {
return
}
// 改變按鈕狀態(tài)
changeButtonState(button)
// 調用代理方法
delegate?.emoticonToolBarButtonDidSelected(HMEmoticonType(rawValue: button.tag)!)
}
運行測試
表情顯示
設置子控件
- 在
HMEmoticonPageCell
中添加 20 個按鈕表情按鈕
/// 添加表情按鈕
private func addEmoticonButtons(){
et leftMargin: CGFloat = 5
let bottomMargin: CGFloat = 30
let bW = (UIScreen.mainScreen().bounds.width - 2 * leftMargin) / CGFloat(EmoticonColCount)
let bH = (bounds.height - bottomMargin) / CGFloat(EmoticonRowCount)
for i in 0..<SectionEmoticonCount {
let btn = EmoticonButton()
btn.addTarget(self, action: "btnDidClick:", forControlEvents: .TouchUpInside)
btn.titleLabel?.font = UIFont.systemFontOfSize(32)
let row = i / EmoticonColCount
let col = i % EmoticonColCount
let x = leftMargin + CGFloat(col) * bW
let y = bH * CGFloat(row)
btn.frame = CGRect(x: x, y: y, width: bW, height: bH)
contentView.addSubview(btn)
buttonArray.append(btn)
}
}
- 在
setupUI
方法中調用此方法
private func setupUI(){
// 添加子控件
addEmoticonButtons()
...
}
> 運行測試
### 顯示圖片表情數(shù)據(jù)
- 在 `HMEmoticonPageCell` 中提供 `emoticons` 屬性戚哎,供外界設置表情數(shù)據(jù)
```swift
var emoticons: [HMEmoticon]?
-
HMEmoticonKeyboard
中的collectionView
數(shù)據(jù)源方法里面給 cell 設置數(shù)據(jù)
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(HMEmoticonKeyboardCellId, forIndexPath: indexPath) as! HMEmoticonPageCell
cell.indexPath = indexPath
// 設置表情數(shù)據(jù)
cell.emoticons = HMEmoticonTools.allEmoticons()[indexPath.section][indexPath.row]
return cell
}
- 在
emoticons
的didSet
方法中顯示表情
/// 當前頁顯示的表情數(shù)據(jù)
var emoticons: [HMEmoticon]? {
didSet{
// 遍歷當前設置的表情數(shù)據(jù)
for (index,value) in emoticons!.enumerate() {
let button = emoticonButtons[index]
if !value.isEmoji {
let image = UIImage(named: value.png!)
button.setImage(image, forState: UIControlState.Normal)
}
}
}
}
運行測試:表情沒有顯示出來,加載表情的圖片地址不正確嫂用,因為表情圖片是放在
Emoticons.bundle
中的型凳,所以需要拼接前面的路徑,而這前面的路徑就是表情所對應的info.plist
文件所在的路徑
- 在
HMEmoticon
中添加path
屬性
var png: String? {
didSet {
imagePath = Emoticon.bundlePath + "/Emoticons.bundle/" + (id ?? "") + "/\(png ?? "")"
}
}
var imagePath: String?
- 更新
HMEmoticonPageCell
中emoticons
的didSet
方法
/// 當前頁顯示的表情數(shù)據(jù)
var emoticons: [HMEmoticon]? {
didSet{
// 遍歷當前設置的表情數(shù)據(jù)
for (index,value) in emoticons!.enumerate() {
let button = emoticonButtons[index]
if !value.isEmoji {
let image = UIImage(named: "\(value.path!)/\(value.png!)")
button.setImage(image, forState: UIControlState.Normal)
}
}
}
}
運行測試:圖片表情顯示出來了嘱函,但是
cell 復用
導致沒有表情的頁面也顯示過表情甘畅,所以在遍歷設置表情之后需要先將所有的顯示表情的button
隱藏掉
- 先隱藏所有顯示表情的 button,遍歷幾個表情顯示幾個
/// 當前頁顯示的表情數(shù)據(jù)
var emoticons: [HMEmoticon]? {
didSet{
// 先隱藏所有的表情按鈕
for value in emoticonButtons {
value.hidden = true
}
// 遍歷當前設置的表情數(shù)據(jù)
for (index,value) in emoticons!.enumerate() {
let button = emoticonButtons[index]
// 顯示當前遍歷到的表情按鈕
button.hidden = false
if !value.isEmoji {
let image = UIImage(named: "\(value.path!)/\(value.png!)")
button.setImage(image, forState: UIControlState.Normal)
}
}
}
}
顯示 Emoji 表情數(shù)據(jù)
- 演練Emoji表情, 拖入
String+Emoji
分類到項目中往弓, - Emoji 表情其實就是字符串
- 設置
Emoji
表情數(shù)據(jù)
button.setTitle(em.codeStr(), forState: UIControlState.Normal)
}
- 運行測試:Emoji 表情顯示太小疏唾,調整 button 的文字大小即可解決
/// 添加表情按鈕
private func addEmoticonButtons(){
for _ in 0..<HMEmoticonPageNum {
let button = UIButton()
button.titleLabel?.font = UIFont.systemFontOfSize(36)
contentView.addSubview(button)
emoticonButtons.append(button)
}
}
運行測試
- 更改
HMEmoticonKeyboard
中的collectionView
的背景顏色為透明色
/// 顯示表情的視圖
private lazy var emoticonCollectionView: UICollectionView = {
...
collectionView.backgroundColor = UIColor.clearColor()
...
return collectionView
}()
- 去掉
HMEmoticonPageCell
中顯示 section 的 label
運行測試
- 提升表情數(shù)據(jù)
- 每頁的最后一個添加一個刪除按鈕
- 每頁不足21個表情需要補足空白表情
- 空白表情的最后一個應該是刪除表情
在HMEmoticonPackages提升數(shù)據(jù)
init(id: String, title: String, array: [[String : String]]) {
super.init()
self.title = title
//遍歷數(shù)組 轉換為模型 再將模型轉換為
var emoticonArray: [Emoticon] = [Emoticon]()
var index = 0
for item in array {
let e = Emoticon(id: id, dict: item)
emoticonArray.append(e)
index++
if index == 20 {
//每頁的最后一個添加一個刪除表情
let delete = Emoticon(isDelete: true)
emoticonArray.append(delete)
index = 0
}
}
//不足21個的補空白表情
let delta = emoticonArray.count % 21
if delta != 0 {
for _ in delta..<20 {
let empty = Emoticon(isEmpty: true)
emoticonArray.append(empty)
}
emoticonArray.append(Emoticon(isDelete: true))
}
//將模型數(shù)組處理成 分組的模型數(shù)組
//分組規(guī)則 21 個表情為一頁
self.sectionEmoticon = sectionEmoticonArray(emoticonArray)
}
- 在HMEmoticon模型中添加空表情和刪除表情的構造方法和對應的標記
運行測試 解決頁面復用的問題