Swift中的7种界面传值方式总结(属性传值,构造器传值,通知传值,单例传值,协议传值,闭包传值,NSUserDefaults传值)

之前,我整理过OC的5种基本界面传值方式,很多朋友都有很喜欢,今天再整理一下Swift中的7种常用界面传值供大家参考,不到之处,欢迎各位提点,更多的希望能对大家有所帮助.
注: 文中 ,第一个界面为ViewController,第二个界面为DetailViewController两个界面都会分别有一个Button和一个TextFiled 用于输入值和接受值.来查看传值效果
两个界面的简单搭建及跳转操作如下
//第一个界面ViewController
import UIKit
class ViewController: UIViewController {
fileprivate var tf:UITextField = UITextField()
  override func viewDidLoad() {
    super.viewDidLoad()

    setUpUI()

  }
 //点击进入第二个界面的按钮进行跳转操作
 @objc fileprivate func btnAction()  {
    let vc = DetailViewController()
    navigationController?.pushViewController(vc, animated: true)
  }

  }

//设置界面
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("进第二个界面", 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)
  }

}


 //第二个界面DetailViewController

import UIKit

class DetailViewController: UIViewController {
    var tf:UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = UIColor.white
    setUpUI()
}

//点击按钮返回上一界面
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("返回第一个界面", 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)
}
}
上面👆界面搭建,下面进行的几种传值操作都是以这两个界面为基础分别进行的
1.属性传值

属性传值的方式特别简单,其实现的基本思路,就是为创建对象并为其属性赋值,同样的,我们模拟从第一个界面传值到第二个界面,首先我们在第二个界面声明一个String类型的tfStr属性。

import UIKit
class DetailViewController: UIViewController {
var tf:UITextField!
 //String类型的tfStr属性
var tfStr:String!
  
 override func viewDidLoad() {
    super.viewDidLoad()
  }
}

然后在处理第一个界面跳转到第二个界面的方法中实例化第二个界面对象,并为其属性赋值,如下所示

func btnAction()  {
let vc = DetailViewController()
    vc.tfStr = tf.text
 navigationController?.popViewController(animated: true)

}

接下来,我们即可直接在第二个界面中的viewDidLoad()方法中如下操作实现传值。

 tf.text = tfStr

👆你以学会属性传值

2.构造器传值

构造器传值,类似于OC的自定义初始化传值,即在初始化某一个视图控制器的时候,将需要传递的数据传递过去,因此,我们同样可以模拟从第一个面传值到第二个界面。
首先,我们需要在第二个界面中,自定义构造器,具体实现方式如下

class DetailViewController: UIViewController {
var tfStr:String!
var tf:UITextField!
override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = UIColor.white
    setUpUI()
    
}

 // 析构器

deinit {
    print(#function)
}
// 自定义构造器
init(text:String) {
    print(#function)
    print(text)
    tfStr = text
    // 指定构造器必须调用它最近父类的指定构造器
    super.init(nibName: nil, bundle: nil)
}
// init(coder aDecoder: NSCoder) 方法是来自父类的指定构造器, 因为这个构造器是 required, 必须要实现。但是因为我们已经重载了 init(), 定义了一个指定构造器, 所以这个方法不会被继承, 需要要手动覆写。

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}



func btnAction()  {

 navigationController?.popViewController(animated: true)

  }

}

自定义构造器创建好之后,我们即可在第一个界面处理界面跳转的方法中通过自定义构造器创建对象并传值了,如下所示

//点击钮进行跳转操作方法

@objc fileprivate func btnAction()  {
    let vc = DetailViewController(text: tf.text!)
    navigationController?.pushViewController(vc, animated: true)
    
}

👆你以学会构造器传值

3.通知传值

我们来看看通知传值,通知可实现任意界面之间的数据传递,但必须满足一个条件,就是保证在发送通知的时候监听者已经存在(先要注册通知)。而通知的注册与OC写法有所不同Swift种主要通过NotificationCenter 而不是NSNotificationCenter通知中心实现(其实就是写法不同),其为一个系统单例,系统提供了default方法获取通知实例对象。

通知使用步骤:注册通知 -> 发送通知 -> 移除通知

通知实现的原理,我们可以这样去理解,学生监听下课铃声。我们把学生看做监听者(或者叫观察者),监听铃声,铃声一响就放学。当铃声响起时,我们看做发出一个通知(信号),学生在监听到铃声之后就会做出相应的操作,比如放学之后做什么……

接下来我们看看通知传值的具体实现方式。这里我们模拟从第二个界面传值到第一个界面

