美文网首页
iOS之引导页和登录界面的搭建思路

iOS之引导页和登录界面的搭建思路

作者: 请输入账号名 | 来源:发表于2017-08-03 10:35 被阅读364次

    在市面上几乎所有的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进行加载的时候就要判断存放在本地的数据,根据这个数据来设置根控制器。
      代码

    相关文章

      网友评论

          本文标题:iOS之引导页和登录界面的搭建思路

          本文链接:https://www.haomeiwen.com/subject/qjvulxtx.html