iOS開發之視圖控制器與導航模式
模態視圖
在導航過程中,有時候需要放棄主要任務而做其他次要任務,然后在返回到次要任務,這個次要任務就是在模態視圖中完成的,如注冊中主要任務是登錄后進入主界面,如果用戶沒有注冊,就要先去注冊,注冊是次要任務,當用戶注冊完成后,它會關閉注冊視圖,回到登錄界面繼續進行主要任務。
默認情況下,模態視圖是從屏幕下方滑出來的。
負責控制器模態視圖的控制器稱為模態視圖控制器,它并不是一個專門的類,它可以是前面提到的控制器的子類。負責主要任務視圖的控制器稱為主視圖控制器。在UICOntrollerView中,主要有如下兩個方法:
-
-present():呈現視圖
-
-dismiss():關閉視圖
在呈現模態視圖時有兩種方式:一是通過使用UIViewController的present方法實現;二是通過故事板的“過渡”(Segue)實現。
下面我們通過登錄案例來介紹模態視圖
步驟
-
創建一個iOS工程,將當前控制器嵌入到一個導航控制器中,具體步驟是:在故事板中選擇View Controller,然后點擊Xcode菜單欄Editor-Embed In-Navigation Controller菜單就會自動創建一個導航視圖
-
點擊導航欄,將導航欄的標題設為登錄,然后從對象庫中拖入Lable、TextField、Button等控件
-
接下來設計第二個界面,先從對象庫中拖入一個View Controller到設計界面中,然后參考步驟1將該視圖控制器嵌入到導航控制器中,修改該導航欄標題為注冊,然后從對象庫中拖入兩個Bar Button Item到導航欄兩邊,分別設置identifier屬性為Cancel和Save
-
接下來需要在登錄場景和注冊場景創建一個過渡,按住control鍵,從登錄界面的注冊按鈕拖鼠標到注冊導航控制器,然后松開鼠標,在彈出的視圖框中選擇Present Modally菜單;它是模態類型的過渡
-
最后,添加注冊控制器類,創建一個類RegisterViewController集成UIViewController,然后回到故事板中將注冊視圖的Class選擇為RegisterViewController
代碼實現
ViewController.swift
//
// 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>
RegisterViewController.swift
//
// 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>
效果圖


分屏導航
基于分屏導航是平鋪導航的主要實現方式,涉及的主要控件有分屏控件UIPageControll和屏幕滾動視圖UIScrollView,一般不超過10屏
步驟
-
創建iOS工程,從對象庫中拖入UIPageControll和UIScrollView到故事板中,并將其放到合適的位置,UIPageControll放在靠底部,UIScrollView全屏顯示,將視圖的背景設為黑色
-
選中UIScrollView的屬性檢查器,設置不顯示Scroll View的Indicator,同時選擇滾動Scrolling Enable和分屏Paging Enable。分屏屬性是Scroll View每次滑動時翻一屏
-
選擇Page Controll的屬性檢查器,設置Pages中的of pages總屏數為3,Current當前位置為0,并修改其寬度為300,它的高度是不能修改的。
-
最后為這兩個控件定義輸出口并連接注冊到ViewController類中,為Page Controll控件定義響應屏幕變化事件的方法。-changPage
代碼實現
//
// 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>
效果圖

分頁控制器
在iOS5以后,我們可以使用分頁控制器UIPageViewController構建類似于電子書效果的應用。
分頁控制器需要放置在一個父視圖控制器中,在分頁控制器下面還要有子視圖控制器,每個子視圖控制器對應一個頁面。
UIPageViewController沒有對應的視圖,我們需要使用代碼來實現;需要在UIPageViewController所在的控制器實現UIPageViewControllerDelegate和UIPageViewControllerDataSource協議,UIPageViewControllerDataSource數據源協議必須要實現的方法有以下兩個:
-
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController)
-
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController)
UIPageViewControllerDelegate委托協議中一般實現的方法是:
-
func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation)
-
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
步驟
-
創建一個iOS工程
-
代碼實現UIPageViewController
代碼實現
//
// 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>
效果圖

標簽導航
使用標簽欄時有一定的指導原則:標簽欄位于屏幕下方,占有49個像素屏幕空間,有時可以隱藏起來;標簽欄中的標簽不能超過5個,如果超過5個則最后一個顯示為更多,點擊更多標簽會出現更多的列表。
步驟
在開發具體應用的時候,標簽導航的各個標簽分別代表一個功能模塊,各功能模塊之間相對獨立。
-
創建一個iOS工程模板Tabbed Application應用,默認創建兩個標簽
-
從對象庫中拖入一個ViewController到故事板中
-
添加ViewController和Tab Controller Scene的連線,具體操作是:按住control鍵從Tab Controller Scene拖曳鼠標到ViewController,釋放鼠標,從彈出窗口中選擇view controllers項即可
-
設置三個場景的標題
-
分別為三個場景創建三個視圖類

代碼實現
//
// 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>
//
// 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>
//
// 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>
效果圖

樹形導航
樹形導航模式是將導航控制器UINavigationController與表視圖結使用,主要用于構建從屬關系的導航。下面我們創建一個三級視圖的樹形導航示例。
步驟
-
創建iOS工程,使用UINavigationController創建以及表視圖
-
創建二級表視圖CitiesViewController
-
從對象庫中拖入一個Table View Controller到對象庫中作為二級視圖控制器
-
按住control鍵,從上一個Root View Controller的單元格中拖動鼠標到當前添加的Table View Controller中,釋放鼠標,在彈出窗口中選擇Show選項
-
選擇連接線中的過渡Segue,打開其屬性檢查器,然后在Indentifier屬性中輸入ShowCities
-
創建三級視圖DetailViewController
-
從對象庫中拖入一個View Controller到對象庫中作為三級視圖控制器
-
按住control鍵,從上一個Table View Controller的單元格中拖動鼠標到當前添加的View Controller中,釋放鼠標,在彈出窗口中選擇Show選項
-
選擇連接線中的過渡Segue,打開其屬性檢查器,然后在Indentifier屬性中輸入ShowDetail
-
設置各級視圖的Table View Cell的屬性
代碼實現
一級視圖
//
// 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>
二級視圖
//
// 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>
三級視圖
//
// 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>
效果圖

來自:https://segmentfault.com/a/1190000007567382