iOS开发设计模式之代理

    今天主要讲的是iOS中经典的设计模式-代理模式,代理在我们日常开发中经常用到,面试也是必提的一道题,今天我将用一个delegateDemo通过OC与Swift两种语言来讲述代理模式的应用,我的邮箱是KenenCS@163.com,欢迎多多交流!

一级界面二级界面


delegateDemo效果:
            如图大家可以看到有一个一级界面和二级界面;
            点击一级界面中的按钮“请选择”进入二级界面界面;
            点击二级界面中的cell“点我”返回到一级界面,并把cell上的值带过去赋值给地点;

一、使用Swift语言

appdelegate中布局window和nav

1
2
3
4
5
6
7
8
9
10
11
12
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//布局window和nav
self.window = UIWindow.init(frame: UIScreen.main.bounds);
let vc = ViewController();
let nav = UINavigationController.init(rootViewController: vc);
self.window?.rootViewController = nav;
self.window?.makeKeyAndVisible();
return true
}

一级界面ViewController中布局UI和点击跳转事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import UIKit
class ViewController: UIViewController {
//声明属性
var btn = UIButton();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor.white;
self.title = "第一界面";
//设置label
let label = UILabel.init(frame: CGRect.init(x: 80, y: 100, width: 90, height: 30));
label.text = "选择地点:";
label.font = UIFont.systemFont(ofSize: 20);
self.view.addSubview(label);
//设置button
self.btn = UIButton.init(frame: CGRect.init(x: 170, y: 106, width: 100, height: 20));
self.btn.setTitle("请选择", for: .normal);
self.btn.setTitleColor(UIColor.red, for: .normal);
self.btn.backgroundColor = UIColor.green;
self.btn.titleLabel?.font = UIFont.systemFont(ofSize: 15);
self.btn.addTarget(self, action: #selector(ViewController.btnClick), for: .touchUpInside);
self.view.addSubview(self.btn);
}
//按钮点击事件
func btnClick() {
let testVC = TestViewController();
//跳入二级界面
self.navigationController?.pushViewController(testVC, animated: false);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

二级界面TestViewController中布局UI和点击跳转事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import Foundation
import UIKit
class TestViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
override func viewDidLoad() {
self.view.backgroundColor = UIColor.white;
self.title = "二级界面";
//当你写完TableView代码并遵循好代理的时候,会发现还是会报错,那是因为你没有实现这些代理,实现完就好了
let tableView = UITableView.init(frame: UIScreen.main.bounds, style: .plain);
tableView.delegate = self;
tableView.dataSource = self;
self.view.addSubview(tableView);
}
/***********UITableViewDelegate***********/
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10;
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell");
if cell==nil {
cell = UITableViewCell.init(style: .default, reuseIdentifier: "cell");
}
cell?.textLabel?.text = "点我";
return cell!;
}
//cell的点击事件
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//返回上一级界面
self.navigationController?.popViewController(animated: true);
//取消选中
tableView.deselectRow(at: indexPath, animated: true);
}
}

好了,我们把两个界面的布局都弄好了,功能跳转也弄好了,下面就是写代理了,并把值传过去了;

在二级界面TestViewController.swift文件中进行操作:

创建代理

1
2
3
4
//创建代理
protocol TestDelegate {
func changeTheLocation(str:NSString);
}

在TestViewController将你创建的代理声明成属性

1
2
//声明属性
var delegate:TestDelegate!;

在cell点击事件中我们使用代理,把值传过去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//cell的点击事件
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//这个代表 我们需要代理出去的数据源,当然,你可以通过indexPath.row代理出去不同row的值,我这里只是例子只做了一个
let str = "这是新地点";
//当执行这个代理并且是执行这个代理方法的时候
if self.delegate != nil {
//通过这个代理方法把需要的值代理过去
self.delegate.changeTheLocation(str: str as NSString);
}
//返回上一级界面
self.navigationController?.popViewController(animated: true);
//取消选中
tableView.deselectRow(at: indexPath, animated: true);
}


在一级界面ViewController.swift文件中进行操作:

遵循创建的代理

1
2
3
class ViewController: UIViewController,TestDelegate {
}

在点击跳转事件中设置代理

1
2
3
4
5
6
func btnClick() {
let testVC = TestViewController();
//设置的代理
testVC.delegate = self;
self.navigationController?.pushViewController(testVC, animated: false);
}

使用代理

1
2
3
4
5
//在这里实现代理方法,接收代理过来的值
func changeTheLocation(str: NSString) {
//通过代理过来的值来改变按钮的text,即改变地点
btn.setTitle(str as String, for: .normal);
}

