NSMutableDictionary拼接字典
NSMutableDictionary *dic1 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"BMW",@"CarLogo",@"Red",@"CarColor", nil];
NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:@"Xiaoming",@"name",@"28", @"age",nil];
[dic1 addEntriesFromDictionary:dic2];
NSLog(@"%@",dic1);
Privacy - Photo Library Additions Usage Description 相册的描述
Privacy - Camera Usage Description 相机的描述
- 定位隐私描述描述
参考
(1)在项目的 Info.plist 添加定位权限申请,根据您的实际业务需求,选择如下方式设置:
- NSLocationWhenInUseUsageDescription:表示应用在前台的时候可以搜到更新的位置信息;
- NSLocationAlwaysUsageDescription:表示应用在前台和后台(suspend 或 terminated)都可以获取到更新的位置数据;
- NSLocationAlwaysAndWhenInUseUsageDescription:申请永久定位权限,以便应用在前台和后台都可以获取位置数据;
//iOS11新出的
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Give me location authority</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Give me location authority</string>
模态 modal出半透明的控制器
ZCWithdrawPasswordVC *vc = [ZCWithdrawPasswordVC new];
vc.modalPresentationStyle =UIModalPresentationOverCurrentContext|UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:^{
UIColor *color = [UIColor blackColor];
vc.view.backgroundColor = [color colorWithAlphaComponent:0.55];
}];
把view某个直角切成圆角
//把 view2 的 左下角 和 右下角的直角切成圆角
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(120,10,80,80)];
view2.backgroundColor = [UIColor redColor];
[self.view addSubview:view2];
//得到view的遮罩路径
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view2.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10,10)];
//创建 layer
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = view2.bounds;
//赋值
maskLayer.path = maskPath.CGPath;
view2.layer.mask = maskLayer;
圆角图片
-(void)kt_addCorner:(CGFloat)radius
{
if (self.image) {
self.image = [self.image imageAddCornerWithRadius:radius andSize:self.bounds.size];
}
return;
}
- (UIImage*)imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
CGContextAddPath(ctx,path.CGPath);
CGContextClip(ctx);
[self drawInRect:rect];
CGContextDrawPath(ctx, kCGPathFillStroke);
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
阿拉伯数字123转中文显示一二三
cell.labelTitle.text = [NSString stringWithFormat:@"第%@周",[self chineseWithInteger:indexPath.row + 1]];
- (NSString *)chineseWithInteger:(NSInteger)integer {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = kCFNumberFormatterRoundHalfDown;
NSString *string = [formatter stringFromNumber:[NSNumber numberWithInt:(int)integer]];
return string;
}
防止按钮重复点击
- 控制enabled属性
[btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- (void)clickBtn:(UIButton *)sender {
sender.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
sender.enabled = YES;
});
}
- 使用performSelector:withObject:afterDelay:和cancelPreviousPerformRequestsWithTarget方法实现(缺点是会有延迟)
[btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- (void)clickBtn:(UIButton *)sender {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(nextAction) object:nil];
[self performSelector:@selector(nextAction) withObject:nil afterDelay:1];
}
- 通过runtime对UIButton的sendAction:to:forEvent:方法hook
3.1 对象关联技术为UIButton添加属性
@interface UIButton (Category)
/**
* 按钮点击的间隔时间
*/
@property (nonatomic, assign) NSTimeInterval clickDurationTime;
@end
@implementation UIButton (Category)
- (void)setClickDurationTime:(NSTimeInterval)clickDurationTime {
objc_setAssociatedObject(self, @selector(clickDurationTime), @(clickDurationTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSTimeInterval)clickDurationTime {
return [objc_getAssociatedObject(self, @selector(clickDurationTime)) doubleValue];
}
@end
3.2 对sendAction:to:forEvent:方法hook
参考:
https://www.jianshu.com/p/925f8176781f
https://blog.csdn.net/icetime17/article/details/51782983
// 默认的按钮点击时间
static const NSTimeInterval defaultDuration = 3.0f;
// 记录是否忽略按钮点击事件,默认第一次执行事件
static BOOL _isIgnoreEvent = NO;
// 设置执行按钮事件状态
static void resetState() {
_isIgnoreEvent = NO;
}
@implementation UIButton (Tool)
+ (void)load {
SEL originSEL = @selector(sendAction:to:forEvent:);
SEL mySEL = @selector(my_sendAction:to:forEvent:);
Method originM = class_getInstanceMethod([self class], originSEL);
const char *typeEncodinds = method_getTypeEncoding(originM);
Method newM = class_getInstanceMethod([self class], mySEL);
IMP newIMP = method_getImplementation(newM);
if (class_addMethod([self class], mySEL, newIMP, typeEncodinds)) {
class_replaceMethod([self class], originSEL, newIMP, typeEncodinds);
} else {
method_exchangeImplementations(originM, newM);
}
}
- (void)my_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
if ([self isKindOfClass:[UIButton class]]) {// 保险起见,判断下Class类型
//1. 按钮点击间隔事件
self.clickDurationTime = self.clickDurationTime == 0 ? defaultDuration : self.clickDurationTime;
//2. 是否忽略按钮点击事件
if (_isIgnoreEvent) {//2.1 忽略按钮事件
return;
} else if(self.clickDurationTime > 0) {//2.2 不忽略按钮事件
// 后续在间隔时间内直接忽略按钮事件
_isIgnoreEvent = YES;
// 间隔事件后,执行按钮事件
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.clickDurationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resetState();
});
// 发送按钮点击消息
[self my_sendAction:action to:target forEvent:event];
}
} else {
[self my_sendAction:action to:target forEvent:event];
}
}
后台持续运行
- 持续定位
- 播放无声音乐
问题 - 比较耗电
- 审核可能不通过
参考
https://juejin.im/post/5c1784c0f265da6167203bb3
https://www.jianshu.com/p/47ff7c239ba2
FPS帧率检测
AppDelegate导入 #import "YYFPSLabel.h"
YYFPSLabel *fpsLabel = [YYFPSLabel new];
fpsLabel.frame = CGRectMake(20, kScreenHeight/2, 150, 30);
[fpsLabel sizeToFit];
[self.window addSubview:fpsLabel];
手机上显示的APP名称和版本号及其他信息
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] //应用显示的名称
#import "UIApplication+YYAdd.h"
[[UIApplication sharedApplication] appVersion]//版本号 1.2.0
//设备唯一标识符
NSString *identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
NSLog(@"设备唯一标识符:%@",identifier);
//手机别名:用户定义的名称
NSString *userPhoneName = [[UIDevice currentDevice] name];
NSLog(@"手机别名:%@",userPhoneName);
//设备名称
NSString *deviceName = [[UIDevice currentDevice] systemName];
NSLog(@"设备名称:%@",deviceName);
//手机系统版本
NSString *phoneVersion = [[UIDevice currentDevice] systemVersion];
NSLog(@"手机系统版本:%@",phoneVersion);
//手机型号
NSString *phoneModel = [CXDHelper iphoneType];
NSLog(@"手机型号:%@",phoneModel);
//地方型号(国际化区域名称)
NSString *localPhoneModel = [[UIDevice currentDevice] localizedModel];
NSLog(@"国际化区域名称:%@",localPhoneModel);
UITableView的Group style
注意:在grouped style表中不能有一个(右边的)索引
Group类型默认设置tableView灰色背景色,cell为白色背景色,section外边缘设置浅灰色边框,cell设置浅灰色间隔线。如下图:
注意:
去掉Group类型的表section头部和中间间隔的方法:
- 设置标题tableHeaderView的高度为特小值,但不能为零,若为零的话,ios会取默认值18,就无法消除头部间距了。
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 0.001)];
view.backgroundColor = [UIColor redColor];
self.tableView.tableHeaderView = view;
- 设置代理方法(中间的留白其实是段尾的高度,代理的作用设置段尾的高度,返回值也不能为0,否则系统启用默认值18)
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01f;
}
注意:sectionHeaderHeight/sectionFooterHeight这2个属性只在Grouped类型,且未实现代理方法tableView:heightForHeaderInSection: 时有效
在plain风格下设置无效。故在使用UITableView过程中尽量使用代理方法设置sectionHeader和sectionFooter的高度
UITableView reloadData刷新完毕的时机
参考:https://juejin.im/post/5bf572a3e51d45218f3d0591
reloadData是一个异步方法,并不会等待UITableView或者UICollectionView(后面统称listView)真正刷新完毕后才执行后续代码,而是立即执行后续代码。那么,直接在reloadData后执行代码是有可能出问题的。
- 同步:numberOfSectionsInCollectionView和numberOfItemsInSection
- 异步:cellForItemAtIndexPath
- 同步+异步:sizeForItemAtIndexPath
如果表中的数据非常大,在一个run loop周期可能不会执行完,所以cellfor方法是异步的,这时,需要tableview视图数据的操作就会出问题了。
想要获取listView真正刷新完毕的时机,可以用以下方法
- 通过layoutIfNeeded方法,强制重绘并等待完成
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
// 刷新完成,执行后续需要执行的代码
if ( self.didPlayIdx ) {
MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
if (cell) {
[cell playWithPlayer:self.player];
}
}
- reloadData方法会在主线程执行,通过GCD,使后续操作排队在reloadData后面执行。一次runloop有两个机会执行GCD dispatch main queue中的任务,分别在休眠前和被唤醒后。设置listView 的layoutIfNeeded为YES,在即将进入休眠时执行异步任务,重绘一次界面
[self.collectionView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
// 刷新完成,执行后续代码
if ( self.didPlayIdx ) {
MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
if (cell) {
[cell playWithPlayer:self.player];
}
}
});
PCH文件的创建和配置
Build Settigs --> 搜索prefix关键字--> Precompile Prefix Header改为YES --> 点击下面的空白把PCH相对路径粘贴上就OK了
自定义代码块路径
~/Library/Developer/Xcode/UserData/CodeSnippets
清除缓存路径
~/Library/Developer/Xcode/DerivedData
~/Library/Caches/com.apple.dt.Xcode
切换UIWindow的rootViewController根控制器动画
// 登陆后淡入淡出更换rootViewController
- (void)restoreRootViewController:(UIViewController *)rootViewController
{
typedef void (^Animation)(void);
UIWindow* window = [UIApplication sharedApplication].keyWindow;
rootViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
Animation animation = ^{
BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
[UIApplication sharedApplication].keyWindow.rootViewController = rootViewController;
[UIView setAnimationsEnabled:oldState];
};
[UIView transitionWithView:window
duration:0.5f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:animation
completion:nil];
}
YYModel模型转字典 且 深拷贝返回可变对象
NSMutableDictionary *parameters = [[self.requestShopListModel modelToJSONObject] mutableCopy];
动画效果
[UIView animateWithDuration:0.2 animations:^{
self.btnPSWLogin.alpha = 1.f;
self.btnCode.alpha = self.btnRole.alpha = self.btnRegister.alpha = self.pswSuperView.alpha = 0.f;
self.loginBtnToPhoneSpace.constant = 20;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[self.btnLogin setTitle:@"获取验证码" forState:UIControlStateNormal];
[self.btnLogin setTitle:@"获取验证码" forState:UIControlStateDisabled];
}];
读取本地文件
- 读取plist文件
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]];
- 读取json文件(如果读取不到内容或者nil,先检查一下json格式是否正确,因为这个曾经耽误我很多时间)
// 读取本地JSON文件
- (NSDictionary *)readLocalFileWithName:(NSString *)name {
// 获取文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"json"];
// 将文件数据化
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
// 对数据进行JSON格式化并返回字典形式
return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
}
- 读取本地图片 自动区分@2x @3x 的图片
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png",imageName]];
iOS11之后设置leftBarButtonItem的距离左侧间距的方法
通过在创建 UIBarButtonItem 时设置 custom view 的布局,但是又会出现部分区域不能接收到点击事件
UIButton *button = [UIButton buttonWithImageName:@"back_blackColor"];
button.size = CGSizeMake(70, 30);
// 让按钮内部的所有内容左对齐
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
// 让按钮的内容往左边偏移10
button.contentEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
[button addTarget:self action:@selector(clickLeftItem) forControlEvents:UIControlEventTouchDown];
// 修改导航栏左边的item
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
断言NSAssert的使用
参考
NSAssert是一个预处理宏, 他的主要作用就是可以让开发者比较便捷的捕获一个错误, 让程序崩溃, 同时报出错误提示
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
当调用SDWebImage的上面的方法, 如果变量完成的回调为nil, 此时程序就会崩溃, 并且抛出一个异常, 异常提示 调用此方法是没有意义的
设置行间距
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:NSTextAlignmentCenter];
[style setLineSpacing:self.configure.lineSpacing];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
[attributedString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, string.length)];
label自适应高度宽度
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
label.text = @"大师傅好搜啊发";
label.numberOfLines = 0;
CGSize size = CGSizeMake(100, MAXFLOAT);//设置高度宽度的最大限度 CGRect rect = [label.text boundingRectWithSize:size options:NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:20]} context:nil];
label.frame = CGRectMake(100, 100, rect.size.width, rect.size.height);
self.view addSubview:label];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 0)];
label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
label.text = @"我会自己去适应";
label.numberOfLines = 0;
[label sizeToFit];
[self.view addSubview:label];
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
label.text = @"我会自己去适应宽度的";
label.font = [UIFont systemFontOfSize:50];
CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:50]}];
//ceilf()向上取整函数, 只要大于1就取整数2. floor()向下取整函数, 只要小于2就取整数1.
CGSize adaptionSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
label.frame = CGRectMake(100, 100, adaptionSize.width, adaptionSize.height);
[self.view addSubview:label];
统计项目代码行数
- 打开终端
- cd 进入项目根目录
- 输入命令行
find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" ")" -print | xargs wc -l
最后显示的total前面的数字就是项目所有的代码行数.
每一行前面的数字表示,某一个.m或者.h文件中的代码行数.
参考
判断字符串是否是数字
判读模拟器
#if !TARGET_IPHONE_SIMULATOR
#endif
延迟执行与取消延迟执行
[self performSelector:@selector(onClickOverlay:) withObject:nil afterDelay:DelayTimeSeconds];
//延迟
[NSObject cancelPreviousPerformRequestsWithTarget:self]; //这个是取消当前run loop 里面所有未执行的 延迟方法(Selector Sources)
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onClickOverlay:) object:nil];// @selector 和 object 都和 延迟一致 就是 指定取消 未执行的一条或者多条的延迟方法.
iOS去除数组中重复的model数据
// 去除数组中model重复
for (NSInteger i = 0; i < self.selectedModelArray.count; i++) {
for (NSInteger j = i+1;j < self.selectedModelArray.count; j++) {
AssistantModel *tempModel = self.selectedModelArray[I];
AssistantModel *model = self.selectedModelArray[j];
if ([tempModel.assistantId isEqualToString:model.assistantId]) {
[self.selectedModelArray removeObject:model];
}
}
}
iOS 获取蜂窝网络信号强度 包含iPhoneX XS XR XSMASX
https://www.cnblogs.com/allencelee/p/10483265.html
何获取导航栏中rightBarButtonItem对应self.view的坐标
https://blog.csdn.net/allanGold/article/details/80675331
https://segmentfault.com/q/1010000005592396
界面旋转
https://juejin.im/post/5d0233da6fb9a07eac05ce51
设置UISlider滑轨宽度
- (CGRect)trackRectForBounds:(CGRect)bounds {
return CGRectMake(0, 0, ScreenWidth, 15);
}
判断每天第一次和每周第一次打开APP
https://www.jianshu.com/p/f22e9aba1f7b
safari打开链接注意对URL做转义处理
+ (void)jumptoSafariWithLinkStr:(NSString *)linkStr VC:(UIViewController *)weakSelf {
if (![linkStr hasPrefix:@"http://"]&&![linkStr hasPrefix:@"https://"]) {
linkStr=[NSString stringWithFormat:@"http://%@",linkStr];
}
NSURL *url = [NSURL URLWithString:linkStr];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}else {
NSString* encodedString = [linkStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
BOOL canOpen = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:encodedString]];
if (!canOpen) {
[PSProgressHUD showMessageHUD:^(PSProgressHUD *make) {
make.message(@"无法打开链接");
}];
DDLogInfo(@"%s 打开链接失败. URL:%@ \n EncodingUrl:%@", __FUNCTION__, linkStr,encodedString);
} else {
DDLogInfo(@"%s 打开链接中包含中文. URL:%@", __FUNCTION__, linkStr);
}
}
}