0%

openURL打开应用

B应用打开A应用

需要A应用设置URL Scheme,如testA,并且告知B应用.此时B应用就可以通过”testA://“打开A应用了.

A应用实现方法

1
2
3
4
5
6
7
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
DLog(@"%@", app);
DLog(@"%@", url);
DLog(@"%@", options);
return YES;
}

传递参数

只能是key=value形式,多个参数使用&连接.
testA://?key1=value1&key2=value2&key3=value3

注意
value是一个json字符串时将打不开A应用.这是由于json字符串中的花括号和冒号都是保留字符.
testA://?p={"type":2,"detail":{"backAction":1}}

可以把json字符串百分号编码后传递,如下:
testA://?p=%7B%22type%22%3A2%2C%22detail%22%3A%7B%22backAction%22%3A1%7D%7D

PINCache使用注意事项

默认的[PINCache sharedCache] 文件保存在目录/Library/Caches/com.pinterest.PINDiskCache.PINCacheShared 在磁盘空间不足时/Caches目录下的所有文件都会被系统自动清除,不适合存储用户信息,登录状态等相关数据.

诡异BUG
现象
用户在APP中登录成功后,每隔一段时间(大概2-3个小时),再次打开App后,App没有进入首页而是到了登录页.由于不是必现,所以难以分析其具体原因.

分析解决
经过个把星期的纠缠,终于发现原因:在写的时候将登录成功后的token使用[PINCache sharedCache]保存了,而它的默认保存路径就是在/Caches目录下,对于那些磁盘空间一直徘徊在200-300M的手机,系统会周期性的清理各个App的/Caches目录下的所有文件,来释放部分磁盘空间.这时token就被清理了,而启动时是先从文件中取token进行判断,由于已被清除,自然也取不到,因此进入了登录页.解决办法:换一个路径保存就可以了.

计算字符串占用的字节长度

前提

需要了解字符的编码规则.使用不同的编码规则会得到不同的结果.

API

- (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)enc; // Result in O(n) time; the result is exact. Returns 0 on error (cannot convert to specified encoding, or overflow)

示例

1
2
3
4
5
NSString *chString = @"中国abc";
NSUInteger utf8Bytes = [chString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"utf8Bytes:%ld", utf8Bytes); //9
NSUInteger unicodeBytes = [chString lengthOfBytesUsingEncoding:NSUnicodeStringEncoding];
NSLog(@"unicodeBytes:%ld", unicodeBytes); //10

天朝目前强制使用的是GB18030.

CFStringEncodingExt.h里面包含了其他编码枚举.如:

1
2
3
kCFStringEncodingGB_2312_80 = 0x0630,
kCFStringEncodingGBK_95 = 0x0631, /* annex to GB 13000-93; for Windows 95 */
kCFStringEncodingGB_18030_2000 = 0x0632,
阅读全文 »

图片压缩笔记

压缩方法

工程里图片压缩方法过程:传入一张图片,压缩后,再转回图片.加上上传到服务器的整个过程如下:
UIImage-大力压缩->NSData(A)->UIImage-UIImageJPEGRepresentation(img, 1)->NSData(B)->服务器.

测试发现dataB要比dataA大很多很多.导致实际上传给服务器的数据远远不止600kb.eg某次上传的头像:4.56Mb(原图,压缩前)->579.16kb(压缩后)->2.93Mb(实际上传).实际上传的数据是2.93Mb

因此再转回UIImage是多余且错的.而应该压缩后直接上传服务器,即UIImage-大力压缩->NSData(A)->服务器.

正确的压缩方法为:

1
2
3
4
5
6
7
8
9
10
11
//指定压缩阈值,压缩图片
+ (NSData *)za_compressImage:(UIImage *)image toMaxFileSize:(NSInteger)maxFileSize {
CGFloat compression = 0.9f;
CGFloat minCompression = 0.1f;
NSData *imageData = UIImageJPEGRepresentation(image, compression);
while ([imageData length] > maxFileSize && compression > minCompression) {
compression -= 0.05;
imageData = UIImageJPEGRepresentation(image, compression);
}
return imageData;
}