一、使用Objective-C语言


    OC语法大家应该都是很熟悉的,这个demo的逻辑是一样的,我demo中注释都很详细的,所以我就不在一一讲解了,直接把代码粘贴上来好了,其实那么多代码关于代理的也就是那么几句,所以大家只需要细看那么几句就行了,我将关于代理的代码都有详细的注释,其余都是UI布局所以没有注释。

appdelegate.m

1
2
3
4
5
6
7
8
9
10
11
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController *vc = [[ViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[self.window setRootViewController:nav];
[self.window makeKeyAndVisible];
return YES;
}

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#import "ViewController.h"
#import "TestViewController.h"
//遵循创建的这个代理TestDelegate
@interface ViewController () <TestDelegate> {
UIButton *btn;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"第一界面";
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(80, 100, 90, 30)];
label.text = @"选择地点:";
label.font = [UIFont systemFontOfSize:20];
[self.view addSubview:label];
btn = [[UIButton alloc] initWithFrame:CGRectMake(170, 106, 100, 20)];
[btn setTitle:@"请选择" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
btn.backgroundColor = [UIColor greenColor];
btn.titleLabel.font = [UIFont systemFontOfSize:15];
[btn addTarget:self action:@selector(btnClick) forControlEvents:(UIControlEventTouchUpInside)];
[self.view addSubview:btn];
}
- (void)btnClick {
TestViewController *testVC = [[TestViewController alloc] init];
//设置的代理
testVC.delegate = self;
[self.navigationController pushViewController:testVC animated:NO];
}
//在这里实现代理方法,接收代理过来的值
- (void)changeTheLocation:(NSString *)str {
//通过代理过来的值来改变按钮的text,即改变地点
[btn setTitle:str forState:(UIControlStateNormal)];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

TestViewController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <UIKit/UIKit.h>
//声明代理
@protocol TestDelegate <NSObject>
//改变地点的方法
- (void)changeTheLocation:(NSString *)str;
@end
@interface TestViewController : UIViewController
//让创建的代理变为一个属性,记着用弱引用
@property (nonatomic,weak)id<TestDelegate> delegate;
@end

TestViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#import "TestViewController.h"
@interface TestViewController ()<UITableViewDelegate,UITableViewDataSource>
@end
@implementation TestViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"二级界面";
UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
}
#pragma mark ----tableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 40;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:@"cell"];
}
cell.textLabel.text = @"点我";
return cell;
}
//cell的点击事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//这个代表 我们需要代理出去的数据源,当然,你可以通过indexPath.row代理出去不同row的值,我这里只是例子只做了一个
NSString *str = @"这是新地点";
//当执行这个代理并且是执行这个代理方法的时候
if (self.delegate && [self.delegate respondsToSelector:@selector(changeTheLocation:)]) {
//通过这个代理方法把需要的值代理过去
[self.delegate changeTheLocation:str];
}
//返回上一级界面
[self.navigationController popViewControllerAnimated:YES];
//取消选中
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}



以上就是此次讲解,欢迎大家下载交流!Demo地址如下:
Swift版本:delegateDemo_Swift
OC版本:delegateDemo_OC

Mac电脑如何对Windows电脑进行远程桌面访问

    今天上午改公司官网,因为改的代码较少,所以我直接访问我们公司的windows服务器(就是windows电脑),直接进行线上操作,遇到了一些坑,同时也把操作步骤讲解给大家,方便大家以后使用,我的邮箱是KenenCS@163.com,欢迎多多交流!

准备工作:
1.你需要下载一个Remote Desktop Connection应用,简称是RDC,这个应用是微软出品的,适用于Mac电脑连接Windows电脑,进行远程桌面操作的工具;下面是该应用的图标:

远程桌面连接应用图标

2.准备好你要访问的电脑的IP地址,用户名,密码;

操作步骤:
1.打开你下载的RDC后,会有一个跟下面一样的弹框,输入你准备好的IP地址;
输入IP地址

2.IP地址输入完之后点击连接按钮,会出现一个对话框,把你准备好的用户名和密码输入进去,然后一直点击连接按钮,直到出现下面如图所示就证明连接成功;
输入密码即可操作

3.再次输入密码就进入到一个windows桌面,远程桌面操作连接成功!

可能出现的坑:用户名和密码输入好之后点击连接出现如下图所示:
错误弹框
不要怕,已经有前辈解决了,看这个 解决链接 解决链接 解决链接 解决链接


附带远程连接时Mac电脑拷贝文件给windows电脑方法:
    远程连接时肯定少不了要拷贝文件过去,因为Mac和windows本身的操作系统不一样,所以直接拖拽文件是不行的(windows和window进行远程连接时直接拖拽文件是可以的,因为两者操作系统一样)。

1.打开你Mac电脑上的文件共享。(系统偏好设置->共享->文件共享 打勾);

