该文章仅作为个人对iOS开发的加深记忆。
开发语言 OC or Swift
OC
OC是一门动态语言。OC类的类型和变量的类型都是在运行时确定的。 OC利用Runtime机制,可以在运行期动态的添加变量、方法、类。
Swift
Swift则是一门静态语言。同时Swift也是一种强类型语言。变量一旦被定义了类型,将再也无法改变他的类型。
OC注重灵活。Swift则更加安全。
我个人而言,比较倾向于实用Swift.
因为Swift中有更多的开发技巧,而且可以面向协议编程,面向函数编程(swfit中注重函数式编程),也可以面向对象编程,而且Swift中的Extension特别好用。
当然在Swift也可以选择混合开发,可以同时使用到OC语言的灵活性(Runtime)。
UI
无论OC还是Swift, 使用的全部都是官方的UI,例如UIButton,UIView.当然也可以自定义View样式。
OC
1.新建一个控件的流程。TestOneView
@implementation TestOneView
//系统的初始化view方法
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
//初始化button
UIButton * testButton = [UIButton buttonWithType:(UIButtonTypeSystem)];
//设置frame 也就是控件的位置
testButton.frame = CGRectMake(0, 0, 50, 40);
//背景颜色
testButton.backgroundColor = [UIColor redColor];
//按钮内文字以及状态
[testButton setTitle:@"点我啊" forState:(UIControlStateNormal)];
//设置按钮点击事件,以及响应手势的类型.
[testButton addTarget:self action:@selector(clickButton) forControlEvents:(UIControlEventTouchUpInside)];
//添加button到self
[self addSubview:testButton];
return self;
}
//点击事件
-(void)clickButton{
NSLog(@"你还真点啊!");
}
@end
2.在ViewController中使用这个控件。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//初始化View
TestView * testView = [[TestView alloc] initWithFrame:(CGRectMake(100, 100, 100, 100))];
//设置背景颜色
[testView setBackgroundColor:[UIColor lightGrayColor]];
//添加到当前控制器的view上
[self.view addSubview:testView];
}
Swift
在Swift中 因为语言不同,所以初始化的写法也有所不同。当然只是写法有些不同“而已”,涉及到控制器与子view之间的关系和OC是一样的
1.新建一个控件的流程。TestOneView
class TestOneView: UIView {
override init(frame: CGRect) {
super.init(frame: frame);
//初始化对象
let testButton = UIButton(type: UIButton.ButtonType.system);
//设置frame
testButton.frame = CGRectMake(0, 0, 50, 40);
//设置按钮文字
testButton.setTitle("快点我", for: UIControl.State.normal);
//添加点击事件
testButton.addTarget(self, action: #selector(clickButton), for: UIControl.Event.touchUpInside);
//把testButton 添加到 self
self.addSubview(testButton);
}
//button点击事件
@objc func clickButton(){
print("你还真点啊");
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
2.在ViewController中使用这个控件。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//初始化
let testView = TestView(frame: CGRectMake(100, 100, 100, 100));
//设置背景颜色
testView.backgroundColor = UIColor.lightGray;
//添加到当前控制器的view上
self.view.addSubview(testView);
}
布局
在上面的UI中可以看到,是使用的Frame对控件进行定位。这种方式也可以,不过多个UI控件之间需要做相对位置移动时,处理起来就会相当的麻烦。
所以在iOS开发中,大家使用的基本都是AutoLayout布局。各种第三方布局库也是基于AutoLayout进行的二次开发。
OC 以Masonry为例。
上面 TestOneView中的布局 可以修改为这样。具体使用方法可以去github(网上文章也很多)查看文档,这里只是说可以使用这个库做布局。
[testButton mas_makeConstraints:^(MASConstraintMaker *make) {
//距离自己父视图左边0
make.left.equalTo(self).with.offset(0);
//距离自己父视图顶部0
make.top.equalTo(self).with.offset(0);
//宽度50
make.height.mas_equalTo(50);
//高度40
make.width.mas_equalTo(40);
}];
Swift 以SnapKit为例
上面 TestOneView中的布局 可以修改为这样。该布局库具体使用方法,可以去github(网上文章也很多)查看文档。
testButton.snp.remakeConstraints { (make) in
//也可以指定left距离某控件的left or right的距离 make.left.equalTo(iconImageView.snp.right).offset(8)
make.left.equalTo(0)
make.top.equalTo(0)
make.width.equalTo(50)
make.height.equalTo(40)
}
页面数据处理
iOS中没有web中的双向数据绑定功能。都是单向的。
非列表的情况
这里OC和Swift区别不大,所以就拿Swift来做例子。
创建 TestTwoView
class TestTwoView: UIView {
var name:String = ""
var age:Int = 0;
var heigh:Float = 0;
var weight:Float = 0;
lazy var nameLabel: UILabel = {
let l = UILabel(frame: CGRectMake(10, 10, 100, 20));
return l
}()
lazy var ageLabel: UILabel = {
let l = UILabel(frame: CGRectMake(10, 40, 100, 20));
return l
}()
var heighLabel: UILabel = {
let l = UILabel(frame: CGRectMake(10, 70, 100, 20));
return l
}()
var weightLabel: UILabel = {
let l = UILabel(frame: CGRectMake(10, 100, 100, 20));
return l
}()
override init(frame: CGRect) {
super.init(frame: frame);
self.addSubview(self.nameLabel);
self.addSubview(self.ageLabel);
self.addSubview(self.heighLabel);
self.addSubview(self.weightLabel);
}
func setData(name: String, age: Int, heigh: Float, weight: Float){
self.name = name
nameLabel.text = "姓名: \(name)"
self.age = age
ageLabel.text = "年龄: \(age)"
self.heigh = heigh
heighLabel.text = "身高: \(heigh)"
self.weight = weight
weightLabel.text = "体重: \(weight)"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
在 ViewController 中 使用它
override func viewDidLoad() {
super.viewDidLoad()
let testTwoView = TestTwoView(frame: CGRect(x: 50, y: 100, width: 200, height: 150));
testTwoView.setData(name: "张三", age: 18, heigh: 175.5, weight: 80)
self.view.addSubview(testTwoView);
}
这是你如果想更改这个用户体重怎么办? 最简单的方法就是找出展示体重的label,然后:
testTwoView.weightLabel.text = "体重: \(70)"
但是这样用起来不够优雅,而且把内部的控件/逻辑暴露了出来。
还有以下几种选择,
1.在 TestTwoView 内添加一个weightChange方法,专门用来修改体重(在OC中通常使用这种方式)。
2.可以在 TestTwoView 暴露出一个属性,然后通过Swift中的属性观察方法(在Swift中推荐使用这种方式,更加的方便),代码如下。
var weight:Float = 0{
didSet{
weightLabel.text = "体重: \(oldValue)";
}
}
View暴露一个weight属性给外界,外界在修改这个weight属性时,会触发didSet,然后你可以在didSet中通过oldValue获取到刚修改的值,然后做一些你想做的事情。这样内部的处理逻辑外界并不用关心。
weight属性使用方法:
testTwoView.weight = 70;
列表中刷新数据
例如: UITableView UICollectionView 他们有一个 reloadData方法来刷新页面。数据源发生变化之后调用即可。
页面导航
iOS中的导航,无论OC还是Swift都是一样的.因为这是UI框架提供的功能.
页面的跳转需要使用到导航控制器UINavigationController,将一个UIViewController设置成他的rootViewController即可。
然后就可以通过这个 rootViewController 跳转到别的 UIViewController
页面的跳转分为push和present两种。
需要注意的是,如果需要底部Tabbar来管理多个页面吧,那么就需要在Tabbar中创建多个导航控制器,具体使用方法,可以通过官方文档查看UITabBarController的使用方法。
页面之间数据/事件传递。
数据传递
- 从上往下
直接属性赋值或者通过方法调用传值即可。例如:
let view = TestThreeView(frame: CGRect(x: 50, y: 100, width: 200, height: 150));
//属性传值
view.count = 10;
//方法传值
view.changCount(10);
self.view.addSubview(view);
- 从下往上
使用 block、代理、KVO 都可以。这些不写了 网上可以了查到
子组件的引用方式
OC
需要使用
#import "XXX.h"
Swift
不需要主动引入、直接可以使用
也就是说,你创建的所有Swift对象、类,都是全局的。
如果你要创建一个全局属性。只需要在任意页面,创建一个不属于任何类的变量即可,
而且Swift中单例的创建也很简单:
创建一个全局的属性,然后使用let声明。那么他就是一个单例。
let SCREEN_HEIGHT = UIScreen.main.bounds.height