之前,我整理過OC的5種基本界面?zhèn)髦捣绞?很多朋友都有很喜歡,今天再整理一下Swift中的7種常用界面?zhèn)髦倒┐蠹覅⒖?不到之處,歡迎各位提點(diǎn),更多的希望能對(duì)大家有所幫助.
注: 文中 ,第一個(gè)界面為ViewController,第二個(gè)界面為DetailViewController兩個(gè)界面都會(huì)分別有一個(gè)Button和一個(gè)TextFiled 用于輸入值和接受值.來查看傳值效果
兩個(gè)界面的簡(jiǎn)單搭建及跳轉(zhuǎn)操作如下
//第一個(gè)界面ViewController
import UIKit
class ViewController: UIViewController {
fileprivate var tf:UITextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
}
//點(diǎn)擊進(jìn)入第二個(gè)界面的按鈕進(jìn)行跳轉(zhuǎn)操作
@objc fileprivate func btnAction() {
let vc = DetailViewController()
navigationController?.pushViewController(vc, animated: true)
}
}
//設(shè)置界面
extension ViewController{
fileprivate func setUpUI() {
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
btn.center = self.view.center
btn.backgroundColor = UIColor.cyan
btn.addTarget(self, action: #selector(btnAction), for: .touchUpInside)
btn.setTitle("進(jìn)第二個(gè)界面", for: UIControlState.normal)
self.view .addSubview(btn)
tf = UITextField(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
tf.center = self.view.center
tf.frame.origin.y = 200
tf.backgroundColor = UIColor.blue
self.view.addSubview(tf)
}
}
//第二個(gè)界面DetailViewController
import UIKit
class DetailViewController: UIViewController {
var tf:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
setUpUI()
}
//點(diǎn)擊按鈕返回上一界面
func btnAction() {
navigationController?.popViewController(animated: true)
}
extension DetailViewController{
fileprivate func setUpUI() {
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
btn.center = self.view.center
btn.backgroundColor = UIColor.cyan
btn.addTarget(self, action: #selector(btnAction), for: .touchUpInside)
btn.setTitle("返回第一個(gè)界面", for: UIControlState.normal)
self.view .addSubview(btn)
tf = UITextField(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
tf.center = self.view.center
tf.frame.origin.y = 200
tf.text = tfStr
tf.backgroundColor = UIColor.blue
self.view.addSubview(tf)
}
}
上面??界面搭建,下面進(jìn)行的幾種傳值操作都是以這兩個(gè)界面為基礎(chǔ)分別進(jìn)行的
1.屬性傳值
屬性傳值的方式特別簡(jiǎn)單,其實(shí)現(xiàn)的基本思路,就是為創(chuàng)建對(duì)象并為其屬性賦值,同樣的,我們模擬從第一個(gè)界面?zhèn)髦档降诙€(gè)界面,首先我們?cè)诘诙€(gè)界面聲明一個(gè)String類型的tfStr屬性。
import UIKit
class DetailViewController: UIViewController {
var tf:UITextField!
//String類型的tfStr屬性
var tfStr:String!
override func viewDidLoad() {
super.viewDidLoad()
}
}
然后在處理第一個(gè)界面跳轉(zhuǎn)到第二個(gè)界面的方法中實(shí)例化第二個(gè)界面對(duì)象,并為其屬性賦值,如下所示
func btnAction() {
let vc = DetailViewController()
vc.tfStr = tf.text
navigationController?.popViewController(animated: true)
}
接下來,我們即可直接在第二個(gè)界面中的viewDidLoad()方法中如下操作實(shí)現(xiàn)傳值。
tf.text = tfStr
??你以學(xué)會(huì)屬性傳值
2.構(gòu)造器傳值
構(gòu)造器傳值,類似于OC的自定義初始化傳值,即在初始化某一個(gè)視圖控制器的時(shí)候,將需要傳遞的數(shù)據(jù)傳遞過去,因此,我們同樣可以模擬從第一個(gè)面?zhèn)髦档降诙€(gè)界面。
首先,我們需要在第二個(gè)界面中,自定義構(gòu)造器,具體實(shí)現(xiàn)方式如下
class DetailViewController: UIViewController {
var tfStr:String!
var tf:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
setUpUI()
}
// 析構(gòu)器
deinit {
print(#function)
}
// 自定義構(gòu)造器
init(text:String) {
print(#function)
print(text)
tfStr = text
// 指定構(gòu)造器必須調(diào)用它最近父類的指定構(gòu)造器
super.init(nibName: nil, bundle: nil)
}
// init(coder aDecoder: NSCoder) 方法是來自父類的指定構(gòu)造器, 因?yàn)檫@個(gè)構(gòu)造器是 required, 必須要實(shí)現(xiàn)。但是因?yàn)槲覀円呀?jīng)重載了 init(), 定義了一個(gè)指定構(gòu)造器, 所以這個(gè)方法不會(huì)被繼承, 需要要手動(dòng)覆寫。
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func btnAction() {
navigationController?.popViewController(animated: true)
}
}
自定義構(gòu)造器創(chuàng)建好之后,我們即可在第一個(gè)界面處理界面跳轉(zhuǎn)的方法中通過自定義構(gòu)造器創(chuàng)建對(duì)象并傳值了,如下所示
//點(diǎn)擊鈕進(jìn)行跳轉(zhuǎn)操作方法
@objc fileprivate func btnAction() {
let vc = DetailViewController(text: tf.text!)
navigationController?.pushViewController(vc, animated: true)
}
??你以學(xué)會(huì)構(gòu)造器傳值
3.通知傳值
我們來看看通知傳值,通知可實(shí)現(xiàn)任意界面之間的數(shù)據(jù)傳遞,但必須滿足一個(gè)條件,就是保證在發(fā)送通知的時(shí)候監(jiān)聽者已經(jīng)存在(先要注冊(cè)通知)。而通知的注冊(cè)與OC寫法有所不同Swift種主要通過NotificationCenter 而不是NSNotificationCenter通知中心實(shí)現(xiàn)(其實(shí)就是寫法不同),其為一個(gè)系統(tǒng)單例,系統(tǒng)提供了default方法獲取通知實(shí)例對(duì)象。
通知使用步驟:注冊(cè)通知 -> 發(fā)送通知 -> 移除通知
通知實(shí)現(xiàn)的原理,我們可以這樣去理解,學(xué)生監(jiān)聽下課鈴聲。我們把學(xué)生看做監(jiān)聽者(或者叫觀察者),監(jiān)聽鈴聲,鈴聲一響就放學(xué)。當(dāng)鈴聲響起時(shí),我們看做發(fā)出一個(gè)通知(信號(hào)),學(xué)生在監(jiān)聽到鈴聲之后就會(huì)做出相應(yīng)的操作,比如放學(xué)之后做什么……
接下來我們看看通知傳值的具體實(shí)現(xiàn)方式。這里我們模擬從第二個(gè)界面?zhèn)髦档降谝粋€(gè)界面
首先我們需要在第一個(gè)界面注冊(cè)通知,因?yàn)?,程序運(yùn)行肯定是先到第一個(gè)界面中,所以,當(dāng)在第二個(gè)界面發(fā)送通知的時(shí)候,通知監(jiān)聽者肯定是存在的。注冊(cè)通知的方法常用的有以下兩種:
//1. open func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)
//2. open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil)
第1種,我們需要通過Selector參數(shù)設(shè)置接收到通知時(shí)觸發(fā)的方法。
而第2種,我們無需關(guān)聯(lián)觸發(fā)方法,在方法尾部跟著一個(gè)閉包,當(dāng)接收到通知的時(shí)候該閉包會(huì)自動(dòng)調(diào)用,我們可直接在閉包內(nèi)處理相應(yīng)的邏輯即可。第2種方法還有一個(gè)參數(shù)queue,該參數(shù)主要設(shè)置通知觸發(fā)方法執(zhí)行的隊(duì)列,其為OperationQueue類型的對(duì)象,這里我們一般在主隊(duì)列執(zhí)行,配置參數(shù)方法為OperationQueue.main。我們可以直觀的看到,在兩種方法中都有一個(gè)name參數(shù),該參數(shù)我們可以理解為通知的代號(hào),通過這個(gè)代號(hào)我們可以避免多個(gè)通知串聯(lián),這個(gè)參數(shù)我們可以賦值任意字符串。我們這里實(shí)例需要傳值而不是接到通知去進(jìn)行一個(gè)事件所以這里以第2種為例。
按照上面提到的通知使用步驟:
(1)注冊(cè)通知
注冊(cè)通知我們?cè)诘谝粋€(gè)界面中
NotificationCenter.default.addObserver(forName: NSNotification.Name?, object: Any?, queue: OperationQueue?) { (Notification) in
//拿到Notification內(nèi)容進(jìn)行邏輯處理....
}
(2)發(fā)送通知
通知注冊(cè)好之后,下一步我們就可以在第二個(gè)界面發(fā)送通知了,我們?cè)谔幚斫缑嫣D(zhuǎn)(返回)的方法中處理這一邏輯。發(fā)送通知主要用到以下方法:
open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil)
這里需要注意,發(fā)送通知的aName參數(shù),必須和注冊(cè)通知時(shí)的name參數(shù)一致,否則在第一個(gè)界面將無法接收到通知。我們可通過aUserInfo參數(shù)將需要傳遞的數(shù)據(jù)傳遞到第一個(gè)界面中,該參數(shù)為一個(gè)[NSObject : AnyObject]?(字典)類型的數(shù)據(jù)。實(shí)現(xiàn)示例如下:
func btnAction() {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notifyName"), object: nil, userInfo: ["text": self.tf.text])
navigationController?.popViewController(animated: true)
}
當(dāng)用戶點(diǎn)擊返回按鈕時(shí),發(fā)送通知,第一個(gè)界面接收到對(duì)應(yīng)通知之后將會(huì)回調(diào)閉包,我們可在閉包中獲得數(shù)據(jù),如下所示:
class ViewController: UIViewController {
fileprivate var tf:UITextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "notifyName"), object: nil, queue: OperationQueue.main) { (text) in
//這里拿到通知傳來的數(shù)據(jù)text,然后進(jìn)行邏輯處理,我們這里是為tf.text 賦值
self.tf.text = "\(text.userInfo?["text"])"
}
setUpUI()
}
(3)移除通知
我們已經(jīng)基本實(shí)現(xiàn)通知傳值了,我們還需要最后一步,移除通知,通知的移除我們可在界面釋放的方法(析構(gòu)方法)中去執(zhí)行,如下所示:
deinit {
NotificationCenter.default.removeObserver(self)
}
??你以學(xué)會(huì)通知傳值
4.單例傳值
單例,我們可以簡(jiǎn)單理解為“一個(gè)類,一個(gè)實(shí)例”。因此,不管我們?cè)谑裁吹胤絼?chuàng)建單例,所得到的都是同一個(gè)實(shí)例,根據(jù)這一特點(diǎn),我們可通過單例進(jìn)行傳值,但是注意的是,單例傳值,在我們獲取單例值的時(shí)候首先必須保證單例確實(shí)有值,因此,我們可模擬通過單例實(shí)現(xiàn)從第一個(gè)界面?zhèn)髦档降诙€(gè)界面的場(chǎng)景。
要使用單例傳值,我們必須得創(chuàng)建單例,首先我們需要建立一個(gè)Swift文件,建立步驟:command + n -> iOS ->Swift File,為其取名為Singleton。然后我們?cè)谠撐募袠?gòu)造單例類,具體構(gòu)造方式如下:
import Foundation
class Singleton{
// 單例屬性,用于傳值;
var text : String!
static let singleton = Singleton()
// 獲取單例實(shí)例方法
func share() -> Singleton {
return .singleton
}
// 私有化init初始化方法,防止通過此方法創(chuàng)建對(duì)象
private init(){
}
}
這里需要注意的是,我們可將需要傳遞的數(shù)據(jù)設(shè)置成單例的屬性,如上述代碼中的text屬性。在第一個(gè)界面為其賦值之后我們就可以在第二個(gè)界面訪問了
//第一個(gè)界面賦值
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
let singleton = Singleton.singleton.share()
singleton.text = "我叫MangoJ"
}
//第二個(gè)界面任意位置都可獲取單例屬性的值本例在viewDidLoad中
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
tfStr = Singleton.singleton.share().text
print(tfStr)
setUpUI()
}
??你以學(xué)會(huì)單例傳值
5.協(xié)議傳值
協(xié)議傳值,主要用于代理模式。假設(shè)我們要實(shí)現(xiàn)從第二個(gè)界面?zhèn)髦档降谝粋€(gè)界面這一需求,首先,我們需要擬定一份協(xié)議,為了方便,我們可直接在第二個(gè)界面中擬定協(xié)議,如下所示:
基本步驟:
1.聲明協(xié)議
2.設(shè)置協(xié)議中的方法
3.聲明代理
import UIKit
//1.聲明協(xié)議
@objc protocol DetailViewControllerDelegate {
//2.設(shè)置協(xié)議中的方法
@objc optional func viewController(viewController:DetailViewController,popWithValue:String)->Void
}
@objc關(guān)鍵字標(biāo)識(shí)該協(xié)議為一個(gè)可選協(xié)議;optional關(guān)鍵字標(biāo)識(shí)該協(xié)議方法對(duì)于協(xié)議的遵守者而言不是必須實(shí)現(xiàn)的。
聲明了協(xié)議之后,我們需要為第二個(gè)界面聲明一個(gè)代理屬性,如下所示:
class DetailViewController: UIViewController {
//3.聲明協(xié)議屬性
// 代理屬性delegate值為實(shí)現(xiàn)了DetailViewControllerDelegate協(xié)議的任意對(duì)象,weak關(guān)鍵字主要為了防止循環(huán)引用導(dǎo)致對(duì)象無法釋放。
weak var delegate : DetailViewControllerDelegate!
var tfStr:String!
var tf:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
setUpUI()
}
func btnAction() {
navigationController?.popViewController(animated: true)
}
}
聲明了代理屬性之后,我們需要在處理界面跳轉(zhuǎn)(返回)的方法中處理協(xié)議傳值的邏輯了。首先我們需要判斷代理人是否存在,可通過可選綁定來操作,如果代理存在,則讓代理執(zhí)行協(xié)議方法,并且將需要傳遞的信息通過參數(shù)傳遞給代理所在的界面,如下所示:
func btnAction() {
if let delegate = self.delegate {
delegate.viewController!(viewController: self, popWithValue: "MangoJ")
}
navigationController?.popViewController(animated: true)
}
切換到第一個(gè)界面中,在處理界面跳轉(zhuǎn)的方法中,我們將第二個(gè)界面的代理屬性設(shè)為第一個(gè)界面,如下所示:
//點(diǎn)擊進(jìn)入第二個(gè)界面的按鈕進(jìn)行跳轉(zhuǎn)操作
@objc fileprivate func btnAction() {
let vc = DetailViewController()
//設(shè)置代理
vc.delegate = self
navigationController?.pushViewController(vc, animated: true)
}
然后,實(shí)現(xiàn)協(xié)議方法,在協(xié)議方法中,我們可以直接獲取從第二個(gè)界面?zhèn)鬟f過來的值。
// MARK:- DetailViewControllerDelegate -
func viewController(viewController: DetailViewController, popWithValue: String) {
tf.text = popWithValue
}
??你以學(xué)會(huì)代理傳值
6.閉包傳值
閉包主要用于回調(diào),這里我們還是模擬從第二個(gè)界面?zhèn)髦档降谝粋€(gè)界面,首先我們需要在第二個(gè)界面為閉包取個(gè)別名,聲明一個(gè)閉包類型,如下所示:
使用閉包的步驟:
1.聲明閉包類型
2.聲明閉包屬性
3.設(shè)置閉包傳值調(diào)用方法
4.賦值閉包屬性
import UIKit
class DetailViewController: UIViewController {
// 1、聲明閉包類型
typealias ClosureName = (String)->()
// 2、聲明閉包屬性
var closure : ClosureName!
var tfStr:String!
var tf:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
setUpUI()
}
3.設(shè)置閉包傳值調(diào)用方法
func callBack(closure:@escaping ClosureName) {
self.closure = closure
}
func btnAction() {
5、可選綁定:判斷closure屬性是否不為nil,如果不為nil,則通過closure將文本信息回調(diào)到調(diào)用closure方法所在的控制器中;
guard (self.closure != nil) else {
return
}
closure(tf.text!)
navigationController?.popViewController(animated: true)
}
}
現(xiàn)在第二個(gè)界面已經(jīng)配置完畢,最后一步,我們?cè)诘谝粋€(gè)界面推送到第二個(gè)界面的方法中,通過實(shí)例化的第二界面對(duì)象,調(diào)用閉包回調(diào)方法,然后打印數(shù)據(jù)即可,該方法在第二界面返回到第一界面的時(shí)候會(huì)直接被調(diào)用,代碼如下
//第一個(gè)界面的如下方法中
@objc fileprivate func btnAction() {
let vc = DetailViewController()
vc.callBack { (value) in
self.tf.text = value
}
navigationController?.pushViewController(vc, animated: true)
}
??你以學(xué)會(huì)閉包傳值
7.NSUserDefaults傳值
UserDefaults為系統(tǒng)單例,通過UserDefaults傳值和自定義單例傳值原理基本一致。對(duì)于UserDefaults傳值,首先在獲取值的地方,必須保證單例實(shí)例key對(duì)應(yīng)的數(shù)據(jù)確實(shí)有值才行,同樣的,我們模擬從第一個(gè)界面?zhèn)髦档降诙€(gè)界面
使用步驟:
1.獲取UserDefaults單例實(shí)例
2.為UserDefaults實(shí)例設(shè)值
3.同步數(shù)據(jù)
在第一個(gè)界面的該方法中有如下操作
@objc fileprivate func btnAction() {
// 獲取defaults單例實(shí)例
let defaults = UserDefaults.standard
//設(shè)值
defaults.setValue(tf.text, forKey: "text")
// 同步數(shù)據(jù)
defaults.synchronize()
let vc = DetailViewController()
navigationController?.pushViewController(vc, animated: true)
}
在第二個(gè)界面的該方法中有如下操作
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
let defalts = UserDefaults.standard
guard let text = defalts.object(forKey: "text") as! String? else {
return
}
tfStr = text
setUpUI()
}
通過UserDefaults傳值,不僅僅只局限于傳遞字符串類型的數(shù)據(jù),同時(shí)可傳遞集合類型(字典、數(shù)組、集)或者基本數(shù)據(jù)類型的數(shù)據(jù)