Swift传值+视图跳转
文章内容:
- Swift中的常用传值方式
- 不同情况下视图的几种跳转方法详解
- xib的使用
- 简单介绍一下单例设计模式
- 单例的创建和使用方法
- NSUserDefaults的用法
由于我最近正在进阶Swift的原因 控件都是手动创建。避免繁琐你可以在SB中直接创建并布局。
属性传值
简单实现一个demo
首先国际惯例,打开Xcode新建一个Project,选择SingleViewApplication 。然后在viewDidLoad中创建输入框和按钮
//创建输入框
textField = UITextField(frame: CGRectMake(0,200,200,30))
textField.center.x = self.view.center.x
textField.borderStyle = UITextBorderStyle.RoundedRect
//创建按钮和对应的触发动作 添加到主视图上
let button1 = UIButton(type: .System)
button1.frame = CGRectMake(20,300,60,30)
button1.setTitle("属性传值", forState: .Normal)
button1.addTarget(self, action: "buttonAction1", forControlEvents: .TouchUpInside)
let button2 = UIButton(type: .System)
button2.frame = CGRectMake(20,300,60,30)
button2.center.x = self.view.center.x
button2.setTitle("代理传值", forState: .Normal)
button2.addTarget(self, action: "buttonAction2", forControlEvents: .TouchUpInside)
let button3 = UIButton(type: .System)
button3.frame = CGRectMake(20,300,60,30)
button3.center.x = self.view.frame.width - button1.center.x
button3.setTitle("单例传值", forState: .Normal)
button3.addTarget(self, action: "buttonAction3", forControlEvents: .TouchUpInside)
self.view.addSubview(button1)
self.view.addSubview(button2)
self.view.addSubview(button3)
self.view.addSubview(textField)
- 注意UIButton的buttonType属性是只读计算属性 不能修改 所以最好用带type参数的初始化方法初始化
下面当然是添加按钮的触发方法
func buttonAction1(){
}
func buttonAction2(){
}
func buttonAction3(){
}
既然是不同界面之间的传值,那么除了系统默认的ViewController还需要再创建3个视图控制器。这里使用xib
- 选择菜单栏File -> New -> File
- 在弹出的对话框中选择IOS下Source中的Cocoa Touch Class文件
- 输入类名,并继承于UIViewController,选中使用xib复选项 。击next
重复步骤再创建2个xib(由于前面取名FirstVC 我这里分别取名为SecondVC和ThirdVC)
然后点按每个xib的File’s Owner 确保为对应的类(通过创建类文件同时创建xib 系统默认是绑定好了的 这里是为了保住万无一失)
考虑到刚学Swift的同学可能只接触过StoryBoard,没接触到xib。所以这里简单解释一下xib。
- xib和StoryBoard均可通过拖拽控件等方式实现可视化编程。
- 区别在于StoryBoard其实就是多个xib的组合。
- 相比StoryBoard 通过创建xib实现视图的分类管理能使整个Project看起来更有条理。(稍后会介绍xib的使用方法)
属性传值
我们通过在输入框中输入数据 然后点击按钮跳转到FirstVC 并通过一个标签显示输入的数据的方式实现属性传值。
首先打开FirstVC.Swift文件 添加一个属性
var value:String!
修改viewDidLoad方法,建立标签和关闭按钮
override func viewDidLoad() {
super.viewDidLoad()
let valueLable = UILabel(frame: CGRectMake(0,0,300,40))
valueLable.center = self.view.center
//显示传递的数据到lable
valueLable.text = value
let closeButton = UIButton(type: .System)
closeButton.frame = CGRectMake(0, 30, 60, 30)
closeButton.setTitle("关闭", forState: .Normal)
closeButton.addTarget(self, action: "closeVC", forControlEvents: .TouchUpInside)
self.view.addSubview(valueLable)
self.view.addSubview(closeButton)
}
再实现关闭按钮的触发方法
func closeVC(){
self.dismissViewControllerAnimated(true, completion: nil)
}
好了,终于要开始传值了。在ViewController.Swift中的buttonAction1方法中添加下面的代码
func buttonAction1(){
let firstVC = FirstVC(nibName:"FirstVC",bundle: nil)
firstVC.value = textField.text
presentViewController(firstVC, animated: true, completion: nil)
}
解释一下:
- 首先 通过xib的名字初始化一个FirstVC实例对象(这里注意:在FirstVC后直接打括号,并不会有带nibName 和 bundle 参数的初始化方法,需要手动打出)
- 然后 将输入框输入的值赋给 firstVC的value
- 通过presentViewController方法跳转到firstVC视图控制器
顺便提一下另一个视图跳转的方法:
performSegueWithIdentifier("Segue跳转名字", sender: self)
- 其中Segue跳转名字就是你在StoryBoard中 在不同控制器中进行连线跳转时 点击控制器间蓝色的线条 右边属性面板上StoryBoard Segue下的Identifier 需要手动设置
- sender是个AnyObject类型 看实际需要传入参数
如果你是用StoryBoard连线跳转实现传值请重写这个父类方法
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segue跳转名字")
let firstVC = segue.destinationViewController as FirstVC
firstVC.value = textField.text
}
- 如其名字prepareForSegue 这个方法是为当前视图进行跳转时做最后的准备,所以这是最后的传值和处理数据的机会
- 这个方法来自UIViewController,由于所有视图控制器默认均继承自UIViewController。所以任何一个控制器都有此方法 只需要override重写一下
- 当然一般我们的跳转动作不只一个 这时候就需要判断segue.identifier 实现不同的跳转传
当然你也可以使用StoryBoard中视图的Identifier进行跳转。只需调用StoryBoard的instantiateViewControllerWithIdentifier方法获取实例对象 再结合presentViewController方法跳转 如下:
var firstVC = self.storyboard?.instantiateViewControllerWithIdentifier("FirstViewController") as FirstViewController
presentViewController(firstVC, animated: true, completion: nil)
当你有多个StoryBoard时 可以类比xib的实例对象获取方式
var storyboard = UIStoryboard(name: "New", bundle: nil)
var newVC = storyboard.instantiateViewControllerWithIdentifier("NewViewController") as NewViewController
presentViewController(newVC, animated: true, completion: nil)
代理反向传值
我们通过点击之前在ViewController上创建的代理传值按钮跳转到SecondVC,然后在SecondVC的输入框中输入数据 点击关闭按钮 在ViewController的输入框中显示数据的方式。
首先打开SecondVC.Swift文件添加全局textField
var textField:UITextField!
在viewDidLoad中添加下面代码 并在下面实现按钮关闭视图的方法
override func viewDidLoad() {
textField = UITextField(frame: CGRectMake(0,200,200,30))
textField.center.x = 187.5
textField.borderStyle = .RoundedRect
self.view.addSubview(textField)
let closeButton = UIButton(type: .System)
closeButton.frame = CGRectMake(0, 30, 60, 30)
closeButton.setTitle("关闭", forState: .Normal)
closeButton.addTarget(self, action: "closeVC", forControlEvents: .TouchUpInside)
self.view.addSubview(closeButton)
}
func closeVC(){
self.dismissViewControllerAnimated(true, completion: nil)
}
通过代理反向传值。通俗的说就是先定义一个protocol(协议)同时在协议里声明一个方法,然后在需要传值的A类中定义一个属性delegate(代理),类型为定义的协议类型。然后在A类的某个地方调用协议的方法。因为协议里的方法只进行了声明,并未具体实现。它将具体的实现交给遵循这个协议的B类。这样通过协议-代理的模式A类让B类 做了一些 A类想让B类做的事情。当然我们这里是进行传值(把SecondVC输入框中输入的数据传给ViewController并显示在textField上)这类似于其它编程语言里的接口。如下:
在SecondVC类的上面添加代码
protocol SecondVCDelegate{
func passOnInformation(VC:SecondVC,value:String)
}
- 系统命名协议的规则(类名+Delegate)
- VC表示调用此方法的对象
- value表示传出的数据
在SecondVC类中添加代理属性
var delegate:SecondVCDelegate?
修改closeVC方法
func closeVC(){
//判断代理是否为空
if (self.delegate != nil) {
//传值
self.delegate?.passOnInformation(self, value: textField.text!)
}
self.dismissViewControllerAnimated(true, completion: nil)
}
你一定要意识到 现在SecondVC的代理属性本来就为空,因为我们还没有设置它的代理 😂 在哪里设置呢?既然我们是先从默认的ViewController跳转到SecondVC 再从SecondVC反向传值到ViewController。所以跳转到SecondVC的时候是个绝佳的机会。
下面打开ViewController.Swift
首先继承SecondVCDelegate协议
class ViewController: UIViewController,SecondVCDelegate {
再修改buttonAction2方法
func buttonAction2(){
let secondVC = SecondVC(nibName:"SecondVC",bundle: nil)
//设置secondVC代理为自己
secondVC.delegate = self
presentViewController(secondVC, animated: true, completion: nil)
}
这时候你会发现系统会报错,为什么呢?还少了什么?
那就是我们还没有实现协议里的方法 (再次强调继承协议必须实现协议里的方法)
最后在buttonAction2方法下面实现
func passOnInformation(VC: SecondVC, value: String) {
//接收数据
self.textField.text = value
}
完美🙄
单例传值
首先解释一下IOS里的单例模式:
单例模式是一种常用的软件设计模式。它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。(来自百度--)
优点就不说了,可以自行百度
iOS中单例的创建方法
class AppInstance {
static let sharedInstance = AppInstance()
private init() {} // 私有化init方法
}
- 一定注意要用Pravate把构造器私有化,因为Swift中默认所有对象的构造器都是public 防止其它其它对象使用它的默认初始化方法创建对象
例如下面这样构造将不会通过
var a1 = AppInstance() //不能通过
单例的使用方式
AppInstance.sharedInstance
不扯了,现在来通过单例传值。你可以自行创建一个单例🤔🤔🤔
当然还有更偷懒的方式,因为UIApplication就是一个单例,所以我们可以直接用它。
首先打开AppDelegate.Swift 在window属性下面添加一个value
var value:String?
再打开ThirdVC.Swift 在加载时给界面添加lable和关闭按钮 同时实现按钮的方法 和单例传值
override func viewDidLoad() {
super.viewDidLoad()
let closeButton = UIButton(type: .System)
closeButton.frame = CGRectMake(0, 30, 60, 30)
closeButton.setTitle("关闭", forState: .Normal)
closeButton.addTarget(self, action: "closeVC", forControlEvents: .TouchUpInside)
self.view.addSubview(closeButton)
let lable = UILabel(frame: CGRectMake(0,200,100,30))
lable.center.x = 187.5
self.view.addSubview(label)
//单例传值-接收数据
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
lable.text = appDelegate.value!
// Do any additional setup after loading the view.
}
最后在ViewController的buttonAction3方法里添加代码
func buttonAction3(){
//单例传值-传出数据
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.value = textField.text!
//跳转视图
let thirdVC = ThirdVC(nibName:"ThirdVC",bundle: nil)
presentViewController(thirdVC, animated: true, completion: nil)
}
单例还有很多 比如 NSUserDefaults 。它是一个轻量的经常用来保存数据的类,而且数据不会伴随App的退出而消失,当然也可以用来传值。它通过健-值对的方式存储和获取
let userInfo = NSUserDefaults()
userInfo.setValue(textField.text, forKey: "value")
let userInfo = NSUserDefaults()
lable.text = userInfo.valueForKey("value") as! String
总结:
传值原理基本是都是通过先获取具体实例对象,然后通过某个属性或者媒介进行传值。
End
终于结束了😭 熬夜写的啊(头次写博文 有点慢😰)