首先我们需要在第一个界面注册通知,因为,程序运行肯定是先到第一个界面中,所以,当在第二个界面发送通知的时候,通知监听者肯定是存在的。注册通知的方法常用的有以下两种:

//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参数设置接收到通知时触发的方法。
而第2种,我们无需关联触发方法,在方法尾部跟着一个闭包,当接收到通知的时候该闭包会自动调用,我们可直接在闭包内处理相应的逻辑即可。第2种方法还有一个参数queue,该参数主要设置通知触发方法执行的队列,其为OperationQueue类型的对象,这里我们一般在主队列执行,配置参数方法为OperationQueue.main。我们可以直观的看到,在两种方法中都有一个name参数,该参数我们可以理解为通知的代号,通过这个代号我们可以避免多个通知串联,这个参数我们可以赋值任意字符串。我们这里实例需要传值而不是接到通知去进行一个事件所以这里以第2种为例。

按照上面提到的通知使用步骤:
(1)注册通知
注册通知我们在第一个界面中

 NotificationCenter.default.addObserver(forName: NSNotification.Name?, object: Any?, queue: OperationQueue?) { (Notification) in
        //拿到Notification内容进行逻辑处理....
    }

(2)发送通知
通知注册好之后,下一步我们就可以在第二个界面发送通知了,我们在处理界面跳转(返回)的方法中处理这一逻辑。发送通知主要用到以下方法:

open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil)

这里需要注意,发送通知的aName参数,必须和注册通知时的name参数一致,否则在第一个界面将无法接收到通知。我们可通过aUserInfo参数将需要传递的数据传递到第一个界面中,该参数为一个[NSObject : AnyObject]?(字典)类型的数据。实现示例如下:

 func btnAction()  {

    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notifyName"), object: nil, userInfo: ["text": self.tf.text])
     navigationController?.popViewController(animated: true)

}

当用户点击返回按钮时,发送通知,第一个界面接收到对应通知之后将会回调闭包,我们可在闭包中获得数据,如下所示:

 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
//这里拿到通知传来的数据text,然后进行逻辑处理,我们这里是为tf.text 赋值
        self.tf.text = "\(text.userInfo?["text"])"
    }
    setUpUI()
 
}

(3)移除通知
我们已经基本实现通知传值了,我们还需要最后一步,移除通知,通知的移除我们可在界面释放的方法(析构方法)中去执行,如下所示:

deinit {
    NotificationCenter.default.removeObserver(self)
}

👆你以学会通知传值

4.单例传值

单例,我们可以简单理解为“一个类,一个实例”。因此,不管我们在什么地方创建单例,所得到的都是同一个实例,根据这一特点,我们可通过单例进行传值,但是注意的是,单例传值,在我们获取单例值的时候首先必须保证单例确实有值,因此,我们可模拟通过单例实现从第一个界面传值到第二个界面的场景。
要使用单例传值,我们必须得创建单例,首先我们需要建立一个Swift文件,建立步骤:command + n -> iOS ->Swift File,为其取名为Singleton。然后我们在该文件中构造单例类,具体构造方式如下:

import Foundation
class Singleton{
 // 单例属性,用于传值;
var text : String!
static let singleton = Singleton()
// 获取单例实例方法
func share() -> Singleton {
    return .singleton
}

// 私有化init初始化方法,防止通过此方法创建对象
private init(){
  }

}

这里需要注意的是,我们可将需要传递的数据设置成单例的属性,如上述代码中的text属性。在第一个界面为其赋值之后我们就可以在第二个界面访问了

//第一个界面赋值
 override func viewDidLoad() {
    super.viewDidLoad()

    setUpUI()
    let singleton = Singleton.singleton.share()
    singleton.text = "我叫MangoJ"
    
}
//第二个界面任意位置都可获取单例属性的值本例在viewDidLoad中
 override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = UIColor.white
    tfStr = Singleton.singleton.share().text
    print(tfStr)
    setUpUI()
}

👆你以学会单例传值

5.协议传值

协议传值,主要用于代理模式。假设我们要实现从第二个界面传值到第一个界面这一需求,首先,我们需要拟定一份协议,为了方便,我们可直接在第二个界面中拟定协议,如下所示:
基本步骤:
1.声明协议
2.设置协议中的方法
3.声明代理

import UIKit

//1.声明协议
@objc protocol DetailViewControllerDelegate {
//2.设置协议中的方法
  @objc optional func viewController(viewController:DetailViewController,popWithValue:String)->Void
}

@objc关键字标识该协议为一个可选协议;optional关键字标识该协议方法对于协议的遵守者而言不是必须实现的。

