一:訪客界面效果如圖swift
二:xib封裝訪客視圖的view數組
1:業務邏輯分析:1:因爲用戶未登陸時要顯示訪客視圖,要先進行判斷用戶是否登陸,未登陸則顯示訪客視圖,登陸則顯示正常的登錄界面,因爲要在四個子控制器界面的控制器中都要判斷是否顯示訪客視圖,一樣的邏輯,因此考慮抽成父類,把判斷是否顯示訪客視圖的邏輯封裝在父類中,讓子類去繼承。2:訪客視圖的界面如圖:將訪客視圖封裝在一個view中,view的界面相對固定,因此用xib搭建,首先將盡量顯示的控件所有封裝在view的內部,再根據外界傳入的model或是在封裝類中定義方法來控制控件顯示的內容或是相應控件的顯示和隱藏,3:經過在父類控制器中重寫loadView方法判斷用戶是否登錄來設置是否加載訪客視圖閉包
2:訪客視圖的搭建:新建文件繼承UIView,新建xib與新建的UIview類同名,首先來到xib中,將類與xib進行關聯,能夠不設置freeformapp
1:1:先拖入UIimageView來設置旋轉圖片:先在如圖處設置xib約束,設置控件相對於父視圖水平居中豎直居中,在如圖所示位置水平豎直居中(此處1設置的是相對於父視圖的水平豎直居中,圖2水平豎直居中對齊,通常都是任意選中兩個控件,設置控件A相對於控件B的中心點水平豎直居中)。ide
2:先設置旋轉圖片相對於父視圖水平豎直居中,在上圖1中去設置,再來到右側調整其豎直中心點向上偏移,按住command + =調整旋轉圖片的UIimageView的大小等於圖片的大小,就不用再去設置寬高的約束了。模塊化
3:再拖入一個UIImageView背景圖片來蓋住旋轉圖片的底部,在如圖所示的位置設置背景圖片的頂部左右和高約束,(圖中小箭頭的部分能夠設置相對於哪一個控件的約束)函數
4:再設置房子的約束,拖入一個UIimageView,command + = 來設置imageView的大小和圖片的大小相等,因此就不用設置寬高約束了,再來到如圖所示的位置處設置,同時選中房子和旋轉圖片,拉線,按住shift鍵可同時設置多個約束,設置房子相對於旋轉view的中心點水平居中,豎直居中(在右側又能夠調整位置的相對偏移)工具
5:提示lable的約束設置:拖入lable,換行,設置numberoflines爲0,設置文字的居中效果,Aliment對齊方式爲居中,就會顯示出如圖所示的效果:在左側同時選中lable和房子,設置lable相對於房子中心點水平居中對齊,y方向的約束就設置lable距離房子底部的約束,拉線設置豎直距離。給lable設置一個最大寬度的約束,爲了適配登錄註冊按鈕,高度約束不去設置動畫
10:登錄註冊按鈕的設置:拖入註冊按鈕,調整樣式爲custom,不然不能顯示出按鈕的高亮狀態,設置按鈕的背景圖片,此時背景圖片會拉伸,能夠在如圖處設置,防止註冊按鈕的背景拉伸:在如圖出sliciing處設置水平豎直爲保護區域,不產生拉伸spa
寬高約束不設置,讓按鈕根據內容大小來自定義寬高,在設置按鈕相對lable左對齊,y方向約束,設置lable相對於房子(固定不變)的底部約束,爲了不因提示文字內容的多少,而致使四個界面的登錄註冊按鈕再也不同一個y值方向上。再拷貝粘貼出登錄按鈕,拷貝粘貼的控件保留了寬高約束,因此還須要設置其右對齊和y方向上的約束。設置兩個控件的對齊方式如圖:1:同時選中兩個控件,來到如圖處位置設置兩個控件的對齊方式
三:控制器代碼
1:封裝訪客view的代碼
import UIKit /* 總結:1:設置好xib後,拉線到相應的view中成爲RHVisitorView的屬性(對於註釋:通常代碼的模塊化用 //MARK:-1來註釋,非模塊化只是可用//來表示),@IBOutlet weak var loginBtn: UIButton!,用weak修飾由於xib控件已經在該view上了,因此該view對該控件已經有了一個強引用,在swift中通常變量基本都爲可選類型,要是肯定其必定有值,則能夠用!來進行強制解包,as?能夠將左邊的類型轉換爲右邊的可選類型,as!當肯定要轉的變量必定有值的時候,能夠將左邊的可選類型轉換爲右邊肯定的類型,as,將左邊的類型轉換爲右邊的類型,例如:利用as將swift字符串轉爲oc字符串來實現字符串的截取 2:1:在封裝的view中提供類方法快速返回xib中加載的對象:在swift中類方法以class開頭:class + func + 函數名(參數裂變)-> 返回值類型{ 業務邏輯代碼 return 返回值類型 } 2:代碼 class func visitorView()-> RHVisitorView{ //1:加載xib的view let visitor = Bundle.main.loadNibNamed("RHVisitorView", owner: nil, options: nil)!.last as! RHVisitorView visitor.backgroundColor = UIColor(red: 238/255.0, green: 238/255.0, blue: 238/255.0, alpha: 1) return visitor } 1:Bundle.main.loadNibNamed("RHVisitorView", owner: nil, options: nil),從xib中得到加載的數組,此得到數組的類型爲可選類型[Any]?,Any表示數組中存放的元素,此時能夠保證數組必定有值,因此能夠!強制解包, Bundle.main.loadNibNamed("RHVisitorView", owner: nil, options: nil)! 2:Bundle.main.loadNibNamed("RHVisitorView", owner: nil, options: nil)!.last,從數組或是字典取出的元素都是Any?的可選類型,能保證其必定有值,能夠用as!轉換爲RHVisitorView類型 3:封裝view提供方法接口來控制封裝view具體顯示的內容和須要隱藏的內容:1:傳入參數imageName和title來設置圖片和文字的顯示內容,其中蘋果默認函數中第一個參數爲內部參數,用_+空格+參數名來表示,默認從第二個參數開始即爲內部參數又爲外部參數,要想讓其餘參數,也爲默認參數則能夠用_+空格+參數名來表示,還能夠傳入默認參數。 4:首頁須要旋轉圖片:旋轉圖片繞着z軸旋轉一圈2π,從0到2π,兩個value值,因此用CABasicAnimation func rotationImage() { //1:建立基礎動畫對象 let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") //2:設置動畫效果 rotationAnimation.fromValue = 0 rotationAnimation.toValue = M_PI * 2 rotationAnimation.repeatCount = MAXFLOAT rotationAnimation.isRemovedOnCompletion = false rotationAnimation.duration = 3 //3:將動畫添加到layear上 rotationImageView.layer.add(rotationAnimation, forKey: nil) } 注意:1:核心動畫,都會在切換界面或是程序進入後臺的時候中止動畫,因此rotationAnimation.isRemovedOnCompletion = false 2:要將核心動畫添加到view的layer上 **/ class RHVisitorView: UIView { //MARK:-1:定義xib屬性 @IBOutlet weak var loginBtn: UIButton! @IBOutlet weak var infoLable: UILabel! @IBOutlet weak var registerBtn: UIButton! @IBOutlet weak var rotationImageView: UIImageView! @IBOutlet weak var iconImageView: UIImageView! //MARK:-2:類方法加載xib並設置背景色 class func visitorView()-> RHVisitorView{ //1:加載xib的view let visitor = Bundle.main.loadNibNamed("RHVisitorView", owner: nil, options: nil)!.last as! RHVisitorView visitor.backgroundColor = UIColor(red: 238/255.0, green: 238/255.0, blue: 238/255.0, alpha: 1) return visitor } //MARK:-3:提供接口,供子類調用 func setupVisitorView(_ imageName:String,title:String) { iconImageView.image = UIImage(named: imageName) infoLable.text = title rotationImageView.isHidden = true } //MARK:-4:旋轉圖片 func rotationImage() { //1:建立基礎動畫對象 let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") //2:設置動畫效果 rotationAnimation.fromValue = 0 rotationAnimation.toValue = M_PI * 2 rotationAnimation.repeatCount = MAXFLOAT rotationAnimation.isRemovedOnCompletion = false rotationAnimation.duration = 3 //3:將動畫添加到layear上 rotationImageView.layer.add(rotationAnimation, forKey: nil) } }
2:父類控制器代碼
import UIKit /* 總結: 1:爲何要抽父類RHBaseTableViewController?由於要顯示訪客視圖就要判斷用戶是否登錄,四個控制器界面都須要判斷,因此能夠將四個界面相同的業務邏輯抽到父類,讓子類去繼承(如果將業務邏輯封裝在工具類中,四個界面依然須要寫相同的代碼,因此考慮抽到父類,讓子類去繼承)四個界面的訪客視圖界面又是相似,因此能夠將訪客視圖的界面也封裝在父類中,訪客視圖封裝時把儘量顯示的控件所有添加到視圖中,而後父類提供訪客視圖的接口,讓子類去繼承,訪客視圖中再去封裝方法來控制不一樣界面訪客視圖的顯示和隱藏 2:在父類中定義屬性標識來判斷用戶是否登錄:1:在類中定義屬性的時候,必須給屬性一個初始值,或是定義爲可選類型,在以後初始化或是其餘地方再給該屬性賦值,Bool屬性只有兩個關鍵字true和false 2:懶加載:1:閉包懶加載 2:直接進行初始化 注意:1:懶加載用關鍵字lazy而且必須是var來修飾 2:在定義類屬性或是懶加載或是定義類中方法的時候,要考慮是否須要用private或是filePrivate來修飾來保證私有不讓外界訪問,private,只在當前的class類中能夠訪問,在同一個文件中不一樣class類中不能夠訪問,filePrivate在當前文件任何地方均可以訪問,兩者都不能在外界被訪問 3: 設置訪客視圖須要重寫控制器的方法loadView,在此方法中能夠設置控制器當前的view,當咱們須要重寫系統的方法的時候,須要加上override重寫標識,用三目運算,根據是否登錄的表示,來選擇加載不一樣的控制器的view,setupView()方法是建立訪客視圖的方法,通常將此方法都封裝在當前類的擴展中extension override func loadView() { //1:根據標識判斷是加載訪客視圖仍是登錄後界面 isLogin ? super.loadView() : setupView() } 4: //MARK:-4:設置訪客視圖的view extension RHBaseTableViewController { fileprivate func setupView() { view = visitorView } },fileprivate設置該方法爲私有,外界不能夠訪問 5:在viewDidLoad中設置左右導航欄按鈕:將設置導航欄左右按鈕也封裝在當前類中的extension中,在當前類中調用當前類的方法能夠省去self: //註冊按鈕 navigationItem.leftBarButtonItem = UIBarButtonItem(title: "註冊", style: .plain, target: self, action: #selector(RHBaseTableViewController.clickRegisterBtn)) 事件監聽:#selector(RHBaseTableViewController.clickRegisterBtn),通常也將事件的監聽封裝在當前類的extension中, 當監聽點擊事件的時候,才用 @objc,當swift中定義的方法用private或是fileprivate修飾的時候,則不會將該方法加載進類的方法列表,用@objc則會將該方法依然加入到方法列表中 extension RHBaseTableViewController { @objc fileprivate func clickRegisterBtn() { DLog(message: "點擊了左側的註冊按鈕") } 6:訪客視圖中的登錄註冊按鈕的監聽:1:如果將監聽方法放在訪客視圖的view中則須要協議代理,通知,閉包來進行反向監聽,還能夠在訪客視圖中提供兩個btn的屬性接口,在父類控制器中來設置監聽,在父類的控制器中來實現跳轉 **/ class RHBaseTableViewController: UITableViewController { //MARK:-1:定義屬性標識:用來判斷是顯示訪客視圖仍是登錄界面,默認是沒有登錄 let isLogin = false lazy var visitorView = RHVisitorView.visitorView() //MARK:-2:設置訪客視圖:重寫loadView方法,從而更改當前控制器的view override func loadView() { //1:根據標識判斷是加載訪客視圖仍是登錄後界面 isLogin ? super.loadView() : setupView() } override func viewDidLoad() { super.viewDidLoad() //MARK:-3:設置左右按鈕的導航欄 setupNavgationItem() //MARK:-4:添加登陸註冊按鈕監聽 //註冊按鈕 visitorView.registerBtn.addTarget(self, action: #selector(registBtnClick), for: .touchUpInside) //登陸按鈕 visitorView.loginBtn.addTarget(self, action: #selector(loginBtnClic), for: .touchUpInside) } } //MARK:-4:設置訪客視圖的view extension RHBaseTableViewController { fileprivate func setupView() { view = visitorView } } //MARK:-5:設置導航欄左右item extension RHBaseTableViewController { fileprivate func setupNavgationItem() { //註冊按鈕 navigationItem.leftBarButtonItem = UIBarButtonItem(title: "註冊", style: .plain, target: self, action: #selector(RHBaseTableViewController.clickRegisterBtn)) //登陸按鈕 navigationItem.rightBarButtonItem = UIBarButtonItem(title: "登陸", style: .plain, target: self, action: #selector(RHBaseTableViewController.clickLoginBtn)) } } //MARK:-6:監聽按鈕的點擊 extension RHBaseTableViewController { @objc fileprivate func clickRegisterBtn() { DLog(message: "點擊了左側的註冊按鈕") } @objc fileprivate func clickLoginBtn() { DLog(message: "點擊了右側的登陸按鈕") } @objc fileprivate func registBtnClick() { DLog(message: "點擊了註冊按鈕") } @objc fileprivate func loginBtnClic() { DLog(message: "點擊了登陸按鈕") } }
3:Appdelegate中設置Navbar的渲染
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { //MARK:-1:設置全局tab渲染顏色 UITabBar.appearance().tintColor = UIColor.orange //MARK:-2:設置全局導航欄渲染顏色 UINavigationBar.appearance().tintColor = UIColor.orange return true } } //MARK:-1:定義全局打印 func DLog<T> (fileName:String = #file,function:String = #function,lineNum:Int = #line, message:T) { #if DEBUG let fileNameComponse = (fileName as NSString).lastPathComponent print("\(fileNameComponse):\(function):\(lineNum):\("打印內容"):\(message)") #endif }
4:子類控制器中調用
import UIKit class HomeViewController: RHBaseTableViewController { override func viewDidLoad() { super.viewDidLoad() //MARK:-1:讓iconImageView旋轉 visitorView.rotationImage() } }
import UIKit class MessageViewController: RHBaseTableViewController { override func viewDidLoad() { super.viewDidLoad() //MARK:-1:更換圖片和title,取消動畫 visitorView.setupVisitorView("visitordiscover_image_message", title: "支持MJExtension,沒毛病,關注MJExtension,有驚喜") } }