一 app大致框架圖 : (豎屏)
二 app大致框架圖 : (橫屏)
三 知識點
1 自動布局(熟悉)
2 協議(熟悉)
3 橫屏和豎屏的配置(掌握)
4 細節處理
四 布局登錄界面和處理業邏輯
1 自動布局效果圖
2 賬號和密碼相關設置
—-> 2.1 賬號
—-> 2.2 密碼
3 創建一個登錄控制器,用來處理登錄模塊相關業務邏輯
4 該部分需要的相關屬性(通過從storyboard中拖線得到)
class XFJLoginViewController: XFJBaseViewController {
/// 登錄時候轉圈提示
@IBOutlet weak var activityView: UIActivityIndicatorView!
/// 登錄的view
@IBOutlet weak var loginView: UIView!
/// 密碼文本
@IBOutlet weak var passWordTextField: UITextField!
/// 賬號文本
@IBOutlet weak var countTextField: UITextField!
//自動登錄
@IBOutlet weak var autoLogin: UIButton!
//記住密碼
@IBOutlet weak var remberPassword: UIButton!
}
5 處理賬號和密碼登陸的業務邏輯
—-> 5.1 isEmpty:用來判斷賬號和密碼是否為空
——–> 5.1.1 獲取用戶輸入的賬號和密碼
——–> 5.1.2 模擬用戶登錄效果(通過延遲執行達到效果)
——–> 5.1.3 顯示用戶賬號正在登錄的提示(UIActivityIndicatorView類)
—-> 5.2 用字符串相等來判斷是否輸入正確
該部分業務邏輯實現代碼 :
//MARK: - 判斷賬號和密碼是否為空和密碼錯誤
extension XFJLoginViewController {
@IBAction func loginBtnClick() { //通過對按鈕拖線
//獲取賬號和密碼
let countTF = countTextField.text
let passwordTF = passWordTextField.text
//判斷內容為空是否登錄
if countTF?.isEmpty == true || passwordTF?.isEmpty == true {
showError("密碼或賬號不能為空")
//為空的話,後面就沒必要執行了
return
}
//判斷賬號和面是否正確
activityView.startAnimating()
//轉圈的時候停止交互
view.userInteractionEnabled = false
//延遲執行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC)), dispatch_get_main_queue(), { () -> Void in
//停止轉圈
self.activityView.stopAnimating()
//允許交互
self.view.userInteractionEnabled = true
//判斷賬號和密碼是否正確
if countTF == "123" && passwordTF == "123" {
//登錄成功跳轉下一個界面(這裡采用修改根控制器的方法實現)
self.view.window?.rootViewController = XFJHomeViewController()
}else {
self.showError("賬號或密碼錯誤")
}
})
}
}
6 提示框和登錄的時候賬號和密碼一起抖動情況
—-> 6.1 用UIAlertController這個類來達到提示用戶是否登錄成功
—-> 6.2 抖動效果(由於賬號;密碼;登錄按鈕;記住密碼;自動登錄在布局的時候是用一個UIView來作為父控制器,如果不是這樣是無法達到同時抖動的效果)
該部分業務邏輯實現代碼 : (考慮到該部分代碼比較多,所以寫在類擴展的方法中)
//MARK: - 處理登錄的抖動情況
extension XFJLoginViewController {
//登錄時候的抖動效果和彈框提示
private func showError (messaage : String) {
let alertControler = UIAlertController(title: "登錄失敗", message: messaage, preferredStyle: .Alert)
let alertAction = UIAlertAction(title: "確認", style: .Cancel, handler: nil)
//添加
alertControler.addAction(alertAction)
//彈出框
presentViewController(alertControler, animated: true, completion: nil)
//登錄時候的抖動效果
let shakeAnimted = CAKeyframeAnimation(keyPath: "transform.translation.x")
shakeAnimted.repeatCount = 3
shakeAnimted.duration = 0.1
shakeAnimted.values = [-10,0,10]
loginView.layer.addAnimation(shakeAnimted, forKey: nil)
}
}
7 自動登錄和記住密碼
—-> 7.1 該部分業務邏輯介紹 :
——> 7.1.1 當記住密碼被用戶選中,那麼自動登錄也需要選中
——> 7.1.2 當記住密碼由選中到取消,那麼自動登錄也需要自動選中
——> 7.1.3 當自動登錄被選中,那麼記住密碼也需要自動選中
記住密碼業務邏輯代碼塊 :
//MARK: - 記住密碼
extension XFJLoginViewController {
@IBAction func remberBtnClick(button : UIButton) { //需要通過連線拿到
//反選中
button.selected = !button.selected
if button.selected == false {
autoLogin.selected = false
}
}
}
自動登錄業務邏輯代碼塊 :
//MARK: - 自動登錄
extension XFJLoginViewController {
@IBAction func automaticLogin(button : UIButton) { //需要通過連線拿到
//反選中
button.selected = !button.selected
if button.selected == true {
remberPassword.selected = true
}
}
}
8 處理鍵盤上的NEXT和Down邏輯
—-> 8.1 處理思想 : 代理
—-> 8.2 業務邏輯介紹 : 1> 判斷賬號是否正確,如果正確點擊NEXT跳入密碼輸入框 2> 判斷密碼是否正確,如果正確點擊Down跳入主界面
—-> 8.3 問題 : 如何給鍵盤設置代理呢?
—-> 8.4 解決方法 : 在storyboard中通過連線解決(賬號和密碼輸入框同理)
—-> 8.5 實現代理方法,在代理方法中實現相關業務邏輯
//對鍵盤的處理(NEXT和Down)----> 需要在storyboard中設置代理否則是下面代碼是沒用的
extension XFJLoginViewController : UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
//判斷
if textField == countTextField {
//如果判斷正確,讓密碼鍵盤成為第一響應者
passWordTextField.becomeFirstResponder()
}else if passWordTextField == textField {
//如果密碼相等,調用登錄方法
loginBtnClick()
}
return true
}
}
五 處理控制器view的狀態欄顏色
1 思路 : 重寫系統設置狀態欄的函數,將狀態欄的顏色改為自己想要的,由於主界面的控制器也需要這種狀態,那麼我們的思想是創建一個控制器,在該類中設置狀態欄樣式,然後再創建一個主界面控制器,繼承第一個控制.
2 用來設置狀態欄的控制器
3 處理代碼 :
class XFJBaseViewController: UIViewController {
//設置狀態欄的樣式
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
六 主界面
1 創建一個繼承XFJBaseViewController(主控制器也需要設置狀態欄樣式)的控制器.用來作為主界面控制器
2 創建左側的工具欄(dock)是一個繼承系統的UIView,用來管理底部,中間,頂部按鈕
3 在XFJHomeViewController控制器中創建dock(一般是懶加載)
該部分代碼 :
//懶加載
private lazy var dock : XFJDock = {
//創建dock
let dock = XFJDock()
//添加dock到view中
self.view.addSubview(dock)
//設置高度
dock.frame.size.height = self.view.frame.height
//設置autoresizing跟隨父控件的拉伸而拉伸
dock.autoresizingMask = .FlexibleHeight
return dock
}()
4 創建底部工具欄
—-> 4.1 注意 : 1> 代碼書寫位置 : XFJDock ; 2>采用方式 : 懶加載
//懶加載底部工具欄
private lazy var bottomMenu : XFJBottom = {
//創建底部工具欄
let bottomMenu = XFJBottom()
//將創建好的底部工具條添加到view中
self.addSubview(bottomMenu)
//設置子控件跟隨父控件的頂部拉伸而拉伸
bottomMenu.autoresizingMask = .FlexibleTopMargin
return bottomMenu
}()
5 整個文件中需要用到的參數
—-> 5.1 創建一個文件用來設置整個文件需要用到的參數
—-> 5.2 該文件中需要的參數
//橫屏
let kDockLaspaceWidth : CGFloat = 270.0
//豎屏
let kDockProtraitWidth : CGFloat = 70.0
//item的高度
let kDockLaspaceHeight : CGFloat = 70.0
//頭像橫屏時候的高度
let kIconButtonLaspaceHeight : CGFloat = kIconButtonLaspaceWidth + kIconTextHeight
//頭像橫屏時候的寬度
let kIconButtonLaspaceWidth : CGFloat = 90.0
//頭像豎屏時候的高度和寬度
let kIconProtraitWH : CGFloat = 60
//頭像橫屏和豎屏的時候y值一樣
let kIconLpaceY : CGFloat = 40.0
//頭像文字的高度
let kIconTextHeight : CGFloat = 30.0
6 處理屏幕旋轉過程中dock中的各部分工具模塊
—-> 6.1 通過在XFJHomeViewController重寫系統的方法,在該方法中將屏幕的,通過在該方法中讓屏幕的寬度和高度比較,將計較的值傳入各個工具模塊中
//當屏幕即將旋轉的時候調用,其實並沒有旋轉
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
//判斷當前屏幕的true還是false
let isLaspace = size.width > size.height
//設置dock的寬度
let duration = coordinator.transitionDuration()
UIView.animateWithDuration(duration) { () -> Void in
//將屏幕方向傳入dock中
self.dock.roteLspaces(isLaspace)
//更新旋轉後的x值
self.contentive?.frame.origin.x = self.dock.frame.width
}
}
—-> 6.2 解析 : 該方法中通過self.dock.roteLspaces(isLaspace)將判斷得出的結果傳入dock中,在dock中再做相應的處理
7 在XFJDock中定義一個方法,用來接收self.dock.roteLspaces(isLaspace)中的isLaspace值,同時作為參數,傳到每個模塊中,通過這個值來計算各個模塊在屏幕不同的方向時候各模塊顯示的樣式.
七 底部模塊
1 在XFJDock類中創建XFJBottom(底部模塊對象)
2 采用創建方式 : 懶加載(用到的時候再加載)
//懶加載底部工具欄
private lazy var bottomMenu : XFJBottom = {
//創建底部工具欄
let bottomMenu = XFJBottom()
//將創建好的底部工具條添加到view中
self.addSubview(bottomMenu)
//設置子控件跟隨父控件的頂部拉伸而拉伸
bottomMenu.autoresizingMask = .FlexibleTopMargin
return bottomMenu
}()
3 在XFJBottom類中布局屏幕旋轉的尺寸和添加按鈕
—-> 3.1 根據從XFJDock類中的方法中傳入的isLaspace參數來布局底部模塊的尺寸
//提供一個方法,讓外界將屏幕當前的狀態傳進來
func rateLaspace(isLaspace : Bool) {
//取出按鈕總數
let count = subviews.count
//設置自身的寬度
self.frame.size.width = (superview?.frame.width)!
//根據傳入的屏幕方法設置高度
self.frame.size.height = isLaspace ? kDockLaspaceHeight : kDockLaspaceHeight * CGFloat(count)
//y值
self.frame.origin.y = (superview?.frame.height)! - frame.size.height
//遍歷
for var i = 0 ; i < count ;i++ {
//根據角標取出按鈕
let item = subviews[i]
//設置按鈕的尺寸
item.frame.size.width = isLaspace ? frame.width/CGFloat(count) : frame.width
item.frame.size.height = kDockLaspaceHeight
item.frame.origin.x = isLaspace ? CGFloat(i) * item.frame.size.width : 0
item.frame.origin.y = isLaspace ? 0 : CGFloat(i) * item.frame.size.width
}
}
—-> 3.2 創建按鈕(該部分的代碼比較多,我們寫在類擴展的函數中)
代碼塊一 : (該部分的兩個函數是一起使用的)
override init(frame: CGRect) {
super.init(frame: frame)
//初始化子控件
setUpButton("tabbar_blog",type: .blog)
setUpButton("tabbar_mood",type: .mood)
setUpButton("tabbar_photo",type: .photo)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
代碼塊二 :
//給類擴充一個方法,用來創建子控件數
extension XFJBottom {
@objc private func setUpButton(imageName : String, type : ButtomMenuType ) {
//創建按鈕
let iconButton = UIButton()
//綁定tag,方便點擊按鈕的時候可以傳入對應的值
iconButton.tag = type.rawValue
//添加按鈕
addSubview(iconButton)
//設置按鈕圖片(普通圖片)
iconButton.setImage(UIImage(named: imageName), forState: .Normal)
//設置高亮圖片
iconButton.setBackgroundImage(UIImage(named: "tabbar_separate_selected_bg"), forState: .Highlighted)
//監聽按鈕點擊(這是oc的方法,一定要加上@objc)
iconButton.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
}
}
八 中間模塊
1 和底部模塊基本一樣,也是通過在XFJDock類中提供的roteLspaces (isLaspace : Bool)函數,用來最為將isLaspace的屏幕狀態來計算該部分情況
2 注意 : 該部分橫屏和豎屏不同的是,在橫屏的時候能顯示圖片和文字,但是在屏幕豎屏的時候,只顯示圖片.所以這部分需要做判斷.
3 在XFJDock類中懶加載中間部分,然後添加到dock中
//懶加載中間工具欄
private lazy var middleMenu : XFJTabBar = {
//創建中間工具欄
let middleMenu = XFJTabBar()
//添加
self.addSubview(middleMenu)
//設置中間工具欄底部不變,頂部隨著父控件拉伸而拉伸
middleMenu.autoresizingMask = .FlexibleTopMargin
//返回
return middleMenu
}()
4 根據傳入的屏幕方向,計算中間模塊的尺寸
//根據外界傳入的屏幕方向,計算高度和寬度
func roteTabLaspace(isLaspace : Bool) {
//中間部分的子控件總數
let count = subviews.count
//寬度跟隨父控件
frame.size.width = (superview?.frame.width)!
//高度
frame.size.height = kDockLaspaceHeight * CGFloat(count)
//遍歷
for var i = 0 ; i < count ; i++ {
//取出item
let item = subviews[i]
//設置尺寸
item.frame.size.width = (superview?.frame.width)!
item.frame.size.height = kDockLaspaceHeight
item.frame.origin.x = 0
item.frame.origin.y = item.frame.size.height * CGFloat(i)
}
}
5 添加中間模塊的按鈕(由於該部分的代碼比較多,那麼我們寫在類擴展的方法中)
代碼塊一 :
//重寫initWithFrame
override init(frame: CGRect) {
super.init(frame: frame)
//添加標題
setUpTabBarItem(imageName: "tab_bar_feed_icon", title: "全部動態")
setUpTabBarItem(imageName: "tab_bar_passive_feed_icon", title: "與我相關")
setUpTabBarItem(imageName: "tab_bar_pic_wall_icon", title: "照片牆")
setUpTabBarItem(imageName: "tab_bar_e_album_icon", title: "電子相框")
setUpTabBarItem(imageName: "tab_bar_friend_icon", title: "好友")
setUpTabBarItem(imageName: "tab_bar_e_more_icon", title: "其它")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
代碼塊二 :
//MARK: - 添加中間的子標題
extension XFJTabBar {
private func setUpTabBarItem(imageName imageName : String, title : String) {
//創建按鈕
let button = XFJItemBtn()
//綁定tag
button.tag = subviews.count
//添加按鈕
addSubview(button)
//設置普通按鈕
button.setTitle(title, forState: .Normal)
//設置普通圖片
button.setImage(UIImage(named: imageName), forState: .Normal)
//設置高亮圖片
button.setBackgroundImage(UIImage(named: "tabbar_separate_selected_bg"), forState: .Selected)
//監聽按鈕
button.addTarget(self, action: "buttonClick:", forControlEvents: .TouchDown)
}
}
6 處理中間部分按鈕選中和反選中情況
—-> 6.1 創建一個屬性來記錄按鈕的狀態
//定義一個屬性,用來記錄按鈕點擊的狀態
private var selectButton = UIButton?()
—-> 6.2 處理按鈕
/MARK: - 實現事件監聽的方法
extension XFJTabBar {
@objc private func buttonClick(button : UIButton) {
//如果selectButton?.ta有值就用它的值,否則就用0
let tag = selectButton?.tag ?? 0
//實現代理方法
tabBarItemDelegate?.XFJTabBarItem!(tag, to: button.tag)
selectButton?.selected = false
selectButton = button
selectButton?.selected = true
}
}
7 取消按鈕的高亮狀態
—-> 7.1 創建橋接文件(如果自己不想配置,就讓系統體你配置)
—-> 7.2 創建OC文件
—-> 7.3 在OC文件中重寫按鈕的高亮方法
- (void)setHighlighted:(BOOL)highlighted{
}
8 自定義按鈕
—-> 8.1 自定義原因 : 由於我們需要修改當屏幕是橫屏和豎屏兩種狀態下,按鈕的文字出現與隱藏的效果,需要用自定義來解決
—-> 8.2 創建繼承XFJCustomBtn的自定義文件
—-> 8.3 重寫系統的init(frame: CGRect)方法,並且做相關設置(代碼裡有說明)
class XFJItemBtn: XFJCustomBtn {
//定義一個屬性用來計算圖片占的比例
var ratio : CGFloat = 0.4
override init(frame: CGRect) {
super.init(frame: frame)
//僅僅讓圖片高亮
adjustsImageWhenHighlighted = false
//設置圖片居中
imageView?.contentMode = .Center
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
—-> 8.4 由於是繼承按鈕,所以重寫按鈕中的兩個方法 : 一個是對按鈕中標題的處理和另一個是對按鈕中圖片的處理(函數內部代碼比較易懂,我這裡就不多加說明了)
代碼塊一 : 按鈕標題
//MARK: - 按鈕標題
extension XFJItemBtn {
override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
if frame.height == frame.width { //豎屏
return CGRectZero
}else{//橫屏
let width : CGFloat = frame.width * (1 - ratio)
let height : CGFloat = frame.height
let x : CGFloat = frame.width * ratio
let y : CGFloat = 0
return CGRectMake(x, y, width, height)
}
}
}
代碼塊二 : 按鈕圖片
//MARK: - 按鈕圖片
extension XFJItemBtn {
override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
if frame.height == frame.width { //豎屏
return bounds
}else{//橫屏
let width : CGFloat = frame.width * ratio
let height : CGFloat = frame.height
let x : CGFloat = 0.0
let y : CGFloat = 0.0
return CGRectMake(x, y, width, height)
}
}
}
九 頭像模塊
1 在XFJDock類中對頭像模塊的懶加載和添加到dock中
//懶加載頭像
private lazy var iconButton : XFJIconButton = {
//創建頭像
let iconButton = XFJIconButton()
//添加頭像
self.addSubview(iconButton)
//
//返回
return iconButton
}()
func roteToLapace(isLapace : Bool) {
//icon的高度
frame.size.height = isLapace ? kIconButtonLaspaceHeight : kIconProtraitWH
frame.size.width = isLapace ? kIconButtonLaspaceWidth : kIconProtraitWH
frame.origin.y = kIconLpaceY
frame.origin.x = ((superview?.frame.width)! - frame.width) * 0.5
self.isLapace = isLapace
}
3 添加頭像中的文字和圖片設置
//重寫init方法
override init(frame: CGRect) {
super.init(frame: frame)
//設置頭像圖片
setImage(UIImage(named: "icon"), forState: .Normal)
//設置文字
setTitle("不良少年J", forState: .Normal)
//設置文字顯示位置
titleLabel?.textAlignment = .Center
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
4 由於頭像的本質是一個按鈕,那麼我們可以通過重寫按鈕中對圖片和對標題的方法來對圖片進行設置
頭部按鈕的圖片設置代碼 : (易懂)
//MARK: - 頭部按鈕的圖片
extension XFJIconButton {
override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
if frame.width == frame.height { //豎屏
return bounds
}else{//橫屏
let width : CGFloat = kIconButtonLaspaceWidth
let height : CGFloat = kIconButtonLaspaceHeight
let x : CGFloat = 0.0
let y : CGFloat = 0.0
return CGRectMake(x, y, width, height)
}
}
}
頭部按鈕的標題設置代碼 : (易懂)
//MARK: - 按鈕的標題
extension XFJIconButton {
override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
if frame.width == frame.height {//豎屏
return CGRectZero
}else{//橫屏
let width : CGFloat = kIconButtonLaspaceWidth
let height : CGFloat = kIconTextHeight
let x : CGFloat = 0.0
let y : CGFloat = kIconButtonLaspaceHeight
return CGRectMake(x, y, width, height)
}
}
}
十 底部模塊業務邏輯
1 需求 : 當點擊底部按鈕的時候,彈出控制器,並且點擊彈出的控制器中的按鈕,控制器消失.
2 采用方法 : 代理;協議
3 誰可以成為代理? 主控制器(XFJHomeViewController).原因是因為當點擊底部按鈕時,是有主控制器彈出子控制器.
4 彈出方式 ? model
5 設置枚舉(外面通過switch的判斷方式來選中model出對應的控制器)
//MARK : - 定義枚舉
@objc enum ButtomMenuType : Int {
case blog
case mood
case photo
}
6 定義協議方法
//MARK : - 定義協議
@objc protocol XFJBottomDelegate : NSObjectProtocol {
//可實現的
optional func XFJBottomMenuClickItem(menu : XFJBottom, type : ButtomMenuType)
}
7 定義代理
//設置代理屬性
weak var BottomMenuDelegate :XFJBottomDelegate?
8 監聽底部模塊按鈕的點擊(這是oc的方法,一定要加上@objc)
//監聽按鈕點擊(這是oc的方法,一定要加上@objc)
iconButton.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
8 處理按鈕點擊事件
//MARK: - 點擊底部按鈕,將相應的值傳入協議方法中
extension XFJBottom {
@objc private func buttonClick(button : UIButton) {
BottomMenuDelegate?.XFJBottomMenuClickItem!(self, type: ButtomMenuType(rawValue: button.tag)!)
}
}
9 設置代理(在XFJHomeViewController的類中)
//設置底部工具條的代理
dock.getBottomMenu.BottomMenuDelegate = self
10 實現協議方法
//MARK : - 實現底部協議的方法
extension XFJHomeViewController {
func XFJBottomMenuClickItem(menu: XFJBottom, type: ButtomMenuType) {
switch type {
case .blog :
print("點擊了blog")
case .mood :
//創建控制器
let moodVC = XFJMoodViewController()
//將該控制器作為導航控制器的根控制器
let nav = UINavigationController(rootViewController: moodVC)
//設置控制器樣式
nav.modalPresentationStyle = .FormSheet
//model
presentViewController(nav, animated: true, completion: nil)
print("點擊了mood")
case .photo :
print("點擊了photo")
}
}
}
十一 中間模塊的業務邏輯
1 由於用戶點擊中間模塊的按鈕的時候,是對子控制器的切換,那麼我們需要創建所有的子控制器(在XFJHomeViewController類中書寫;考慮代碼比較多,寫在類擴展中)
//MARK : - 添加子控制器
extension XFJHomeViewController {
private func setUpAllChildController() {
//全部動態
let allDynamicStateController = XFJAllSegViewController()
allDynamicStateController.view.backgroundColor = UIColor.blueColor()
setUpNavController(allDynamicStateController)
//與我相關
let andMeCorrelationController = UIViewController()
andMeCorrelationController.view.backgroundColor = UIColor.redColor()
andMeCorrelationController.title = "與我相關"
setUpNavController(andMeCorrelationController)
//照片牆
let photoWallController = UIViewController()
photoWallController.view.backgroundColor = UIColor.yellowColor()
photoWallController.title = "照片牆"
setUpNavController(photoWallController)
//電子相框
let electronRahmenController = UIViewController()
electronRahmenController.view.backgroundColor = UIColor.purpleColor()
electronRahmenController.title = "電子相框"
setUpNavController(electronRahmenController)
//好友
let friendsController = UIViewController()
friendsController.view.backgroundColor = UIColor.orangeColor()
friendsController.title = "好友"
setUpNavController(friendsController)
//其它
let otherController = UIViewController()
otherController.view.backgroundColor = UIColor.whiteColor()
otherController.title = "其它"
setUpNavController(otherController)
//添加一個頭像子控制器
let iconController = UIViewController()
iconController.view.backgroundColor = UIColor.brownColor()
iconController.title = "個人中心"
setUpNavController(iconController)
}
}
2 由於所有的子控制器都是作為導航控制器的根控制器,所以定義一個方法來設置導航控制器
//MARK : - 定義一個方法用來設置導航控制器
extension XFJHomeViewController {
private func setUpNavController(root : UIViewController){
let nav1 = UINavigationController(rootViewController: root)
addChildViewController(nav1)
}
}
3 設置中間子控制器的view
//MARK : - 設置內容的view
extension XFJHomeViewController {
private func setUpContentView() {
//判斷是否子控制器存在,如果存在就直接返回
guard childViewControllers != [] else{
return
}
//創建一個屬性
let contentive = UIView()
//添加contentView
view.addSubview(contentive)
//設置子控制器的view尺寸
contentive.frame.origin.x = dock.frame.width
contentive.frame.origin.y = 20.0
contentive.frame.size.height = view.frame.height - 20.0
//高度跟隨父控件拉伸而拉伸
contentive.autoresizingMask = .FlexibleHeight
//用寬度和高度取出裡面最小的
let ProtraitWidth = min(view.frame.height, view.frame.width)
//計算子控制器的寬度
contentive.frame.size.width = ProtraitWidth - kDockProtraitWidth
//賦值(用於屏幕旋轉的時候更新x值)
self.contentive = contentive
}
}
4 定義協議
//MARK: - 協議方法
@objc
protocol XFJTabBarDelegate : NSObjectProtocol {
//這裡用兩個參數的原因是由於選中狀態和沒有選中狀態需要切換,那麼需要將不同的tag傳入其中,才能令對應的控制器切換
optional func XFJTabBarItem(from : Int, to : Int)
}
5 設置代理
//定義代理
weak var tabBarItemDelegate : XFJTabBarDelegate?
6 實現代理方法(該部分代碼是在監聽按鈕點擊事件中個書寫的)
//實現代理方法
tabBarItemDelegate?.XFJTabBarItem!(tag, to: button.tag)
7 設置代理
//設置中間部分工具條的代理
dock.getTabBarItem.tabBarItemDelegate = self
8 實現協議方法
//MARK: - 實現中間部分協議方法
extension XFJHomeViewController : XFJTabBarDelegate{
func XFJTabBarItem(from: Int, to: Int) {
//根據傳入的參數,取出上移個view
let previousController = childViewControllers[from]
//移除上一個控制器的view
previousController.view.removeFromSuperview()
//取出下一個控制器的view
let nextVC = childViewControllers[to]
//將切換的子控制器的view添加到contentView中
contentive?.addSubview(nextVC.view)
//計算nextVC的尺寸
nextVC.view.frame = (contentive?.bounds)!
//下一個按鈕的角標
selectButtonIndex = to
}
}
十二 頭像模塊業務邏輯處理
1 在XFJHomeViewController類中實現對頭像按鈕的監聽
2 監聽方法(需要加上@objc)
//監聽頭像點擊
dock.getIcon.addTarget(self, action: "iconButtonClick", forControlEvents: .TouchUpInside)
3 實現頭像監聽方法
//MARK : - 實現頭像監聽方法
extension XFJHomeViewController {
@objc private func iconButtonClick() {
//調用點擊中間部分的內容
XFJTabBarItem(selectButtonIndex, to: childViewControllers.count - 1)
//調用取消選中狀態
dock.getTabBarItem.unSelecButton()
}
}
十三 各個模塊中的get方法
1 我們是在XFJHomeViewController類中設置其為各個模塊的代理,但是我們在該類中是無法直接拿到對應的模塊的代理,那麼我們是通過什麼辦法的呢?—–> get方法
2 在各個模塊創建的時候,我們直接提供一個get方法,這樣很大的方便了外界可以間接的拿到模塊對象
//對底部工具欄提供只讀方法
var getBottomMenu : XFJBottom {
get{
return bottomMenu
}
}
//對中間工具條提供只讀的方法
var getTabBarItem : XFJTabBar {
get{
return middleMenu
}
}
//給頭像按鈕提供一個只讀的屬性,讓外界可以拿到iconButton按鈕
var getIcon : XFJIconButton {
get{
return iconButton
}
}
3 通過get方式拿到模塊設置代理和對頭像的監聽
//設置底部工具條的代理
dock.getBottomMenu.BottomMenuDelegate = self
//設置中間部分工具條的代理
dock.getTabBarItem.tabBarItemDelegate = self
//監聽頭像點擊
dock.getIcon.addTarget(self, action: "iconButtonClick", forControlEvents: .TouchUpInside)
十四 細節
1 默認程序啟動進入主界面,顯示的是頭像模塊
2 在XFJTabBar提供一個方法取消選中狀態
//提供一個方法取消選中狀態(由外界調用)
func unSelecButton() {
selectButton?.selected = false
}
3 設置程序已啟動進入主界面當前的角標為0
//設置一個屬性當前按鈕角標
private var selectButtonIndex : Int = 0
4 將該屬性傳入對頭像按鈕監聽的方法中,就能實現默認選中頭像模塊了
//MARK : - 實現頭像監聽方法
extension XFJHomeViewController {
@objc private func iconButtonClick() {
//調用點擊中間部分的內容
XFJTabBarItem(selectButtonIndex, to: childViewControllers.count - 1)
//調用取消選中狀態
dock.getTabBarItem.unSelecButton()
}
}
十五 補充
1 中間模塊中的導航條的標題進行處理UISegmentedControl
2 該部分沒什麼可以說的,比較簡單,下面是代碼
class XFJAllSegViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//設置背景顏色
view.backgroundColor = UIColor.whiteColor()
//創建seg
let seg = UISegmentedControl(items: ["全部","特別關心","好友動態","認證空間"])
//添加
navigationItem.titleView = seg
//設置顏色
seg.tintColor = UIColor.grayColor()
//設置默認選中第0個
seg.selectedSegmentIndex = 0
//設置選中文字
seg.setTitleTextAttributes([NSForegroundColorAttributeName : UIColor.blackColor()], forState: .Normal)
//監聽點擊事件
seg.addTarget(self, action: "segClick", forControlEvents: .TouchUpInside)
}
}
3 點擊底部模塊,model出控制器,對model出來的控制器相關設置
class XFJMoodViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//設置標題
self.title = "發表說說"
//設置顏色
view.backgroundColor = UIColor.redColor()
//設置導航條左邊的內容
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "關閉", style: .Plain, target: self, action: "exit")
//設置導航條右邊的內容
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "發表", style: .Plain, target: self, action: "deliver")
}
}
4 dismiss
//Mark : - dismiss控制器
extension XFJMoodViewController {
@objc private func exit() {
dismissViewControllerAnimated(true, completion: nil)
}
}
十六 總結
1 總結的差不多了,只要我考慮到的情況,我都寫在了裡面,希望對個位能有所幫助吧.裡面依舊有不完善的地方.後續想到了會完善.
2 邏輯適中,要求能理解協議的用法.明白怎麼對屏幕橫屏和豎屏的判斷,通過橫屏和豎屏對每個模塊尺寸的設置.
3 最後大家如果覺得我寫得還行,麻煩大家關注我的官方博客,有什麼意見大家給我留言即可,謝謝!!!!