Swift:界面之间传值

1、通知传值

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

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

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

接下来我们看看通知传值的具体实现方式。这里我们模拟从详情界面传值到主界面,为了统一,下文都用ViewController表示主界面,DetailViewController表示详情界面。首先我们需要在主界面注册通知,因为,程序运行肯定是先到主界面中,所以,当在详情界面发送通知的时候,通知监听者肯定是存在的。注册通知的方法常用的有以下两种:

// 1、publicfunc addObserver(observer: AnyObject, selector aSelector: Selector, name aName:String?, object anObject: AnyObject?)

// 2、publicfunc addObserverForName(name:String?, object obj: AnyObject?,queue: NSOperationQueue?, usingBlock block: (NSNotification)->Void)->NSObjectProtocol

第1种,我们需要通过Selector参数设置接收到通知时触发的方法。而第2种,我们无需关联触发方法,在方法尾部跟着一个闭包,当接收到通知的时候该闭包会自动调用,我们可直接在闭包内处理相应的逻辑即可。第2种方法还有一个参数queue,该参数主要设置通知触发方法执行的队列,其为NSOperationQueue类型的对象,这里我们一般在主队列执行,配置参数方法为NSOperationQueue.mainQueue()。我们可以直观的看到,在两种方法中都有一个name参数,该参数我们可以理解为通知的代号,通过这个代号我们可以避免多个通知串联,这个参数我们可以赋值任意字符串。此处以第2种为例。

//1、register notification

