iOS开发之视图控制器与导航模式

maker 8年前
   <h2><strong>模态视图</strong></h2>    <p>在导航过程中,有时候需要放弃主要任务而做其他次要任务,然后在返回到次要任务,这个次要任务就是在模态视图中完成的,如注册中主要任务是登录后进入主界面,如果用户没有注册,就要先去注册,注册是次要任务,当用户注册完成后,它会关闭注册视图,回到登录界面继续进行主要任务。</p>    <p>默认情况下,模态视图是从屏幕下方滑出来的。</p>    <p>负责控制器模态视图的控制器称为模态视图控制器,它并不是一个专门的类,它可以是前面提到的控制器的子类。负责主要任务视图的控制器称为主视图控制器。在UICOntrollerView中,主要有如下两个方法:</p>    <ul>     <li> <p>-present():呈现视图</p> </li>     <li> <p>-dismiss():关闭视图</p> </li>    </ul>    <p>在呈现模态视图时有两种方式:一是通过使用UIViewController的present方法实现;二是通过故事板的“过渡”(Segue)实现。</p>    <p>下面我们通过登录案例来介绍模态视图</p>    <h2><strong>步骤</strong></h2>    <ol>     <li> <p>创建一个iOS工程,将当前控制器嵌入到一个导航控制器中,具体步骤是:在故事板中选择View Controller,然后点击Xcode菜单栏Editor-Embed In-Navigation Controller菜单就会自动创建一个导航视图</p> </li>     <li> <p>点击导航栏,将导航栏的标题设为登录,然后从对象库中拖入Lable、TextField、Button等控件</p> </li>     <li> <p>接下来设计第二个界面,先从对象库中拖入一个View Controller到设计界面中,然后参考步骤1将该视图控制器嵌入到导航控制器中,修改该导航栏标题为注册,然后从对象库中拖入两个Bar Button Item到导航栏两边,分别设置identifier属性为Cancel和Save</p> </li>     <li> <p>接下来需要在登录场景和注册场景创建一个过渡,按住control键,从登录界面的注册按钮拖鼠标到注册导航控制器,然后松开鼠标,在弹出的视图框中选择Present Modally菜单;它是模态类型的过渡</p> </li>     <li> <p>最后,添加注册控制器类,创建一个类RegisterViewController集成UIViewController,然后回到故事板中将注册视图的Class选择为RegisterViewController</p> </li>    </ol>    <h2><strong>代码实现</strong></h2>    <p>ViewController.swift</p>    <pre>  <code class="language-objectivec">//  //  ViewController.swift  //  ModalViewSample  //  //  Created by Michael on 2016/11/9.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class ViewController: UIViewController {        @IBOutlet weak var mUserName: UITextField!      override func viewDidLoad() {          super.viewDidLoad()          // 注册消息          NotificationCenter.default.addObserver(self, selector: #selector(self.register(_ :)), name: NSNotification.Name(rawValue: "RegisterCompletion"), object: nil)      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.          NotificationCenter.default.removeObserver(self, name: NSNotification.Name.init(rawValue: "RegisterCompletion"), object: nil)      }            func register(_ notification : Notification) {                    let text = notification.userInfo?["username"] as? String          mUserName.text = text!          NSLog("%@",text!)                }  }</code></pre>    <p>RegisterViewController.swift</p>    <pre>  <code class="language-objectivec">//  //  RegisterViewController.swift  //  ModalViewSample  //  //  Created by Michael on 2016/11/9.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class RegisterViewController: UIViewController {        @IBOutlet weak var mName: UITextField!      override func viewDidLoad() {          super.viewDidLoad()            // Do any additional setup after loading the view.      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }            @IBAction func save(_ sender: Any) {          let userInfo = ["username":self.mName.text!]          //发送消息          NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "RegisterCompletion"), object: nil, userInfo: userInfo)          self.dismiss(animated: true, completion: {          })      }      @IBOutlet weak var save: UIBarButtonItem!            @IBAction func cancel(_ sender: Any) {          self.dismiss(animated: true, completion: {          })      }    }</code></pre>    <h2><strong>效果图</strong></h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b80168cd256c0ecd2200de89cce93b29.jpg"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1ed472929140647da02a2fdd64647e4b.gif"></p>    <h2><strong>分屏导航</strong></h2>    <p>基于分屏导航是平铺导航的主要实现方式,涉及的主要控件有分屏控件UIPageControll和屏幕滚动视图UIScrollView,一般不超过10屏</p>    <h2><strong>步骤</strong></h2>    <ol>     <li> <p>创建iOS工程,从对象库中拖入UIPageControll和UIScrollView到故事板中,并将其放到合适的位置,UIPageControll放在靠底部,UIScrollView全屏显示,将视图的背景设为黑色</p> </li>     <li> <p>选中UIScrollView的属性检查器,设置不显示Scroll View的Indicator,同时选择滚动Scrolling Enable和分屏Paging Enable。分屏属性是Scroll View每次滑动时翻一屏</p> </li>     <li> <p>选择Page Controll的属性检查器,设置Pages中的of pages总屏数为3,Current当前位置为0,并修改其宽度为300,它的高度是不能修改的。</p> </li>     <li> <p>最后为这两个控件定义输出口并连接注册到ViewController类中,为Page Controll控件定义响应屏幕变化事件的方法。-changPage</p> </li>    </ol>    <h2><strong>代码实现</strong></h2>    <pre>  <code class="language-objectivec">//  //  ViewController.swift  //  PageControlNavigation  //  //  Created by Michael on 2016/11/10.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class ViewController: UIViewController,UIScrollViewDelegate {        @IBOutlet weak var mScrollView: UIScrollView!      @IBOutlet weak var mPageControl: UIPageControl!            var mImage1: UIImageView!      var mImage2: UIImageView!      var mImage3: UIImageView!            override func viewDidLoad() {          super.viewDidLoad()          // Do any additional setup after loading the view, typically from a nib.          self.mScrollView.delegate = self          self.mScrollView.contentSize = CGSize(width: self.view.frame.size.width * 3, height: self.mScrollView.frame.size.height)          self.mScrollView.frame = self.view.frame                    self.mImage1 = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 480))          self.mImage1.image = UIImage(named: "达芬奇-蒙娜丽莎")          self.mScrollView.addSubview(mImage1)                    self.mImage2 = UIImageView(frame: CGRect(x: self.view.frame.size.width, y: 0, width: self.view.frame.size.width, height: 480))          self.mImage2.image = UIImage(named: "罗丹-思想者")          self.mScrollView.addSubview(mImage2)                      self.mImage3 = UIImageView(frame: CGRect(x: self.view.frame.size.width * 2, y: 0, width: self.view.frame.size.width, height: 480))          self.mImage3.image = UIImage(named: "保罗克利-肖像")          self.mScrollView.addSubview(mImage3)                  }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }            //UIPageControll事件处理      @IBAction func changePage(_ sender: Any) {          UIView.animate(withDuration: 0.3, animations: {              let whichPage = self.mPageControl.currentPage              //设置内容视图坐标原点与屏幕滚动视图坐原点的偏移量              self.mScrollView.contentOffset = CGPoint(x: 320 * whichPage, y: 0)          })      }            //屏幕滚动视图事件处理方法å      func scrollViewDidScroll(_ scrollView: UIScrollView) {          let offset = scrollView.contentOffset                    self.mPageControl.currentPage = Int(offset.x) / 320      }            }</code></pre>    <h2><strong>效果图</strong></h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/58136eca126044c440363ecaf0980f5f.gif"></p>    <h2><strong>分页控制器</strong></h2>    <p>在iOS5以后,我们可以使用分页控制器UIPageViewController构建类似于电子书效果的应用。</p>    <p>分页控制器需要放置在一个父视图控制器中,在分页控制器下面还要有子视图控制器,每个子视图控制器对应一个页面。</p>    <p>UIPageViewController没有对应的视图,我们需要使用代码来实现;需要在UIPageViewController所在的控制器实现UIPageViewControllerDelegate和UIPageViewControllerDataSource协议,UIPageViewControllerDataSource数据源协议必须要实现的方法有以下两个:</p>    <ul>     <li> <p>func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController)</p> </li>     <li> <p>func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController)</p> </li>    </ul>    <p>UIPageViewControllerDelegate委托协议中一般实现的方法是:</p>    <ul>     <li> <p>func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation)</p> </li>     <li> <p>func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)</p> </li>    </ul>    <h2><strong>步骤</strong></h2>    <ol>     <li> <p>创建一个iOS工程</p> </li>     <li> <p>代码实现UIPageViewController</p> </li>    </ol>    <h2><strong>代码实现</strong></h2>    <pre>  <code class="language-objectivec">//  //  ViewController.swift  //  PageNavigation  //  //  Created by Michael on 2016/11/10.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    enum DirectionFroward : Int {            case Before = 1 //向前      case After = 2 //向后        }    class ViewController: UIViewController,UIPageViewControllerDelegate,UIPageViewControllerDataSource {        //当前Page的索引      var mPageIndex = 0            var direct = DirectionFroward.After            var mPageViewController : UIPageViewController!      var mViewControllers : [UIViewController]!            override func viewDidLoad() {          super.viewDidLoad()                    let pageViewController1 = UIViewController()          let pageViewController2 = UIViewController()          let pageViewController3 = UIViewController()                    self.mViewControllers = [pageViewController1,pageViewController2,pageViewController3]                    let mImage1 = UIImageView(frame: self.view.frame)          mImage1.image = UIImage(named: "达芬奇-蒙娜丽莎")          pageViewController1.view.addSubview(mImage1)                    let mImage2 = UIImageView(frame: self.view.frame)          mImage2.image = UIImage(named: "罗丹-思想者")          pageViewController2.view.addSubview(mImage2)                              let mImage3 = UIImageView(frame: self.view.frame)          mImage3.image = UIImage(named: "保罗克利-肖像")          pageViewController3.view.addSubview(mImage3)                    //transitionStyle: pageCurl表示翻书效果样式 scroll 滑屏效果样式          //navigationOrientation 水平和垂直方向          self.mPageViewController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)            self.mPageViewController.delegate = self          self.mPageViewController.dataSource = self                    //设置首页          //direction forward向前 reverse向后          self.mPageViewController.setViewControllers([pageViewController1], direction: .forward, animated: true, completion: nil)                    self.view.addSubview(self.mPageViewController.view)      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }            //DataSource协议      func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {          NSLog("向前翻")          mPageIndex -= 1                    if mPageIndex < 0 {              mPageIndex = 0;              return nil          }                    direct = .Before          return self.mViewControllers[mPageIndex]                }        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {          NSLog("向后翻")            mPageIndex += 1                    if mPageIndex > 2 {              mPageIndex = 2              return nil          }                    direct = .After          return self.mViewControllers[mPageIndex]                }            //Delegate协议      func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation) -> UIPageViewControllerSpineLocation {          self.mPageViewController.isDoubleSided = false          //min和max 首页显示一个视图  mid首页显示两个视图          return .min      }          func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {                    //翻页未成功          if completed == false {              if direct == .After {                  mPageIndex -= 1              }                            if direct == .Before {                  mPageIndex += 1              }          }      }  }</code></pre>    <h2><strong>效果图</strong></h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1cb9106abee0e686a56d44a80b68ea21.gif"></p>    <h2><strong>标签导航</strong></h2>    <p>使用标签栏时有一定的指导原则:标签栏位于屏幕下方,占有49个像素屏幕空间,有时可以隐藏起来;标签栏中的标签不能超过5个,如果超过5个则最后一个显示为更多,点击更多标签会出现更多的列表。</p>    <h2><strong>步骤</strong></h2>    <p>在开发具体应用的时候,标签导航的各个标签分别代表一个功能模块,各功能模块之间相对独立。</p>    <ol>     <li> <p>创建一个iOS工程模板Tabbed Application应用,默认创建两个标签</p> </li>     <li> <p>从对象库中拖入一个ViewController到故事板中</p> </li>     <li> <p>添加ViewController和Tab Controller Scene的连线,具体操作是:按住control键从Tab Controller Scene拖曳鼠标到ViewController,释放鼠标,从弹出窗口中选择view controllers项即可</p> </li>     <li> <p>设置三个场景的标题</p> </li>     <li> <p>分别为三个场景创建三个视图类</p> </li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bf6c8d61e4288e0390ee3617d58fa34e.jpg"></p>    <h2><strong>代码实现</strong></h2>    <pre>  <code class="language-objectivec">//  //  HeiViewController.swift  //  TabNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class HeiViewController: UIViewController {        override func viewDidLoad() {          super.viewDidLoad()            // Do any additional setup after loading the view.      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }              /*      // MARK: - Navigation        // In a storyboard-based application, you will often want to do a little preparation before navigation      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {          // Get the new view controller using segue.destinationViewController.          // Pass the selected object to the new view controller.      }      */    }</code></pre>    <pre>  <code class="language-objectivec">//  //  JiViewController.swift  //  TabNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class JiViewController: UIViewController {        override func viewDidLoad() {          super.viewDidLoad()            // Do any additional setup after loading the view.      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }              /*      // MARK: - Navigation        // In a storyboard-based application, you will often want to do a little preparation before navigation      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {          // Get the new view controller using segue.destinationViewController.          // Pass the selected object to the new view controller.      }      */    }</code></pre>    <pre>  <code class="language-objectivec">//  //  LiaoViewController.swift  //  TabNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class LiaoViewController: UIViewController {        override func viewDidLoad() {          super.viewDidLoad()            // Do any additional setup after loading the view.      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }              /*      // MARK: - Navigation        // In a storyboard-based application, you will often want to do a little preparation before navigation      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {          // Get the new view controller using segue.destinationViewController.          // Pass the selected object to the new view controller.      }      */    }</code></pre>    <h2><strong>效果图</strong></h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/881d27d7a7e85c9dff1481740f27845b.gif"></p>    <h2><strong>树形导航</strong></h2>    <p>树形导航模式是将导航控制器UINavigationController与表视图结使用,主要用于构建从属关系的导航。下面我们创建一个三级视图的树形导航示例。</p>    <h2><strong>步骤</strong></h2>    <ol>     <li> <p>创建iOS工程,使用UINavigationController创建以及表视图</p> </li>     <li> <p>创建二级表视图CitiesViewController</p>      <ul>       <li> <p>从对象库中拖入一个Table View Controller到对象库中作为二级视图控制器</p> </li>       <li> <p>按住control键,从上一个Root View Controller的单元格中拖动鼠标到当前添加的Table View Controller中,释放鼠标,在弹出窗口中选择Show选项</p> </li>       <li> <p>选择连接线中的过渡Segue,打开其属性检查器,然后在Indentifier属性中输入ShowCities</p> </li>      </ul> </li>     <li> <p>创建三级视图DetailViewController</p>      <ul>       <li> <p>从对象库中拖入一个View Controller到对象库中作为三级视图控制器</p> </li>       <li> <p>按住control键,从上一个Table View Controller的单元格中拖动鼠标到当前添加的View Controller中,释放鼠标,在弹出窗口中选择Show选项</p> </li>       <li> <p>选择连接线中的过渡Segue,打开其属性检查器,然后在Indentifier属性中输入ShowDetail</p> </li>      </ul> </li>     <li> <p>设置各级视图的Table View Cell的属性</p> </li>    </ol>    <h2><strong>代码实现</strong></h2>    <p><strong>一级视图</strong></p>    <pre>  <code class="language-objectivec">//  //  ViewController.swift  //  TreeNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class ViewController: UITableViewController {            var dictData:NSDictionary!      var listData:NSArray!        override func viewDidLoad() {          super.viewDidLoad()          // Do any additional setup after loading the view, typically from a nib.          self.tableView.delegate = self          self.tableView.dataSource = self                    let path = Bundle.main.path(forResource: "provinces_cities", ofType: "plist")                    self.dictData = NSDictionary(contentsOfFile: path!)          self.listData = self.dictData.allKeys as NSArray          self.title = "省份信息"      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {          return self.listData.count      }            override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {                    let cell:UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "Custom", for: indexPath)                    let row = indexPath.row                    cell.textLabel?.text = self.listData[row] as? String                    return cell      }            //场景过渡之前的处理 点击表视图的单元格触发      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {          if segue.identifier == "ShowCities" {              let indexPath = self.tableView.indexPathForSelectedRow! as IndexPath              let selectedIndex = indexPath.row              //获取要跳转到的视图控制器对象              let controller = segue.destination as! CitiesTableViewController              let selectName = self.listData[selectedIndex] as! String              controller.listData = self.dictData[selectName] as! NSArray              controller.title = selectName                        }      }    }</code></pre>    <p><strong>二级视图</strong></p>    <pre>  <code class="language-objectivec">//  //  CitiesTableViewController.swift  //  TreeNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit    class CitiesTableViewController: UITableViewController {            var listData:NSArray!        override func viewDidLoad() {          super.viewDidLoad()      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }          override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {          // #warning Incomplete implementation, return the number of rows          return self.listData.count      }              override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {          let cell:UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "CityCell", for: indexPath)                    let row = indexPath.row          let dict = self.listData[row] as! NSDictionary          cell.textLabel?.text = dict["name"] as? String            return cell      }        //场景过渡之前的与处理      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {          if segue.identifier == "ShowDetail" {              let indexPath = self.tableView.indexPathForSelectedRow! as IndexPath              let selectIndex = indexPath.row              let dict = self.listData[selectIndex] as! NSDictionary              //获取要跳转到的视图控制器对象              let controller = segue.destination as! DetailViewController              controller.url = dict["url"] as! String              controller.title = dict["name"] as? String          }      }  }</code></pre>    <p><strong>三级视图</strong></p>    <pre>  <code class="language-objectivec">//  //  DetailViewController.swift  //  TreeNavigation  //  //  Created by Michael on 2016/11/15.  //  Copyright © 2016年 Michael. All rights reserved.  //    import UIKit  import WebKit    class DetailViewController: UIViewController,WKNavigationDelegate {            var url:String!      var webView: WKWebView!        override func viewDidLoad() {          super.viewDidLoad()          NSLog(url)          self.webView = WKWebView(frame: self.view.frame)          view.addSubview(webView)          webView.navigationDelegate = self          //let nUrl = URL(string: "https://baike.baidu.com/view/2172.htm")          let mUrl = URL(string: url)          let request = URLRequest(url: mUrl!)          webView.load(request)      }        override func didReceiveMemoryWarning() {          super.didReceiveMemoryWarning()          // Dispose of any resources that can be recreated.      }            func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {          NSLog("开始加载")      }            func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {          NSLog("内容开始返回时回调")      }            func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {          NSLog("加载完成")      }            func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {          NSLog("加载失败")      }  }</code></pre>    <h2><strong>效果图</strong></h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/63b6750f3b01e9b0dda0c47d2182dc91.gif"></p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007567382</p>    <p> </p>