声明了协议之后,我们需要为第二个界面声明一个代理属性,如下所示:

class DetailViewController: UIViewController {
//3.声明协议属性
 // 代理属性delegate值为实现了DetailViewControllerDelegate协议的任意对象,weak关键字主要为了防止循环引用导致对象无法释放。
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)

  }

}

声明了代理属性之后,我们需要在处理界面跳转(返回)的方法中处理协议传值的逻辑了。首先我们需要判断代理人是否存在,可通过可选绑定来操作,如果代理存在,则让代理执行协议方法,并且将需要传递的信息通过参数传递给代理所在的界面,如下所示:

func btnAction()  {

    if let delegate = self.delegate {
        delegate.viewController!(viewController: self, popWithValue: "MangoJ")
    }
    
 navigationController?.popViewController(animated: true)
    
}

切换到第一个界面中,在处理界面跳转的方法中,我们将第二个界面的代理属性设为第一个界面,如下所示:

  //点击进入第二个界面的按钮进行跳转操作
 @objc fileprivate func btnAction()  {
  
    let vc = DetailViewController()
    //设置代理
    vc.delegate = self
    
    navigationController?.pushViewController(vc, animated: true)
    
}

然后,实现协议方法,在协议方法中,我们可以直接获取从第二个界面传递过来的值。

 // MARK:- DetailViewControllerDelegate -  
 func viewController(viewController: DetailViewController, popWithValue: String) {
    tf.text = popWithValue
}

👆你以学会代理传值

6.闭包传值

闭包主要用于回调,这里我们还是模拟从第二个界面传值到第一个界面,首先我们需要在第二个界面为闭包取个别名,声明一个闭包类型,如下所示:
使用闭包的步骤:
1.声明闭包类型
2.声明闭包属性
3.设置闭包传值调用方法
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.设置闭包传值调用方法
func callBack(closure:@escaping ClosureName)  {
    self.closure = closure
}

func btnAction()  {
  
      5、可选绑定:判断closure属性是否不为nil,如果不为nil,则通过closure将文本信息回调到调用closure方法所在的控制器中;
    guard (self.closure != nil) else {
        return
    }

    closure(tf.text!)
    
 navigationController?.popViewController(animated: true)

  }

}

现在第二个界面已经配置完毕,最后一步,我们在第一个界面推送到第二个界面的方法中,通过实例化的第二界面对象,调用闭包回调方法,然后打印数据即可,该方法在第二界面返回到第一界面的时候会直接被调用,代码如下

 //第一个界面的如下方法中
@objc fileprivate func btnAction()  {
  
    let vc = DetailViewController()
 
    vc.callBack { (value) in
        self.tf.text = value
    }
    navigationController?.pushViewController(vc, animated: true)
    
}

👆你以学会闭包传值

7.NSUserDefaults传值

UserDefaults为系统单例,通过UserDefaults传值和自定义单例传值原理基本一致。对于UserDefaults传值,首先在获取值的地方,必须保证单例实例key对应的数据确实有值才行,同样的,我们模拟从第一个界面传值到第二个界面

使用步骤:
1.获取UserDefaults单例实例
2.为UserDefaults实例设值
3.同步数据

在第一个界面的该方法中有如下操作

 @objc fileprivate func btnAction()  {
  
    // 获取defaults单例实例
 let defaults = UserDefaults.standard
    //设值
    defaults.setValue(tf.text, forKey: "text")
    // 同步数据
    defaults.synchronize()
    
    let vc = DetailViewController()
 
    navigationController?.pushViewController(vc, animated: true)
    
}

在第二个界面的该方法中有如下操作

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传值,不仅仅只局限于传递字符串类型的数据,同时可传递集合类型(字典、数组、集)或者基本数据类型的数据

👆Swift7种常见界面间传值整理完毕,供大家参考更希望大家多提宝贵意见.

如果看了对您有所帮助,希望可以点一下喜欢😍!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 1、通知传值 首先我们来看看通知传值,通知可实现任意界面之间的数据传递,但必须满足一个条件,就是保证在发送通知的时...
    Insit阅读 3,273评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,796评论 25 707
  • 废话不多说下面我们就简单介绍下页面间常用的几种传值方式,希望能给初学者带来帮助: 注: 文中 ,第一个界面为Fi...
    MangoJ阅读 5,481评论 14 94
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 独自带孩子旅行之南方之旅,第一周结束。只能说结束,不能说完美结束。因为实在是,太!累!人!了! 你知道吗?我还带着...
    茉莉大大阅读 191评论 1 1