2.点击你的RDC应用,看你Mac电脑上方有个菜单栏,点击 RDC,点击列表中的 首选项,点击 驱动器,点击第一个选项框选择 其他文件夹,选择你要拷贝的 文件夹 (一定记得是文件夹,文件不支持,你可以把要拷贝的东西放到文件夹),最后显示的是这样一个对话框;

驱动器

3.看到上图中我用红色框圈着的一个东西没——更改将在下一次连接时应用。 这时你要断开远程连接,断开就是你直接把这个窗口关闭就好(不要问为什么断开,这是机制问题)。然后按照之前的连接步骤成功进行远程连接之后,找到你连接的windows电脑的计算机,打开之后,你会发现你硬盘下面多了一个其他,那个文件夹就是你刚刚通过你Mac电脑共享的文件夹。然后。。。就没有然后了。。。接下来你就知道怎么做了。。。

共享文件夹



以上就是此次讲解,我的邮箱是KenenCS@163.com,欢迎多多交流!

OS开发常用设计模式之单例设计模式

    今天主要讲的是iOS中经典的设计模式-单例设计模式。这一模式的意图就是使得类中的一个对象成为系统中的唯一实例。它提供了对类的对象所提供的资源的全局访问点。因此需要用一种只允许生成对象类的唯一实例的机制。今天我将用一个demo通过OC与Swift两种语言来讲述单例模式的应用,我的邮箱是KenenCS@163.com,欢迎多多交流!

单例模式三准则:
    1. 单例必须是唯一的,在程序生命周期中只能存在一个这样的实例,单例的存在使我们可以全局访问状态。
    2. 为保证单例的唯一性,单例类的初始化方法必须是私有的,这样就可以避免其他对象通过单例类创建额外的实例。
    3. 保证单例的线程安全性,才可以保证其唯一性,通过调用dispatch_once,即可保证实例化代码只运行一次。

一、使用Swift语言创建单例

    Swift中创建单例的方法我知道的从Swift1.0~Swift4.0一共有四种,我今天说的这一种是标准用法,因为是Swift官方推荐的,比较简单,代码简洁。

创建单例类,起名叫做Singleton,继承于NSObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//单例类
class Singleton:NSObject {
//创建一个私有的静态变量
private static let instance = Singleton();
//确保唯一性,通过此类方法创建对象
class var shardSingleton: Singleton {
return instance;
}
//使用单例测试一个颜色
func testSingletonColor() -> UIColor {
let color = UIColor.init(colorLiteralRed: 100/255.0, green: 20/255.0, blue: 30/255.0, alpha: 1);
return color;
}
}

在ViewController中进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//控制器
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let sing1 = Singleton.init();
let sing2 = Singleton.shardSingleton;
//如果这两种创建方式生成的内存地址都一样,那么我们就大功告成
print("---Swift的sing1:\(sing1)\n---Swift的sing2:\(sing2)\n");
//使用单例赋值颜色
self.view.backgroundColor = Singleton.shardSingleton.testSingletonColor();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

调试栏输出的图如下:
Swift

一、使用OC语言创建单例


创建单例类,起名叫做Singleton,继承于NSObject,里面单例创建的方法是我们大家常用的GCD创建手法。

Singleton.h

1
2
3
4
5
6
7
8
9
10
11
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Singleton : NSObject
+(instancetype)shardSingleton;
//单例测试一个颜色
- (UIColor *)testSingletonColor;
@end

Singleton.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#import "Singleton.h"
@implementation Singleton
//确保唯一性,通过此类方法创建对象
+(instancetype)shardSingleton {
//创建一个静态变量
static Singleton *instance = nil;
//通过GCD实现对象只创建一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
//确保唯一性,防止通过【[Singleton alloc] init]创建对象,因此需要重写这个方法,因为调用alloc时会自动调用allocWithZone这个方法
+(instancetype)allocWithZone:(struct _NSZone *)zone {
return [Singleton shardSingleton];
}
//确保唯一性,防止通过copy和mutableCopy创建对象,安全起见,重写以下两个方法
- (id)copy {
return self;
}
- (id)mutableCopy {
return self;
}
//单例测试一个颜色
- (UIColor *)testSingletonColor {
UIColor *color = [UIColor colorWithRed:100/255.0 green:20/255.0 blue:30/255.0 alpha:1];
return color;
}
@end


在ViewController中进行测试,因为.h文件中没有什么变动,我直接粘贴.m文件的代码了,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Singleton *sing1 = [[Singleton alloc] init];
Singleton *sing2 = [Singleton new];
Singleton *sing3 = [Singleton shardSingleton];
Singleton *sing4 = [sing1 copy];
Singleton *sing5 = [sing1 mutableCopy];
//如果这几种创建方式生成的内存地址都一样,那么我们就大功告成
NSLog(@"\n---sing1:%p\n---sing2:%p\n---sing3:%p\n---sing4:%p\n---sing5:%p\n",sing1,sing2,sing3,sing4,sing5);
//使用单例赋值颜色
self.view.backgroundColor = [[Singleton shardSingleton] testSingletonColor];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

调试栏输出的图如下:
Objetive-C



以上就是此次讲解,欢迎大家下载交流!Demo地址如下:
Swift版本:SingletonDemo_Swift
OC版本:SingletonDemo

iOS开发常用设计模式之KVO模式

    最近有点忙,设计产品功能,改官网,这一闲下来就突然迷茫了,不知道该干点什么,到今天已经一个月没有更新博客了,真是不应该,坚持更新博客是一种记录生活的方式。
    今天主要讲的是iOS中经典的设计模式-KVO模式。KVO提供了一种机制,指定一个被观察对象,当这个对象的某个属性发生变化的时候,这个对象会获得属性变化的通知,以作出相应的处理。KVO不管是在面试还是工作中我们都会被用到这个东西,今天我将用一个demo通过OC与Swift两种语言来讲述简单的应用KVO,我的邮箱是KenenCS@163.com,欢迎多多交流!

KVODemo


demo效果:
    点击增加按钮时候num值加1,
    点击重置按钮时候num值归0,
    点击减少按钮时候num值减1;

KVO分三步走:
    1.注册观察者;
    2.在观察的属性变化的方法里面做处理;
    3.移除KVO;

一、使用OC语言创建KVO观察者模式


准备工作:
    我们建一个新工程,名字叫做TestDemo。创建完之后通过Cocoa Touch Class添加一个继承于NSObject的类,起名叫做myKVO,作为我们要用的观察者对象;

准备工作


操作我们新建的myKVO类

myKVO.h

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>
@interface myKVO : NSObject
//创建一个属性我们一会来监听它的变化
@property (nonatomic,assign)int num;
@end

myKVO.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import "myKVO.h"
#import <UIKit/UIKit.h>
@implementation myKVO
//合成器与声明num的property相对应,setter和getter方法
@synthesize num;
//重写了num的setter方法,因为我们有个要求是设置num数字范围在0-100之内
- (void)setNum:(int)newNum {
if (newNum>100||newNum<0) {
NSLog(@"超界了");
}else {//正常赋值
num = newNum;
}
}
//num的getter方法
- (int)num {
return num;
}
@end


操作我们ViewController

viewController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我这里采用XIB拖拽的形式
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
//展示的文字
@property (weak, nonatomic) IBOutlet UILabel *label;
//增加按钮
@property (weak, nonatomic) IBOutlet UIButton *changeNum;
//减少按钮
@property (weak, nonatomic) IBOutlet UIButton *jianShao;
//重置按钮
@property (weak, nonatomic) IBOutlet UIButton *chongZhi;
@end

viewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#import "ViewController.h"
#import "myKVO.h"
@interface ViewController ()
//我们需要观察的对象
@property (nonatomic,strong)myKVO *mykvo;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//初始化观察者对象
self.mykvo = [[myKVO alloc] init];
/*
注册观察者的代码
-----------参数简介----------
*observer 观察者;
*KeyPath 被观察的属性的名称;
*options 被观察属性的一些配置,这里使用的,NSKeyValueObservingOptionOld代表观察旧值,NSKeyValueObservingOptionNew代表观察新值;
*context 上下文,可以给kvo的回调方法传值;
*/
[self.mykvo addObserver:self forKeyPath:@"num" options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:nil];
}
/*
只要你观察的keypath发生变化就会调用这个方法
----------------参数简介--------------
*keypath 被观察的属性的名称;
*object 被观察的对象;
*change 前后变化的值都是储存在这个字典中的;
*context 注册观察者时,context传过来的值;
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"num"] && object==self.mykvo) {
// 这里使用KVO就是为了改变label上的数字
self.label.text = [NSString stringWithFormat:@"当前的num值是:%@",[change valueForKey:@"new"]];
// [change valueForKey:@"new"],代表新值,对应注册观察者时的NSKeyValueObservingOptionNew
// [change valueForKey:@"old"],代表旧值,对应注册观察者时的NSKeyValueObservingOptionOld
NSLog(@"旧值:%@ ----- 新值:%@ -----",[change valueForKey:@"old"],[change valueForKey:@"new"]);
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//增加按钮点击事件
- (IBAction)changeNum:(UIButton *)sender {
self.mykvo.num = self.mykvo.num+1;
}
//减少按钮点击事件
- (IBAction)jianShao:(UIButton *)sender {
self.mykvo.num = self.mykvo.num-1;
}
//重置按钮点击事件
- (IBAction)chongZhi:(UIButton *)sender {
self.mykvo.num = 0;
}
//移除kvo
-(void)dealloc {
[self removeObserver:self forKeyPath:@"num"];
}
@end


一、使用Swift语言创建KVO观察者模式


    使用Swift语言是真的简洁,我们只需要创建一个Swift工程就行了,别忘了在选择语言的时候是Swift而不是Object-C哦!我们只需要在你新建的工程中的viewController.swift文件操作即可;

操作我们观察者对象myKVO

    因为我们操作的myKVO对象中的属性num的时候,需要用到num的setter和getter方法,但是Swift中的setter和getter与OC中的可是不一样的,质一样区别,所以我感觉有必要先给大家普及一下Swift中setter和getter方法:

Swift中的属性分为两种属性,一种就是计算型属性 一种就是存储型属性.

1.计算型属性是什么?
    计算型属性是通过计算而得出来的属性,这种属性相区别于存储属性这种属性是不会存储的。如果是计算型属性,那么提供setter方法那就一定需要提供getter方法,可以直接只有一个getter方法,其实仔细想一想这样的设计是有缘由的,计算型属性我们是为了得出什么?我们是为了获取计算出来的值,那么你提供了setter方法,不提提供getter方法(他又不会存储)那么你是不能得到想要的值的,setter方法他会将新值保存在一个叫newValue中,我们可以直接用,当然getter中也有一个newValue。
2.存储型属性是什么?
    存储型属性就是一个需要存储的属性,如果我们需要自定义setter和getter方法,我们得注意一下,setter方法存在两种,willSet和didSet,这两种方法我们不一定都需要实现,根据需求实现各自的方法,willSet是将要赋值的时候调用的,而didSet方法是已经赋完了值之后调用的。可以提供他的getter方法,和计算型属性不一样的是,他可以有setter方法没有getter,想想设计也是相当的合理,既然他是存储型的属性,已经存起来了,那么我们可以取得到。在willSet方法里没有必要赋值,除非你要改变新赋的值,getter方法和setter 方法不能同时出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import UIKit
//被观察的对象
class myKVO: NSObject {
//我们要用它代替num
var _num:Int = 0;
//使用 @dynamic 修饰,表示该属性的存取都由 runtime 在运行时来决定,由于 Swift 基于效率的考量默认禁止了动态派发机制,因此要加上该修饰符来开启动态派发,而OC中是默认开启动态派发机制;
dynamic var num:Int{
//我们此次使用的是计算型属性
//重写num的setter方法,设置界限在0-100之内
set {
if (_num>100||_num<0) {
print("超界了")
}
//newValue是系统默认的
_num = newValue;
}
//重写num的getter方法
get {
return _num;
}
}
}


操作我们viewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//控制器
class ViewController: UIViewController {
//展示的文字
@IBOutlet weak var label: UILabel!
//增加按钮
@IBOutlet weak var changeNum: UIButton!
//重置按钮
@IBOutlet weak var chongZhi: UIButton!
//减少按钮
@IBOutlet weak var jianShao: UIButton!
//创建观察者对象
var myKvo:myKVO = myKVO();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
/*
注册观察者的代码
-----------参数简介----------
*observer 观察者;
*KeyPath 被观察的属性的名称;
*options 被观察属性的一些配置,这里使用的,NSKeyValueObservingOption.Old代表观察旧值,NSKeyValueObservingOption.New代表观察新值,NSKeyValueObservingOption.indexes代表最初的值;
*context 上下文,可以给kvo的回调方法传值;
*/
//NSKeyValueObservingOption.New可以简写成.new
self.myKvo.addObserver(self, forKeyPath: "num", options: .new, context: nil);
}
/*
只要你观察的keypath发生变化就会调用这个方法
----------------参数简介--------------
*keypath 被观察的属性的名称;
*object 被观察的对象;
*change 前后变化的值都是储存在这个字典中的;
*context 注册观察者时,context传过来的值;
*/
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath=="num" {
/*
这里使用KVO就是为了改变label上的数字
*.newKey,代表新值,对应注册观察者时的NSKeyValueObservingOption.New
*.oldKey,代表旧值,对应注册观察者时的NSKeyValueObservingOption.Old
*.indexesKey,代表最初的值,对应注册观察者时的NSKeyValueObservingOption.indexes
*/
if let newValue = change?[.newKey] {
self.label.text = "当前的num值是:\(newValue)";
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//增加按钮点击事件
@IBAction func changeNum(_ sender: UIButton) {
self.myKvo.num = self.myKvo.num+1;
}
//重置按钮点击事件
@IBAction func chongZhi(_ sender: UIButton) {
self.myKvo.num = 0;
}
//减少按钮点击事件
@IBAction func jianShao(_ sender: UIButton) {
self.myKvo.num = self.myKvo.num-1;
}
//移除KVO
deinit {
self.removeObserver(self, forKeyPath: "num");
}
}



以上就是此次讲解,欢迎大家下载交流!Demo地址如下:
Swift版本:KVOSwiftDemo
OC版本:KVODemo

针对苹果近期的热点事件分析

    这段时间看到各种媒体对苹果每日一黑,甚是有趣,今天就”微信打赏苹果抽取30%提成”和”苹果禁止开发者进行热更新”两个热点事件,想来谈谈自己的见解!我的邮箱是KenenCS@163.com,欢迎多多交流!

一.微信打赏苹果抽取30%提成

    这个事件闹得很大,最后竟然出现”你是选苹果手机还是选微信”这个问题,真是搞不懂!首先要明白一点,微信和苹果不可能开战,因为一旦开战,这绝对是两败俱伤的局,很惨的!而有的人还说,选择苹果手机不爱国,哎呦我去!现在哪款手机的操作系统不是国外的?现在我们使用的所有手机都是国外的系统,没有这些系统,手机只是一个壳,你只能看着黑屏的它!所以,只有我们国家自己打造出属于我们自己的移动操作系统,那样才真正意义上是属于我们中国的手机!那个时候你愿意用我们自己的手机,我就给你点赞!

苹果与微信
    先科普一点知识:目前手机系统主要分为iOS和Android两大阵营,iOS是苹果开发出来的移动操作系统,Android是Google(谷歌)公司开发出来的移动操作系统,所以对应的它们都有自己的应用商店-APP Store和Google Play,大家可能对苹果的APP Store听的多,对Google Play听的较少,Google Play是Google针对Android系统应用打造的的应用商店,只不过现在这个商店已经退出中国了,中国目前的Android应用商店主要是应用宝、百度手机助手、360手机助手、豌豆荚、小米应用商店、vivo应用商店等这些.
    应用的抽成情况:大家都知道了APP Store抽成是30%,其实Android的应用商店也是有抽成的的,Google Play抽成30%、小米应用商店抽成30%、百度手机助手30%~50%、应用宝抽成10%~40%、360手机助手抽成30%~60%、华为和vivo应用商店都是抽成30%~60%,看见了吧,天下没有免费的午餐的!其实苹果的APP Store和Google的Google Play都抽成30%我们还是可以理解的,毕竟这两者都开发出的自己的移动操作系统,但是不理解的是国内的应用商店抽成竟然比前两者还高…呵呵…赚钱吧!….具体下面有图……
应用抽成表
    言归正传:相信大家看过上面的简介之后已经有了初步的了解,下面来谈微信打赏的问题,事实上,打赏是完全可以走红包支付、转账通道直接转给作者,这样是不需要被苹果提成的,但是微信为什么不这样做呢?因为这样做以后腾讯同样不能对打赏提成,要知道这个腾讯对打赏抽成可是很高的.于是苹果说要30%的抽成后腾讯直接关闭了打赏,企图将公众号作者的利益与苹果对立起来,因为公众号在iOS上赚不到打赏钱,自然会每天狂黑苹果来试图将这群优质用户转到安卓…这种做法让我笑了..但是!公众号作者们真以为黑走苹果以后能赚更多钱????…醒醒吧!…一切都是利益!….一切都是套路!…

二.苹果禁止开发者进行热更新

    这段时间的禁止热更新加上前段时间的苹果对战微信,又将苹果推上了风口浪尖!
热更新热更新
    什么是热更新:大家再用苹果手机玩王者荣耀、或者阴阳师、或者使用12306等APP的时候,就拿王者荣耀和12306来说,你登上王者荣耀的时候,有时候会出现”更新”按钮,然后你一点这个按钮,就会出现一个进度条,然后进度条满了,提示你更新成功,这就是热更新!你用12306买火车票,打开12306这个APP的时候,有时候会出现”升级”这个提示框,然后你一点,有个进度条,然后进度条满了之后给你一个提示框”升级成功!”,这就是热更新!
    热更新的危害:热更新技术对于开发人员是好,因为不需要在上传新版本给应用商店进行审核,直接有新内容就可以随时更新,但是,使用热更新技术的应用可能会被黑客攻破呢!会盗取你的信息!会对你造成一定的安全的隐患!
    最新的热更新规则:苹果没有禁止热更新!苹果没有禁止热更新!苹果没有禁止热更新!重要事情说三遍!热更新技术有很多,苹果只是禁止了对用户有危害的相关的热更新技术,并不是禁止了热更新这个东西!不信你看现在的王者荣耀照样可以热更新而不被APP Store下架,因为王者荣耀APP使用的热更新技术符合苹果的规则和安全性…..所以呀!现在的媒体,逮着个信息就炒作,传到最后都变味了…现在的媒体真是强大,让万达就直接一天蒸发了近60亿….
    热更新推荐技术:React Native


    最后总结:现在网络发达,随便一个谣言的传播都能误导好多人!套路啊!都是套路!其实受害的还是我们这些老百姓…

iOS 开发使用OC语言自定义字体

    讲述iOS开发中可能会遇到自定义字体的问题,本篇文章将一步一步教你设置自定义字体,我的邮箱是KenenCS@163.com,欢迎多多交流!

–此demo的地址在GitHub的 CustomFontDemo


先来一张效果图,放大仔细看,注意,字体的大小全是19:

自定义字体效果图




工程配置

    #pragma mark–第一步,下载你需要的字体包,导入工程中(我上传的demo上有两个示例包).
    #pragma mark–第二步,在info.plist里面进行配置,Information Property List -> 添加Fonts provided by application字段(Array类型) -> Fonts provided by application字段中添加你的包名(示例是DINPro-Regular.otf和PingFang Regular.ttf).
工程配置
    #pragma mark–第三步,在工程中前往TARGETS -> Build Phases -> Copy Bundle Resources ,看看此处的两个字体包有没有添加上,没有添加上就自己手动添加一下.工程配置

按照上面的步骤你的字体配置已经完成了,开始写代码吧!

代码编辑

    #pragma mark–下面的代码是以Label进行展示的,还按照你平常创建Label方式进行创建,只不过在此基础上加上一句话label.font = [UIFont fontWithName:@”字体名字” size:19]; 也就是你所有的前期工作只是为了这一句代码进行服务的.
代码配置

恭喜你!大功告成!



以上就是详细代码,demo的地址在GitHub的 CustomFontDemo,欢迎下载交流!

iOS 使用OC语言开发之NSURLSession(二)

    上一遍主要讲解了NSURLSession的GET和POST请求以及文件上传,这一篇主要讲解一下下载文件的问题,因为下载牵扯到正常下载,断点下载,后台下载,内容比较多,所以放到单独的一篇,我的邮箱是KenenCS@163.com,欢迎多多交流!

–此demo的地址在GitHub的 NSURLSessionDownloadDemo


废话不多说,直接上代码,让我们一起代码会友:

    #pragma mark–.ViewController.m



以上就是详细代码,demo的地址在GitHub的 NSURLSessionDownloadDemo,欢迎下载交流!

iOS 使用OC语言开发之NSURLSession(一)

    是不是用惯了AFNetWorking你就忘记了底层的网络请求了呢?自从iOS7的之后NSURLConnection已经被苹果踢了,用NSURLSession进行代替,来进行网络请求,本篇文章主要讲述使用NSURLSession进行GET和POST请求以及进行文件上传,下载和断点下载以及后台下载决定放到下一篇文章单独讲,我的邮箱是KenenCS@163.com,欢迎多多交流!

–此demo的地址在GitHub的 NSURLSessionTestDemo


废话不多说,直接上代码,带你一起回顾网络请求:

    #pragma mark–你要声明一个NSURLSessionDataDelegate这个代理,这个代理是GET和POST请求要用它的一些代理方法.ViewController.m
    #pragma mark–请求URL的方法,因为后面是分为数据请求和文件上传的,所以写到一个大方法里面.ViewController.m
    #pragma mark–请求URL的方法,GET请求的代码和POST请求的代码都有,注意GET请求用的是NSURLSession,POST请求用的是NSMutableURLSession.我这方法里面写了两个小方法,一个是GET和POST请求,一个是文件上传,分开写是为了便于大家后面的观看.ViewController.mViewController.m
    #pragma mark–这个是GET和POST的数据请求,注意使用的是NSURLSessionDataTask,它就是数据请求的任务创建.另外不要忘了执行任务的代码,要不然是不会执行的哦!ViewController.m
    #pragma mark–这是数据请求的代理方法,有接收到服务器响应数据时调用的方法,接收到服务器响应的时候你也要给它一个回应,告诉他该怎么做….有接收到服务器返回数据调用的方法,有时候数据过大的话该方法会调用多次.ViewController.m
    #pragma mark–这是上传文件的方法,记得是使用NSURLSessionUploadTask创建任务的,这里面有两种创建方式,一种是有参数的,一种是无参数的,现在为了安全起见一般用的都是有参数的上传方法,另外不要忘记执行任务哦!.ViewController.m
    #pragma mark–这是一个代理方法,这个代理方法只要是请求的,不管是数据请求或是文件上传还是下载,当完成任务时这个方法都会被调用.ViewController.m



以上就是详细代码,demo的地址在GitHub的 NSURLSessionTestDemo,欢迎下载交流!

iOS 使用OC语言开发3DTouch功能

    3DTouch功能是iphone的专属功能,我们在开发中用到的几率很大,本篇文章讲解的是应用图标按压的3DTouch功能和界面某个控件按压的3DTouch功能,我的邮箱是KenenCS@163.com,欢迎多多交流!

一.应用图标的3DTouch功能开发


先来一张效果图:

应用图标的3DTouch



看一下具体代码:

AppDelegate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//设置APP图标的3DTouch
[self set3DTouch];
return YES;
}
#pragma mark-------设置APP图标的3DTouch</span>
- (void)set3DTouch {
//使用系统自带图标(Type代表图标类型)
UIApplicationShortcutIcon *icon1 = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeHome];
UIApplicationShortcutIcon *icon2 = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeLove];
//也可以使用自己制作的图片
//UIApplicationShortcutIcon *icon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"自己的图片"];
//添加标题和图片(这里示范两个标题)
UIApplicationShortcutItem *item1 = [[UIApplicationShortcutItem alloc] initWithType:@"item1" localizedTitle:@"标题1" localizedSubtitle:nil icon:icon1 userInfo:nil];
UIApplicationShortcutItem *item2 = [[UIApplicationShortcutItem alloc] initWithType:@"item2" localizedTitle:@"标题2" localizedSubtitle:nil icon:icon2 userInfo:nil];
//添加到数组
NSArray *array = @[item1,item2];
//这个就是关键代码
[UIApplication sharedApplication].shortcutItems = array;
}
#pragma mark-----APP图标的3DTouch代理方法
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
//3DTouch实现界面跳转
if ([shortcutItem.type isEqualToString:@"item1"]) {
//跳转界面记得设置rootViewController</span>
NSLog(@"按压了第一个标题");
}else if ([shortcutItem.type isEqualToString:@"item2"]) {
//跳转界面记得设置rootViewController
NSLog(@"按压了第二个标题");
}
}