在现有目录中初始化仓库

1
git init

执行后,会创建一个.git文件夹

1
2
3
4
➜  gitdemoprj git init
Initialized empty Git repository in /Users/xuequan/Desktop/gitdemoprj/.git/
➜ gitdemoprj git:(main)ls -a
. .. .git.md

目前默认创建的分支名是main,不是master了。不注意的话后续一些操作比如push等等可能会出错。如果git init时不想使用默认的main分支名怎么办呢?

方法1:在初始化的时候指定分支名

1
git init -b master //这里指定为master

方法2:全局设置初始化的分支名称。

1
2
3
git config --global init.defaultBranch <name>
示例:
git config --global init.defaultBranch master

该命令会在~/.gitconfig文件里添加配置:

阅读全文 »

TTTAttributedLabel使用

Q1:使用pod管理TTTAttributedLabel,在XIB中使用会导致XIB报错并且一片空白.

A:没找到一种好的解决办法,只能不使用XIB.

Q2:使用TTTAttributedLabel,在聊天cell(xib实现)中,点击链接代理方法不被调用,原因是TTTAttributedLabel的touchesCancelled一直被调用了,touchesEnded没被调用.

A2:代码中有添加点击手势导致label的touch事件总是被cancel.

1
2
3
4
UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedView:)];
tapGes.delegate = self;
tapGes.cancelsTouchesInView = NO; //重要
[self.view addGestureRecognizer:tapGes];

手势与响应者链

父视图上添加了点击手势,有时(不是每次)会导致父视图上的UIButton的action方法不会被调用.
原因在于如果父视图识别出了手势,那么会cancel掉UITouch.从而第一响应者会收到touchesCancelled消息.基本上那些原本不接受用户事件的控件比如UILabel即使设置userInteractionEnabled=YES,但它的touchesEnded将不会被调用.而UIControl子类控件他们的action方法有时不会被调用.

SCLAlertView使用

SCLAlertView设置

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
- (void)showPopView
{
//提交成功
SCLAlertView *alertView = [[SCLAlertView alloc] initWithNewWindowWidth:310];
//去掉顶部圆圈
[alertView removeTopCircle];
//按钮水平放置
[alertView setHorizontalButtons:YES];
//设置出现动画
alertView.showAnimationType = SCLAlertViewShowAnimationSimplyAppear;
alertView.shouldDismissOnTapOutside = NO;

//设置title的字体和颜色
alertView.labelTitle.font = [UIFont pingFangSCWithSize:16];
alertView.labelTitle.textColor = [UIColor blackColor];

//设置富文本的subTitle
alertView.attributedFormatBlock = ^NSAttributedString *(NSString *value) {
return [NSString attributeString:value subString:@"13812341234" subStrColor:kRedColorRegular font:[UIFont pingFangSCWithSize:16] otherStrColor:[UIColor colorWithHexString:@"#818181"]];
};
[alertView setBodyTextFontFamily:@"PingFangSC-Regular" withSize:16];
//设置普通文本的subTitle
// alertView.viewText.font = [UIFont pingFangSCWithSize:16];
// alertView.viewText.textColor = [UIColor colorWithHexString:@"#818181"];

//设置Buttons, top circle and borders的颜色简便方法
alertView.customViewColor = [UIColor redColor];
//统一设置按钮的配置强大方法
alertView.buttonFormatBlock = ^ NSDictionary *{
return @{@"backgroundColor": [UIColor cyanColor]};
};

SCLButton *authPersonalBtn = [alertView addButton:@"取消" actionBlock:^(void) {
DLog(@"取消按钮被按下");
}];
authPersonalBtn.titleLabel.font = [UIFont pingFangSCWithSize:16];
authPersonalBtn.buttonFormatBlock = ^ NSDictionary *{
return @{@"backgroundColor": [UIColor whiteColor], @"textColor": [UIColor colorWithHexString:@"#818181"], @"borderWidth": @0};
};

SCLButton *authCompanyBtn = [alertView addButton:@"去注册" actionBlock:^(void) {
DLog(@"去注册按钮被按下");
}];
authCompanyBtn.titleLabel.font = [UIFont pingFangSCWithSize:16];
//单独设置某个按钮的配置
authCompanyBtn.buttonFormatBlock = ^ NSDictionary *{
return @{@"backgroundColor": [UIColor whiteColor], @"textColor": [UIColor blackColor], @"borderWidth": @0};
};

[alertView showSuccess:self title:@"手机号码未注册" subTitle:@"手机号码 13812341234 未注册, 请注册后再登录。" closeButtonTitle:nil duration:0.0f];
}

