一:訪客界面效果如圖
二:xib封裝訪客視圖的view
1:業務邏輯分析:1:由於用戶未登錄時要顯示訪客視圖,要先進行判斷用戶是否登錄,未登錄則顯示訪客視圖,登錄則顯示正常的登陸界面,由於要在四個子控制器界面的控制器中都要判斷是否顯示訪客視圖,同樣的邏輯,所以考慮抽成父類,把判斷是否顯示訪客視圖的邏輯封裝在父類中,讓子類去繼承。2:訪客視圖的界面如圖:將訪客視圖封裝在一個view中,view的界面相對固定,所以用xib搭建,首先將盡可能顯示的控件全部封裝在view的內部,再根據外界傳入的model或是在封裝類中定義方法來控制控件顯示的內容或是相應控件的顯示和隱藏,3:通過在父類控制器中重寫loadView方法判斷用戶是否登陸來設置是否加載訪客視圖
2:訪客視圖的搭建:新建文件繼承UIView,新建xib與新建的UIview類同名,首先來到xib中,將類與xib進行關聯,可以不設置freeform
1:1:先拖入UIimageView來設置旋轉圖片:先在如圖處設置xib約束,設置控件相對於父視圖水平居中豎直居中,在如圖所示位置水平豎直居中(此處1設置的是相對於父視圖的水平豎直居中,圖2水平豎直居中對齊,一般都是任意選中兩個控件,設置控件A相對於控件B的中心點水平豎直居中)。
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處設置水平豎直為保護區域,不產生拉伸
寬高約束不設置,讓按鈕根據內容大小來自定義寬高,在設置按鈕相對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,有驚喜") } }