iOS之引導頁和登入介面的搭建思路

請輸入賬號名發表於2019-03-04

在市面上幾乎所有的APP中,基本上都有個引導頁面,有的還具有一些登入的功能,更有甚者還需要選擇每個區域的伺服器的節點的操作,我們公司現在做的應用就有這種需求。

大致的實現思路

引導介面,一般都會放幾張輪播圖,我們將在輪播到最後一張圖片的時候顯示出跳轉到登入介面的按鈕,然後在登入介面中會建立選擇伺服器的按鈕,然後再次跳轉到選擇伺服器節點的介面。大致的流程就是這樣。
在輪播到最後一張圖片的時候,建立跳轉按鈕,在這個跳轉的操作中,一般會更改掉本個視窗的根控制器,將根控制器從引導介面轉為登入介面。當進行選擇伺服器節點的操作的時候,這裡就不需要進行根控制器的切換了。

具體的實現過程

此demo的資料,我是放在本地操作的,資料載入是通過plist檔案進行的。

1、搭建引導頁面

在引導頁面中,我們會建立一個輪播器,來展示輪播的圖片。在輪播介面中,往往需要隱藏導航欄的。

    /// 導航欄設定
    func wjNavgationSettings() {
        self.navigationController?.navigationBar.isHidden = true
    }
複製程式碼

在頁面即將要載入完成的時候就要隱藏掉導航欄。
導航欄隱藏掉後,為了頁面的美觀,一般也會隱藏掉狀態列,所以在當前控制器還需要新增隱藏狀態列的程式碼。

    // 隱藏狀態列
    override var prefersStatusBarHidden: Bool{
        return true
    }
複製程式碼

載入輪播圖片:

    self.images = NSMutableArray(capacity: 0)
    let imageArray = NSArray(contentsOfFile: Bundle.main.path(forResource: "data", ofType: "plist")!)
    if let imgArr = imageArray {
        self.images.addObjects(from: imgArr as Array)
    }
複製程式碼

這樣就把plist的資料獲取得到了。
搭建輪播器

let imageCount : Double = Double(self.images.count)
let screenW = self.view.frame.size.width
let screenH = self.view.frame.size.height
// 建立輪播圖
let scrollView = UIScrollView()
scrollView.frame = self.view.frame
scrollView.isPagingEnabled = true
scrollView.bounces = false
scrollView.contentSize = CGSize(width: Double(screenW) * imageCount, height: 0) // 頁面的展示的大小
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.backgroundColor = UIColor.brown
scrollView.delegate = self
self.view.addSubview(scrollView)
self.scrollView = scrollView
複製程式碼

建立圖片展示的imageView以及pageControl。
根據圖片的張數來迴圈建立imageView和建立pageControl

        // 建立圖片
        for i in 0..<Int(imageCount) {
            let imageView = UIImageView()
            imageView.image = UIImage(named: "gagi(i + 1)")
            imageView.frame = CGRect(x: Double(screenW) * Double(i), y: 0, width: Double(screenW), height: Double(screenH))
            imageView.isUserInteractionEnabled = true // 為的是響應鏈能夠被傳遞,不然後新增在其上面的按鈕的點選事件就不能被執行了
            scrollView.addSubview(imageView)
            self.imageView = imageView
        }
        // pageControl
        let pageControl = UIPageControl()
        pageControl.frame = CGRect(x: 137.5, y: 600.0, width: 100.0, height: 20.0) // 暫且寫死資料
        pageControl.isUserInteractionEnabled = false
        pageControl.hidesForSinglePage = true
        pageControl.currentPage = 0
        pageControl.numberOfPages = Int(imageCount)
        self.view .addSubview(pageControl)
        self.pageControl = pageControl
複製程式碼

以上程式碼應該就可以展示輪播的圖片,但是會有個問題就是在輪播圖片的時候,下面的pageControl不會有變化,所以就要scrollview遵守協議,在scrollview在停止滾動的時候就要去修改pageControl。

輪播圖建立

遵守UIScrollViewDelegate,我們單獨寫在一個extension中,然後需要遵守此協議。
在scrollview進行滑動的時候,頁面展示到最後一張圖片的時候,應該建立出跳轉按鈕。