UITextField笔记

监听文本输入变化

方法1: addTarget — UIControlEventValueChanged

1
2
[self.passwordTextField addTarget:self action:@selector(passwordTextFieldTextDidChanged:) forControlEvents:UIControlEventValueChanged];

方法2: 注册通知 — UITextFieldTextDidChangeNotification

1
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(passwordTextFieldTextDidChanged:) name:UITextFieldTextDidChangeNotification object:nil];

note:

  1. 当点击清空按钮时,只有注册通知的方案,回调方法被调用了.
  2. 直接对textField的text属性赋值,textField.text = @"dfsfsd";是不会触发UIControlEventValueChanged事件,和UITextFieldTextDidChangeNotification通知回调的.可以使用textField.text = @"";[textField insertText:newString];

根域名

在有些场合,www.example.com被写成www.example.com.,即最后还会多出一个点。这个点就是根域名。

理论上,所有域名查询都必须先查询根域名,因为只有根域名才能告诉你,某个顶级域名由哪台服务器管理。事实上也确实如此,ICANN 维护着一张列表,里面记载着顶级域名和对应的托管商。

顶级域名(TLDs)

Top-level domains,first-level domains.
顶级域名是域名的最后一个部分,即是域名最后一点之后的字母,例如在example.com这个域名中,顶级域是.com(或.COM),大小写视为相同。

二级域名(SLD)

second-level domain,最靠近顶级域名左侧的字段。如:zh.wikipedia.org中,wikipedia就是二级域名.

父域名、子域名

这是一个相对概念.
如 www.sina.com.cn 和 news.sina.com.cn 都是 sina.com.cn 的子域名(或称为二级域名), sina.com.cn 就是父域名。

同样,sina.com.cn 其实又可以看作是 .com.cn 的子域名;而 com.cn 又是 .cn 的一个子域名。

阅读全文 »

NSData与它的属性bytes

1
2
3
NSString* enString= @"abc";
NSData* utf8EnData = [enString dataUsingEncoding:NSUTF8StringEncoding];

bytes属性:

1
2
3
4
5
6
/*
The -bytes method returns a pointer to a contiguous region of memory managed by the receiver.
If the regions of memory represented by the receiver are already contiguous, it does so in O(1) time, otherwise it may take longer
Using -enumerateByteRangesUsingBlock: will be efficient for both contiguous and discontiguous data.
*/
@property (readonly) const void *bytes NS_RETURNS_INNER_POINTER;

bytes属性指向的是NSData对象装载的内容.NSData装载的二进制内容在内存中的分布可能是连续的一片,也可能是不连续的.使用- (void) enumerateByteRangesUsingBlock:(void (NS_NOESCAPE ^)(const void *bytes, NSRange byteRange, BOOL *stop))block遍历所有的分布区域.

NSData对象本身的地址与装载的二进制内容的地址,如下图:

可以看到NSData对象本身的地址与二进制内容所在的地址是不同的.二者内存位置相距还挺远的.

查看NSData对象装载的二进制内容方法: