iOS之引導頁和登錄界面的搭建思路

在市面上幾乎所有的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進行加載的時候就要判斷存放在本地的數據,根據這個數據來設置根控制器。
    代碼
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,807評論 25 708
  • 內容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 46,870評論 22 665
  • 一、 “我說你干啥呢,成天咋這忙,不知道給我打個電話。” “這不得上班么,咋了,你有事兒啊?!?“媽了個逼,你生日...
    若魚若茄閱讀 339評論 0 1
  • 對于CSRF,可能一些朋友比較陌生。我們下面先簡單介紹下。 什么是CSRF呢,我們看下Wikipedia的說明: ...
    侯樹成閱讀 5,381評論 0 2
  • 人生前二十年最大的意義就是我終于意識到了這二十年的無意義。
    加菲其實不是貓閱讀 98評論 0 1