extension wjGuideVC : UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // 頁面在移動過螢幕一般的時候就修改currentPage
        let page = Int(self.scrollView.contentOffset.x / scrollView.frame.size.width + 0.5) 
        self.pageControl.currentPage = page
        // 如果在最後一張照片出現的時候就要建立一個按鈕,進行點選,然後跳轉
        if page == self.images.count - 1 {
            let btn = UIButton(type: .custom)
            btn.frame = CGRect(x: 112.5, y: 500, width: 150.0, height: 50.0)
            btn.layer.masksToBounds = true
            btn.layer.cornerRadius = 10
            btn.backgroundColor = UIColor.cyan
            btn.setTitle("點 擊 進 行 跳 轉", for: .normal)
            btn.setTitleColor(UIColor.black, for: .normal)
            btn.addTarget(self, action: #selector(wjGuideVC.wjModifyRootVCAction(_ :)), for: .touchUpInside)
            self.imageView.addSubview(btn)
        }
    }
}
複製程式碼
跳轉按鈕建立

以上的程式碼需要注意的地方就是,這個按鈕是放在imageView上,也就是說如果沒有寫上imageView.isUserInteractionEnabled = true這句話,會導致這個事件的傳遞會被中斷,在點選到按鈕的時候,事件沒法進行響應,也就沒法事件傳遞到UIApplication,事件的分發也不會讓按鈕去處理,因為分發到imageView的時候就中斷了,整個事件也會拋棄掉。所以加上這就話,就能把事件傳遞下去,然後事件的分發也會找到相應的按鈕去執行。
以上的程式碼就基本完成了整個引導頁面的UI搭建,但在按鈕的點選事件中,該如何處理這個修改根控制器。
接下來就要知曉是誰要修改掉根控制器。
在app中,整個視窗就是個window,需要修改根控制器的也是這個window(其實window在程式中還有別的)。

在點選按鈕後的操作:

  • 讓導航欄顯示出來
  • 記錄引導頁面已經顯示過的資料存到本地去(還可以將本個app的版本號存到本地去)
  • 修改根控制器
  • 頁面的跳轉
    // 修改根控制器的按鈕點選事件
    func wjModifyRootVCAction(_ btn : UIButton) {
        let vc = ViewController()
        vc.navigationController?.navigationBar.isHidden = false
        // 把已經出現過的引導頁的結果記錄到本地
        UserDefaults.standard.set(true, forKey: "isShowGuidePage")
        // 這個地方完全可以將app的version也存到本地去,然後在每次進入到app的時候就判斷版本號
        let app = AppDelegate()
        let nav = UINavigationController(rootViewController: vc)
        app.window?.rootViewController = nav
        self.present(nav, animated: true, completion: nil)
    }
複製程式碼

由於這個頁面的跳轉是通過present的形式跳轉的,所以在跳轉的時候會自動隱藏掉導航欄,即便是在下個控制器中讓導航欄顯示出來,也會隱藏掉的,所以要在下個介面顯示出導航欄,就要推出整個導航控制器,這樣就不會隱藏掉導航欄。
以上就是引導頁面的全部邏輯。
在程式載入的時候,第一次載入會去載入檢視本地有沒存入引導頁顯示的記錄,如果有的話就就直接使登入介面作為根控制器,如果沒有就使得引導頁面作為根控制器。

    func wjRootVCSettings() { // 可以將版本號也作為參考的依據。
        let isShowGuide = UserDefaults.standard.bool(forKey: "isShowGuidePage")
        if isShowGuide == true {
            let vc = ViewController()
            let nav = UINavigationController(rootViewController: vc)
            self.window?.rootViewController = nav            
        } else {
            let guideVC = wjGuideVC()
            let nav = UINavigationController(rootViewController: guideVC)
            self.window?.rootViewController = nav
        }
    }
複製程式碼

2、登入介面