NSNotificationCenter.defaultCenter().addObserverForName("notification_name", object: nil, queue: NSOperationQueue.mainQueue()) { (info: NSNotification) -> Voidin// 处理接收到通知之后执行的逻辑...}

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

// 发送通知publicfuncpostNotificationName(aName: String,objectanObject: AnyObject?, userInfo aUserInfo: [NSObject : AnyObject]?)

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

func respondsToBtn(sender:UIButton) {

// 2、post notification and send value

NSNotificationCenter.defaultCenter().postNotificationName("notification_name", object:nil, userInfo: ["text":self.textField.text!])

self.dismissViewControllerAnimated(true, completion:nil)

}

当用户点击返回按钮时,发送通知,主界面接收到对应通知之后将会回调闭包,我们可在闭包中打印传递过来的数据,如下所示:

NSNotificationCenter.defaultCenter().addObserverForName("notification_name", object: nil, queue: NSOperationQueue.mainQueue()){(info)->Void in 

      print((info.userInfo!["text"])!)

}

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

deinit {

      // 3、remove notification

      NSNotificationCenter.defaultCenter().removeObserver(self)

}

2、协议传值

协议传值,主要用于代理模式。假设我们要实现从详情界面传值到主界面这一需求,首先,我们需要拟定一份协议,为了方便,我们可直接在详情界面中拟定协议,如下所示:

importUIKit

//1、声明协议

@objcprotocol DetailViewControllerDelegate {

optional func viewController(viewController: DetailViewController, dismissWithValue value: String)->Void

}

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

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

class DetailViewController:UIViewController{

// 2、声明协议属性

      weakvardelegate: DetailViewControllerDelegate?

       overridefunc viewDidLoad() {

       }

}

代理属性delegate值为实现了DetailViewControllerDelegate协议的任意对象,weak关键字主要为了防止循环引用导致对象无法释放。

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

// MARK:- Events -

func respondsToBtn(sender:UIButton) {

         // 3、判断代理是否存在,如果代理存在则让代理执行协议方法并且将数据传递给代理

        if let delegate =self.delegate{

               delegate.viewController!(self, dismissWithValue:"123")

        }

        self.dismissViewControllerAnimated(true, completion:nil)

}

现在万事具备,只缺“代理”了,切换到主界面中,在处理界面跳转的方法中,我们将详情界面的代理属性设为主界面,如下所示:

// MARK:- Events -

func respondsToBtn(sender:UIButton) {

let detail_vc = DetailViewController()

// 设置代理

detail_vc.delegate = self

self.presentViewController(detail_vc, animated:true, completion:nil)

}

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

// MARK:- DetailViewControllerDelegate -

func viewController(viewController: DetailViewController, dismissWithValuevalue: String) {

print(value)

}

3、闭包传值

闭包主要用于回调,这里我们还是模拟从详情界面传值到主界面,首先我们需要在详情界面为闭包取个别名,声明一个闭包类型,如下所示:

// 1、声明闭包类型

typealias Closure=(String?)->Void

其次在详情界面控制器中,声明闭包属性:

// 2、声明闭包属性

varclosure: Closure!

接下来,我们需要为详情界面声明一个闭包回调的方法,用于在主界面中调用,并且为闭包属性赋值,如下所示:

// MARK:- closure send values methods -

// 3、闭包传值调用方法

func callBack(closure: Closure!) {

// 4、赋值闭包属性

self.closure = closure

}

现在闭包属性已经有值了,我们还需要在处理界面返回的方法中实现回调传值的逻辑,同样的,闭包类型为可选类型,我们可通过可选绑定判断闭包属性是否有值,如果有值,则通过闭包将需要传递到主界面的数据传递出去,代码如下:

func respondsToBtn(sender:UIButton) {

//5、可选绑定:判断closure属性是否不为nil,如果不为nil,则通过closure将文本信息回调到调用closure方法所在的控制器中;

if let closure =self.closure {

        closure(self.textField.text)    

}

self.dismissViewControllerAnimated(true,completion:nil)}

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

// MARK:- Events -

func respondsToBtn(sender: UIButton) {

    let detail_vc = DetailViewController()

    detail_vc.callBack { 

          (value:String?)->Voidinprint(value!)

     }

self.presentViewController(detail_vc, animated:true, completion: nil)}

Tips:

1、为闭包取别名,可在参数列表中将需要传递的参数写成形参;

2、设置一个方法持有当前block;

3、在合适的地方进行调用类似于代理;

4、在创建该对象的地方进行闭包方面的调用;

4、单例传值

单例,我们可以简单理解为“一个类,一个实例”。因此,不管我们在什么地方创建单例,所得到的都是同一个实例,根据这一特点,我们可通过单例进行传值,但是注意的是,单例传值,在我们获取单例值的时候首先必须保证单例确实有值,因此,我们可模拟通过单例实现从主界面传值到详情界面的场景。

要使用单例传值,我们必须得创建单例,首先我们需要建立一个Swift文件,建立步骤:command + n -> iOS ->Swift File,为其取名为Singleton。然后我们在该文件中构造单例类,具体构造方式如下:

import Foundation

class Singleton{

// 单例属性,用于传值;

vartext: String!

privatestatic let _singleton = Singleton()

// 获取单例实例方法

class func sharedInstance()->Singleton{

      return_singleton    

}

// 私有化init初始化方法,防止通过此方法创建对象

privateinit() {

 }

}

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

接下来,我们在主界面跳转到详情界面的方法中,获取单例实例并为其赋值,具体实现方式如下:

// MARK:- Events -

func respondsToBtn(sender: UIButton) {

//获取单例实例

let singleton = Singleton.sharedInstance()

//为实例属性赋值

singleton.text ="123456"

//界面推送

let detail_vc = DetailViewController()

self.presentViewController(detail_vc, animated:true, completion:nil)

}

现在单例属性已经有值了,我们可在详情界面的任意位置,获取到单例实例,打印其属性值:

//获取单例实例

let singleton = Singleton.sharedInstance()

//打印实例属性

print(singleton.text)

5、构造器传值

构造器传值,类似于OC的自定义初始化传值,即在初始化某一个视图控制器的时候,将需要传递的数据传递过去,因此,我们同样可以模拟从主界面传值到详情界面。

首先,我们需要在详情界面中,自定义构造器,具体实现方式如下:

class DetailViewController: UIViewController {

//析构器

deinit {

print(__FUNCTION__)

}

//自定义构造器

init(text: String) {

print(__FUNCTION__)

//打印参数

print(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")

}

overridefunc viewDidLoad() {

super.viewDidLoad()

}

}

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

// MARK:- Events -func respondsToBtn(sender:UIButton) {

let detail_vc = DetailViewController(text:"123456")

self.presentViewController(detail_vc, animated:true, completion:nil)

}

6、属性传值

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

import UIKit

class DetailViewController:UIViewController{

    var text:String!

    override func viewDidLoad(){

        super.viewDidLoad()

    }

}

然后在处理主界面跳转到详情界面的方法中实例化详情界面对象,并为其属性赋值,如下所示:

func respondsToBtn(sender:UIButton) {

    let detail_vc = DetailViewController()

    detail_vc.text="123456"

    self.presentViewController(detail_vc, animated:true, completion:nil)

}

接下来,我们即可直接在详情界面中的viewDidLoad()方法中打印属性值了。

NSUserDefaults传值

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

不管是存值还是取值,首先我们都需要获取NSUserDefaults单例实例,系统为我们提供了standardUserDefaults()方法来获取实例对象。为方便起见,这里我直接贴上代码,以供各位参考。

主界面代码:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {

        super.viewDidLoad()

        self.view.backgroundColor= UIColor.whiteColor()

        let btn = UIButton(type: UIButtonType.System)

        btn.bounds= CGRectMake(0,0,100,30)

        btn.center= self.view.centerbtn.setTitle("前往", forState: UIControlState.Normal) 

       btn.titleLabel?.font= UIFont.boldSystemFontOfSize(20)

        btn.addTarget(self, action:"respondsToBtn:", forControlEvents: UIControlEvents.TouchUpInside)                                           self.view.addSubview(btn)

    }

    func respondsToBtn(sender: UIButton) {

        // 获取defaults单例实例

        let defaults = NSUserDefaults.standardUserDefaults()

        // 设值

        defaults.setValue("123456", forKey:"text")

        // 同步数据

        defaults.synchronize()

        let detail_vc = DetailViewController()

        self.presentViewController(detail_vc, animated: true, completion: nil)

    }

}

详情界面代码:

import UIKit

class DetailViewController: UIViewController {

override func viewDidLoad() {

super.viewDidLoad()

// 获取defaults单例实例

let defaults = NSUserDefaults.standardUserDefaults()

// 获取单例实例key对应的value值

let value= defaults.valueForKey("text")

// 打印value值print(value!)

}

}

顺便提一下,通过NSUserDefaults传值,我们不仅仅只局限于传递字符串类型的数据,我们同时可传递集合类型(字典、数组、集)或者基本数据类型的数据。编译器也为我们提供了各种方法来实现不同的需求,如下所示:

设值方法

// 1、对象类型

public func setValue(value: AnyObject?, forKey key: String)

// 2、基本数据类型

public func setInteger(value: Int, forKey defaultName: String)

public func setFloat(value: Float, forKey defaultName: String)

public func setDouble(value: Double, forKey defaultName: String)

public func setBool(value: Bool, forKey defaultName: String)

取值方法

// 1、对象类型

publicfunc valueForKey(key:String)->AnyObject?

// 2、基本数据类型

public func integerForKey(defaultName:String)->Int

public func floatForKey(defaultName:String)->Float

public func doubleForKey(defaultName:String)->Double

public func boolForKey(defaultName:String)->Bool

Tips:无论是系统单例还是自定义单例,只要单例对应的key或者属性确实有值,我们就可以在整个项目的任意文件中访问对应数据。

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

推荐阅读更多精彩内容