二.界面按压3DTouch功能


再来看一张效果图:

某个控件的3DTouch



看一下具体代码:

首先导入SafariServices.framework这个框架,看这个名字你应该就知道是和Safari浏览器有关的,没错,你想的很对!

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#import <SafariServices/SafariServices.h>
@interface ViewController ()<UIViewControllerPreviewingDelegate> {
UILabel *label;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
}
-(void)loadView {
[super loadView];
//创建个label,一会儿就按压它
label = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
label.text = @"我的微博";
label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:label];
//注册代理
[self registerForPreviewingWithDelegate:self sourceView:self.view];
}
#pragma mark----PreviewingDelegate
-(void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
//固定这么写
[self showViewController:viewControllerToCommit sender:self];
}
-(UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
//location就是重压点坐标,如果按压点在label上执行以下方法
if (location.x > 100 && location.x < 200 && location.y > 100 && location.y < 200) {
SFSafariViewController *mySV = [[SFSafariViewController alloc]initWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
//第一次按压时,弹出的窗口尺寸,再次按压则跳转到mySV
mySV.preferredContentSize = CGSizeMake(0, 400);
//设置高亮区域
previewingContext.sourceRect = label.frame;
return mySV;
}
return nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

三.项目地址


此小demo的地址在GitHub的 3DTouch,欢迎下载交流!

iOS使用OC语言动态生成本地图片验证码

    前段时间项目中要做一个本地图片验证码,其实说是图片验证码,并不是图片,看完下面你就明白了,我下面是照片教程,你复制不了代码,就是想让你跟着敲代码,如果只是一昧的复制粘贴,我想对你的提高并不大,你可以下载我的demo然后按照教程来走一遍会记得更加清楚呢!我的邮箱是KenenCS@163.com,欢迎多多交流!

–此demo的地址在GitHub的 AuthenticationCode


先看下效果图界面效果

输出的验证码




一.Controller中的代码


ViewController.m
    #pragma mark–在控制器里面只需要导入创建就行了,如果想点击更换,就调用changeCode这个方法,进行重新制作验证码.KenCodeView.m



二.自定义View,封装画线方法


KenCodeView.h
    #pragma mark–这里面主要是,声明一些属性和方法,属性有:字符数组,验证码字符串和展示验证码的label,在这里声明的改变验证码的方法是为了一会儿在Controller中调用的.KenCodeView.h



KenCodeView.m
    #pragma mark–这是初始化时调用的方法,设置了随机的背景颜色,并调用生成验证码方法.KenCodeView.m

    #pragma mark–这是生成验证码的方法,当中调用生成验证码的字符方法,setNeedsDisplay是调用drawRect方法,所以Controller中只需要调用这个方法就可以重新生成验证码.KenCodeView.m

    #pragma mark–这是生成验证码的字符方法,根据你的需求进行素材组合和字符控制,我这儿是4个字符的例子,用self.changString进行接收这个验证码字符.KenCodeView.m

    #pragma mark–好!关键的方法来了,这个方法是通过setNeedsDisplay来调用的,是绘制方法.我这里面主要代码都有注释,请详细观看!(下面代码过长,截的是两张图片)KenCodeView.mKenCodeView.m



以上就是详细代码,demo的地址在GitHub的 AuthenticationCode,欢迎下载交流!

|