其實登入頁面沒啥好說,就demo而言就新增了一個跳轉按鈕還有兩個textField。為了方便除錯,不至於每次刪掉app再進行除錯,做了個重置的操作的按鈕,是放在導航欄上的。

    // 導航欄設定
    func wjNavigationSettings() {
        self.title = "登入介面"
        // 恢復isShowGuidePage為false
        let btn = UIButton(type: .custom)
        btn.setTitle("重置", for: .normal)
        btn.setTitleColor(UIColor.black, for: .normal)
        btn.bounds = CGRect(x: 0, y: 0, width: 50, height: 30)
        btn.addTarget(self, action: #selector(ViewController.wjModifyDataAction(_ :)), for: .touchUpInside)
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: btn)
    }

    // 修改存在本地的資料
    func wjModifyDataAction(_ btn : UIButton) {
        UserDefaults.standard.set(false, forKey: "isShowGuidePage")
        self.dismiss(animated: true, completion: nil) // 只有當從引導頁跳轉過來才有效,如果是再次執行demo跳轉進來的是無效的。堆疊中是沒有建立引導頁的。
    }
複製程式碼
登入頁面展示

3、伺服器選擇介面

也就是建立了兩個textField,當兩個輸入框都有值的時候,按鈕才能進行點選,載入資料。如果沒有輸入內容會有相應的提示。

提示輸入內容

當輸入框均有內容的時候,才載入tableView,然後在去載入資料,同樣資料是通過plist檔案進行載入的。

    // 資料載入
    // 懶載入
    lazy var wjServerListArr : NSMutableArray = {
        let dataArray = NSArray(contentsOfFile: Bundle.main.path(forResource: "serverListData", ofType: "plist")!)
        let wjServerListArr = NSMutableArray(capacity: 0)
        for dict in dataArray! {
            let dataDict = dict as! [String : String]
            let model = wjServerModel().wjServerModelWithDict(dataDict)
            wjServerListArr.add(model)
        }
        return wjServerListArr
    }()
複製程式碼

介面的建立

    func wjCreatTableView() {
        let screenW = self.view.frame.size.width
        let screenH = self.view.frame.size.height
        let rect = CGRect(x: 0, y: 250, width: Double(screenW), height: Double(screenH - 250))
        UIView.animate(withDuration: 0.5) { 
            self.tableView = UITableView(frame: rect, style: UITableViewStyle.plain)
            self.tableView.backgroundColor = UIColor.white
            self.tableView.delegate = self
            self.tableView.dataSource = self
            self.view.addSubview(self.tableView)
        }
    }
複製程式碼

協議的遵守,同樣是放在extension中進行處理。便於管理。

// MARK:- UITableViewDataSource
extension wjServerSelectVC : UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.wjServerListArr.count;
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let iden = "serverCell"
        var cell = tableView.dequeueReusableCell(withIdentifier: iden)
        if cell == nil {
            cell = UITableViewCell(style: .subtitle, reuseIdentifier: iden)
        }
        let model = self.wjServerListArr[indexPath.row] as! wjServerModel
        cell?.textLabel?.text = model.serverName
        cell?.detailTextLabel?.text = model.serverIP
        return cell!
    }
}

// MARK:- UITableViewDelegate
extension wjServerSelectVC : UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 建立彈出框
//        let model = self.wjServerListArr[indexPath.row] as! wjServerModel
//        print("index is : (model.serverName) : (model.serverIP)")
        self.wjShowMessage(indexPath, "選擇此伺服器?") { (model) in
            print("index is : (model.serverName) : (model.serverIP)")
            self.dismiss(animated: true, completion: nil)
        }
    }
}
複製程式碼
獲取資料並展示以及點選效果

總結

其實整個demo的難度不大,也就是一些點需要注意。

  • 在建立scrollview的時候,整個頁面的展示的大小和pageControl是根據圖片的張數確定的。
  • 在建立引導頁面的時候,隱藏掉導航欄和狀態列的方法。
  • 在跳轉的時候,需要把導航欄顯示出來的方法,就是退出導航控制器,而不是登入介面的控制器。
  • 需要在app進行載入的時候就要判斷存放在本地的資料,根據這個資料來設定根控制器。
    程式碼

相關文章