訪客視圖 - 目標(biāo)
- 如果用戶沒(méi)有登錄,顯示
訪客視圖
秦效,提示用戶注冊(cè)或者登錄
課程重點(diǎn)
- 自定義
TableViewController
的基類VisitorViewController
- 自定義訪客視圖阱州,用代碼實(shí)現(xiàn)蘋果原生的自動(dòng)布局
- 設(shè)置全局外觀
應(yīng)用實(shí)例
- 在實(shí)際應(yīng)用開(kāi)發(fā)中犀概,有可能會(huì)出現(xiàn):
- 功能框架已經(jīng)構(gòu)建完成
- 產(chǎn)品經(jīng)理提出新的功能需求
- 而新提出的功能需求姻灶,會(huì)對(duì)已有的架構(gòu)產(chǎn)生影響
- 例如产喉,在新浪微博中曾沈,已經(jīng)搭建好程序架構(gòu)塞俱,但是如何應(yīng)對(duì)用戶登錄的處理呢障涯?
新的需求 —— 未登錄頁(yè)面
界面截圖
- 首頁(yè)
首頁(yè).png
- 消息
消息.png
- 發(fā)現(xiàn)
發(fā)現(xiàn).png
- 我
我.png
架構(gòu)分析及調(diào)整
- 現(xiàn)有架構(gòu)圖
現(xiàn)有架構(gòu)圖.png
- 新增
HMVisitorViewController
新增BassTableVC.png
表格視圖控制器基類
功能需求
- 判斷用戶是否登錄,如果沒(méi)有登錄
- 使用用戶登錄視圖替換表格視圖控制器的默認(rèn)視圖
- 在導(dǎo)航欄左側(cè)添加
注冊(cè)
按鈕 - 在導(dǎo)航欄右側(cè)添加
登錄
按鈕
代碼實(shí)現(xiàn)
新建
HMVisitorViewController
-
將功能主界面的視圖控制器基類設(shè)置為
HMVisitorViewController
- HMHomeTableViewController
- HMMessageTableViewController
- HMDiscoverTableViewController
- HMProfileTableViewController
增加 用戶登錄標(biāo)記涂滴,根據(jù)用戶登錄標(biāo)記判斷是否加載默認(rèn)視圖
/// 功能模塊控制器的基類控制器
class HMVisitorViewController: UITableViewController {
/// 用戶登錄標(biāo)記
var userLogon = true
override func loadView() {
userLogon ? super.loadView() : setupVisitorView()
}
/// 設(shè)置訪客視圖
private func setupVisitorView() {
view = UIView()
view.backgroundColor = UIColor.orangeColor()
}
}
修改
userLogon
的值柔纵,運(yùn)行測(cè)試界面效果
添加導(dǎo)航欄按鈕
/// 設(shè)置訪客視圖
private func setupVisitorView() {
view = UIView()
view.backgroundColor = UIColor.orangeColor()
// 添加導(dǎo)航欄按鈕
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "注冊(cè)", target: nil, action: "")
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "登錄", target: nil, action: "")
}
用戶登錄視圖
對(duì)于第三方開(kāi)發(fā)者,新浪沒(méi)有開(kāi)放未登錄訪問(wèn)數(shù)據(jù)的權(quán)限郭计,因此在用戶登錄之前椒振,無(wú)法
加載微博數(shù)據(jù)
以及關(guān)注用戶
功能需求
- 用戶登錄操作視圖澎迎,用于在用戶沒(méi)有登錄時(shí)替換表格控制器的根視圖
- 每個(gè)功能模塊的登錄視圖包含以下四個(gè)控件
- 模塊圖標(biāo)
- 描述文字
- 注冊(cè)按鈕
- 登錄按鈕
- 特例
- 首頁(yè)有一個(gè)小的轉(zhuǎn)輪圖片會(huì)不停旋轉(zhuǎn)
功能實(shí)現(xiàn)
- 拖拽相關(guān)圖片素材
- 新建
HMVisitorView.swift
繼承自UIView
/// 訪客登錄視圖
class HMVisitorView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupUI()
}
/// 設(shè)置界面元素
private func setupUI() {
}
}
- 修改
setupVisitorView
函數(shù)
// 替換根視圖
view = HMVisitorView()
- 添加界面元素
/// 設(shè)置界面元素
private func setupUI() {
// 1. 添加控件
addSubview(circleView)
addSubview(iconView)
addSubview(messageLabel)
addSubview(registerButton)
addSubview(loginButton)
}
// MARK: - 懶加載屬性
// 小房子
private lazy var iconView: UIImageView = UIImageView(image: UIImage(named: "visitordiscover_feed_image_house"))
// 轉(zhuǎn)圈的 view
private lazy var circleView: UIImageView = UIImageView(image: UIImage(named: "visitordiscover_feed_image_smallicon"))
// 提示 label
private lazy var messageLabel: UILabel = {
// 這個(gè)地方使用 extension 里面的便利構(gòu)造函數(shù)初始化的
let label = UILabel(textColor: UIColor.darkGrayColor(), fontSize: 14)
label.text = "關(guān)注一些人仁堪,回這里看看有什么驚喜關(guān)注一些人各吨,回這里看看有什么驚喜"
label.numberOfLines = 0
// 文本對(duì)齊方式
label.textAlignment = .Center
return label
}()
// 注冊(cè)按鈕
lazy var registerButton: UIButton = {
let button = UIButton()
button.setTitle("注冊(cè)", forState: .Normal)
button.setBackgroundImage(UIImage(named: "common_button_white_disable"), forState: .Normal)
button.titleLabel?.font = UIFont.systemFontOfSize(14)
button.setTitleColor(UIColor.orangeColor(), forState: .Normal)
return button
}()
// 登錄按鈕
lazy var loginButton: UIButton = {
let button = UIButton()
button.setTitle("登錄", forState: .Normal)
button.setBackgroundImage(UIImage(named: "common_button_white_disable"), forState: .Normal)
button.titleLabel?.font = UIFont.systemFontOfSize(14)
button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
return button
}()
設(shè)置自動(dòng)布局
- 設(shè)置圖標(biāo)約束 - 參照視圖居中對(duì)齊
// 1> 圖標(biāo)
// 2.1 圖標(biāo)
iconView.translatesAutoresizingMaskIntoConstraints = false
addConstraint(NSLayoutConstraint(item: iconView, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: iconView, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1, constant: 0))
- 設(shè)置首頁(yè)小房子 - 參照?qǐng)D標(biāo)居中對(duì)齊
// 2> 首頁(yè)的房子
circleView.translatesAutoresizingMaskIntoConstraints = false
addConstraint(NSLayoutConstraint(item: circleView, attribute: .CenterX, relatedBy: .Equal, toItem: iconView, attribute: .CenterX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: circleView, attribute: .CenterY, relatedBy: .Equal, toItem: iconView, attribute: .CenterY, multiplier: 1, constant: 0))
- 設(shè)置文本 - 參照?qǐng)D標(biāo),水平居中徙融,下方 16 個(gè)點(diǎn)
// 3> 描述文字
messageLabel.translatesAutoresizingMaskIntoConstraints = false
addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .CenterX, relatedBy: .Equal, toItem: circleView, attribute: .CenterX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .Top, relatedBy: .Equal, toItem: circleView, attribute: .Bottom, multiplier: 1, constant: 16))
- 增加文本寬度約束 - 224
// 添加最大寬度約束
addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 224))
- 注冊(cè)按鈕,文本標(biāo)簽左下(16)對(duì)齊隐轩,寬度 100渤早,高度 35
// 4> 注冊(cè)按鈕
registerButton.translatesAutoresizingMaskIntoConstraints = false
addConstraint(NSLayoutConstraint(item: registerButton, attribute: .Leading, relatedBy: .Equal, toItem: messageLabel, attribute: .Leading, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: registerButton, attribute: .Top, relatedBy: .Equal, toItem: messageLabel, attribute: .Bottom, multiplier: 1, constant: 16))
addConstraint(NSLayoutConstraint(item: registerButton, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 100))
addConstraint(NSLayoutConstraint(item: registerButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 35))
調(diào)整按鈕背景圖片切片
登錄按鈕,文本標(biāo)簽右下(16)對(duì)齊积瞒,寬度 100,高度 35
loginButton.translatesAutoresizingMaskIntoConstraints = false
addConstraint(NSLayoutConstraint(item: loginButton, attribute: .Trailing, relatedBy: .Equal, toItem: messageLabel, attribute: .Trailing, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: loginButton, attribute: .Top, relatedBy: .Equal, toItem: registerButton, attribute: .Top, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: loginButton, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 100))
addConstraint(NSLayoutConstraint(item: loginButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 35))
- 設(shè)置登錄按鈕文字顏色
btn.setTitleColor(UIColor.darkGrayColor(), forState: UIControlState.Normal)
- 調(diào)整控件整體垂直位置
addConstraint(NSLayoutConstraint(item: iconView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: -60))
- 添加遮罩圖片視圖
/// 遮罩視圖
private lazy var maskIconView: UIImageView = UIImageView(image: UIImage(named: "visitordiscover_feed_mask_smallicon"))
- 調(diào)整控件的層次
// 1. 添加控件
addSubview(circleView)
addSubview(maskIconView)
addSubview(iconView)
...
- 遮罩圖片自動(dòng)布局
// 6> 遮罩視圖
maskIconView.translatesAutoresizingMaskIntoConstraints = false
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: [], metrics: nil, views: ["subview": maskIconView]));
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-(-35)-[regButton]", options: [], metrics: nil, views: ["subview": maskIconView, "regButton": registerButton]));
- 視圖背景顏色
// 3. 設(shè)置視圖背景顏色
backgroundColor = UIColor(white: 237.0 / 255.0, alpha: 1.0)
運(yùn)行測(cè)試
設(shè)置未登錄信息
- 設(shè)置訪客視圖信息
/**
設(shè)置各個(gè)頁(yè)簽信息
- parameter imageName: 圖片名字
- parameter message: 信息內(nèi)容
*/
func setupInfo(imageName: String?, message: String?) {
if imageName != nil {
circleView.hidden = true
iconView.image = UIImage(named: imageName!)
messageLabel.text = message
}
}
- 在
HMVisitorViewController
中添加登錄視圖屬性
private lazy var visitorView: HMVisitorView = {
let visitorView = HMVisitorView()
return visitorView
}()
- 在
setupVisitorView
中記錄登錄視圖
view = visitorView
修改功能視圖控制器中的代碼
- HMHomeTableViewController
if !userLogon {
visitorView.setupInfo(nil, message: nil)
return
}
- HMMessageTableViewController
if !userLogon {
visitorView.setupInfo("visitordiscover_image_message", message: "登錄后登下,別人評(píng)論你的微博茫孔,發(fā)給你的消息,都會(huì)在這里收到通知")
return
}
- HMDiscoverTableViewController
if !userLogon {
visitorView.setupInfo("visitordiscover_image_message", message: "登錄后被芳,最新银酬、最熱微博盡在掌握,不再會(huì)與實(shí)事潮流擦肩而過(guò)")
return
}
- HMProfileTableViewController
if !userLogon {
visitorView.setupInfo("visitordiscover_image_profile", message: "登錄后筐钟,你的微博、相冊(cè)赋朦、個(gè)人資料會(huì)顯示在這里壹将,展示給別人")
return
}
- 提示信息
- 關(guān)注一些人,回這里看看有什么驚喜
- 登錄后闯团,別人評(píng)論你的微博伐割,發(fā)給你的消息,都會(huì)在這里收到通知
- 登錄后,最新崖堤、最熱微博盡在掌握胯甩,不再會(huì)與實(shí)事潮流擦肩而過(guò)
- 登錄后,你的微博、相冊(cè)速挑、個(gè)人資料會(huì)顯示在這里恐疲,展示給別人
首頁(yè)動(dòng)畫(huà)
- 添加動(dòng)畫(huà)代碼
/// 啟動(dòng)動(dòng)畫(huà)
/**
首頁(yè)的動(dòng)畫(huà)
*/
private func startAnim(){
let anim = CABasicAnimation(keyPath: "transform.rotation")
// 旋轉(zhuǎn)
anim.toValue = 2 * M_PI
// 執(zhí)行時(shí)間
anim.duration = 20
// 執(zhí)行次數(shù)
anim.repeatCount = MAXFLOAT
// 切換界面的時(shí)候動(dòng)畫(huà)會(huì)被釋放漱凝,指定為false之后切換界面動(dòng)畫(huà)就不會(huì)被釋放
anim.removedOnCompletion = false
// 添加動(dòng)畫(huà)
circleView.layer.addAnimation(anim, forKey: nil)
}
- 調(diào)整
setupInfo
函數(shù)
/// 設(shè)置訪客視圖信息
///
/// - parameter imageName: 圖片名稱
/// - parameter message: 消息文字
func setupInfo(imageName: String?, message: String?){
if imageName == nil {
circleView.hidden = false
startAnim()
}else{
circleView.hidden = true
iconView.image = UIImage(named: imageName!)
messageLabel.text = message
}
}
運(yùn)行測(cè)試感论,發(fā)現(xiàn)切換控制器后動(dòng)畫(huà)會(huì)被釋放,另外在首頁(yè)退出到桌面再次進(jìn)入芳绩,動(dòng)畫(huà)同樣會(huì)被釋放
- 設(shè)置動(dòng)畫(huà)屬性
anim.removedOnCompletion = false
登錄&注冊(cè)代理回調(diào)
- 定義協(xié)議
/// 訪客登錄視圖協(xié)議
protocol HMVisitorViewDelegate: NSObjectProtocol{
/// 訪客視圖將要登錄
func visitorLoginViewWillLogin();
/// 訪客視圖將要注冊(cè)
func visitorLoginViewWillRegister();
}
定義協(xié)議時(shí),需要繼承自
NSObjectProtocol
,否則無(wú)法設(shè)置代理的屬性為weak
- 定義代理
weak var delegate: HMVisitorViewDelegate?
- 按鈕回調(diào)
// MARK: - 監(jiān)聽(tīng)按鈕點(diǎn)擊
@objc private func registerButtonClick(){
delegate?.visitorLoginViewWillRegister()
}
@objc private func loginButtonClick(){
delegate?.visitorLoginViewWillLogin()
}
- 遵守協(xié)議
class HMVisitorViewController: UITableViewController, VisitorLoginViewDelegate
- 設(shè)置代理
visitorView.delegte = self
- 實(shí)現(xiàn)方法
// MARK: - VisitorLoginViewDelegate
func visitorLoginViewWillLogin() {
print("登錄")
}
func visitorLoginViewWillRegister() {
print("注冊(cè)")
}
- 修改導(dǎo)航條按鈕監(jiān)聽(tīng)方法
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "注冊(cè)", style: UIBarButtonItemStyle.Plain, target: self, action: "visitorLoginViewWillRegister")
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "登錄", style: UIBarButtonItemStyle.Plain, target: self, action: "visitorLoginViewWillLogin")
運(yùn)行測(cè)試
登錄&注冊(cè)按鈕監(jiān)聽(tīng)
- 修改
HMVisitorViewController
- 刪除遵守協(xié)議
- 刪除設(shè)置代理屬性
- 修改
VisitorLoginView
- 刪除協(xié)議
- 刪除
delegate
屬性 - 刪除按鈕監(jiān)聽(tīng)方法
- 取消
注冊(cè)
&登錄
按鈕的private
修飾符
- 在
setupVisitorView
方法中添加按鈕監(jiān)聽(tīng)方法
// 設(shè)置按鈕監(jiān)聽(tīng)方法
visitorView.registerButton.addTarget(self, action: "visitorLoginViewWillRegistor", forControlEvents: UIControlEvents.TouchUpInside)
visitorView.loginButton.addTarget(self, action: "visitorLoginViewWillLogin", forControlEvents: UIControlEvents.TouchUpInside)
- 修改按鈕監(jiān)聽(tīng)方法作用域
// MARK: - 按鈕監(jiān)聽(tīng)方法
@objc private func visitorLoginViewWillLogin() {
print("登錄")
}
@objc private func visitorLoginViewWillRegistor() {
print("注冊(cè)")
}
階段性小結(jié)
應(yīng)用程序設(shè)計(jì)
- 程序開(kāi)發(fā)過(guò)程中敛助,如果因?yàn)樾枨笞兓獙?duì)應(yīng)用程序做大幅度調(diào)整焕数,以對(duì)現(xiàn)有代碼做最小化修改為原則设联,可以考慮抽取基類的方式實(shí)現(xiàn)
- 整理項(xiàng)目目錄結(jié)構(gòu)時(shí)宫蛆,不建議將
AppDelegate
隱藏
代理的使用
- swift 中代理的使用基本與 OC 相同
- 需要注意的是叛拷,定義協(xié)議時(shí),需要繼承自
NSObjectProtocol
- 代理屬性需要使用
weak
防止出現(xiàn)循環(huán)引用
事件傳遞
- 可以直接在控制器中添加
自定義視圖
中的按鈕監(jiān)聽(tīng)方法 - 在 Swift 中可以直接將按鈕開(kāi)放
- 在 OC 中可以將按鈕的定義放在
.h
中 - 在 Storyboard 中,可以直接拖線