总结一些遇到的问题。
日常工作中用到的一些方法总结,有很简单介绍,可能也有错误,如果您看到了希望可以告诉我,会不间断更新。
一、iOS 10以后拨打电话
iOS 10以后,原来拨打电话的几种方式都不太好用,反应特别慢,苹果更新的新的API,使用openURL:options:completionHandler:
替代了openURL
,具体讲解请点击,谢谢大神的分享。
示例代码:
if (@available(iOS 10.0, *)) {
// 拨打电话 iOS 10以后
NSMutableString *str=[[NSMutableString alloc] initWithFormat:@"tel://%@",@"132****0983"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str] options:@{} completionHandler:nil];
} else {
// 拨打电话 iOS 10以前
NSMutableString *str=[[NSMutableString alloc] initWithFormat:@"tel://%@",@"132****0983"];
UIWebView *callWebview = [[UIWebView alloc] init];
[callWebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:str]]];
[self.view addSubview:callWebview];
}
二、UIWebVIew加载网页时,让网页适应屏幕大小
self.webView.scalesPageToFit = YES;
三、更改UILabel中字距边框的距离
要更改 Label 距离边框的距离就需要自定义 Label,需要重写drawTextInRect
方法,重新绘制文本的位置;
代码示例:
-(void)drawTextInRect:(CGRect)rect{
// 上下左右各距5
[super drawTextInRect:CGRectMake(rect.origin.x + 5, rect.origin.y + 5, rect.size.width - 10, rect.size.height -10)];
}
三、解决“当子视图超出父视图范围,子视图点击事件无反应”问题
需要重写父视图的-hitTest:withEvent:
// self.backButton在父视图frame以外
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if (view == nil) {
// 转换坐标系
CGPoint newPoint = [self.backButton convertPoint:point fromView:self];
// 判断触摸点是否在button上
if (CGRectContainsPoint(self.backButton.bounds, newPoint)) {
view = self.backButton;
}
}
return view;
}
四、获取某个View所在控制器
写代码时,经常去封装一些视图,有时候在视图中去处理一些事件,难免会用到view所在的控制器,可以通过这个方法获取view所在控制器。
// 获取当前控制器
- (UIViewController *)findViewController:(UIView *)sourceView
{
id target = sourceView;
while (target) {
target = ((UIResponder *)target).nextResponder;
if ([target isKindOfClass:[UIViewController class]]) {
break;
}
}
return target;
}
五、UILabel显示html标签(字体的颜色和大小)
今天被这个问题问懵逼了,所以查查资料,学习下,经测试很好用。感谢sunboygpz的分享。
可以使用富文本来实现, 代码如下:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 300, 200)];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:[@"<font style='font-size:20px;color:red' >666666666666666</font> asdasd " dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
label.attributedText = attributedString;
[self.view addSubview:label];
六、解决UITextField设置为密文输入时,再次输入会清空原有内容问题
在UITextField的代理方法中操作,实测好用,代码如下:
// self.passwordTF为密码输入框
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.passwordTF) {
/// 解决密文输入时,再次输入清空问题
NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
textField.text = updatedString;
return NO;
}
return YES;
}
七、解决UIScrollView上添加UISlider时,滑动slider出现的问题
在UIScrollView横向排了3个UIView,其中一个UIView中有UISlider。在左右滑动slider的时候,如果快速滑动slider不会动,scrollview会左右滑动。
可以通过重写UIScrollView 的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
方法解决这个问题,事例代码如下:
/// 解决scrollView上滑动UISlider问题
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView *result = [super hitTest:point withEvent:event];
if ([result isKindOfClass:[UISlider class]]) {
self.scrollEnabled = NO;
}else {
self.scrollEnabled = YES;
}
return result;
}
八、对URL中的中文进行转码
事例代码
NSString *url1 = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
九、关于存储下载文件路径问题
最近项目中需要下载文件,保存到本地,然后把文件路径和一些信息存到数据库,再次读取的时候根据数据库中文件路径去获取文件,发现一个问题,就是iOS8以后苹果公司为了安全每次重启APP的时候,沙盒中唯一编码路径都会发生改变,所以如果直接存会出现获取不到文件的问题,解决办法很简单,就是存储地址的时候只存你放文件的地址(口拙,不知如何描述),不存沙盒地址,再次取地址的时候再重新吧沙盒地址拼接上。示例如下:
// 存地址 filePath为文件路径 :/Users/hao/Library/Developer/CoreSimulator/Devices/7314558F-CF6E-486F-B83D-E39B3189C1E8/data/Containers/Data/Application/BD297778-550D-4888-B301-1ACC70C59BAA/Library/Caches/ListenFile/我害怕.mp3
NSArray *pathArr = [filePath componentsSeparatedByString:@"/"];
// 取后两位保存
model.savePath = [NSString stringWithFormat:@"%@/%@",pathArr[pathArr.count-2],pathArr.lastObject];
// 取地址
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:"本地存储路径"];
十、定时器的创建、暂停、继续、终止
1、创建:简单的举一种,有很多种创建方式,可以自行百度。
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updatePlaybackProgress) userInfo:nil repeats:YES];
2、暂停
[self.progressUpdateTimer setFireDate:[NSDate distantFuture]];
3、继续
[self.progressUpdateTimer setFireDate:[NSDate date]];
4、终止:
[_progressUpdateTimer invalidate]; _progressUpdateTimer = nil;
十一、Xib布局后代码修改约束的值
具体请看Xib布局后代码修改约束的值
十二、修改UITableView头视图的高度
有些情况下需要更新头视图的内容,难免会改变高度,其实只要设置好头视图的frame,然后重新设置为头视图就可以,代码如下:
[self.tableView beginUpdates];// 动画效果
[self.tableView setTableHeaderView:self.headerView(头视图)];
[self.tableView endUpdates];// 动画// 动画
十三、iOS中获取各种文件的目录路径的方法
记录下 IOS中获取各种文件的目录路径的方法很不错。
十四、外包公司的感受
很累,貌似很充实,但是自己成长的太慢,每天都是重复的工作内容,简单无趣,再下去要废掉了,决定逃离。
十五、音乐的后台播放、控制、信息的展示和处理中断事件
1、打开后台设置
TARGETS —> Capabilities —> Background Modes —> 勾选 Audio,AirPlay。。。这项。如图:
2、AppDelegate中代码
在方法
-application:didFinishLaunchingWithOptions:
中设置并激活音频会话类别,这样就可以在后台播放了。代码如下
@interface AppDelegate ()
/// 后台任务的UIBackgroundTaskIdentifier
@property(assign,nonatomic)UIBackgroundTaskIdentifier bgTaskId;
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 设置并激活音频会话类别
_bgTaskId=[AppDelegate backgroundPlayerID:_bgTaskId];
return YES;
}
/// 设置并激活音频会话类别
+(UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
{
//设置并激活音频会话类别
NSError* error;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
//允许应用程序接收远程控制
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
//设置后台任务ID
UIBackgroundTaskIdentifier newTaskId=UIBackgroundTaskInvalid;
newTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
if(newTaskId!=UIBackgroundTaskInvalid&&backTaskId!=UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:backTaskId];
}
return newTaskId;
}
3、接收到远程控制事件
在上一步中允许了应用程序接收远程控制[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
,接着实现-remoteControlReceivedWithEvent:
方法。
#pragma mark - 接收到远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event {
if(event.type==UIEventTypeRemoteControl){
switch (event.subtype)
{
case UIEventSubtypeRemoteControlPause:
/// 暂停 (自行处理)
if ([VLCMusicManage defaultPlayer].currentPlaylistItem && [VLCMusicManage defaultPlayer].isPlaying) {
[[VLCMusicManage defaultPlayer] pause];
}
break;
case UIEventSubtypeRemoteControlPlay:
/// 播放 (自行处理)
if ([VLCMusicManage defaultPlayer].currentPlaylistItem && ![VLCMusicManage defaultPlayer].isPlaying) {
[[VLCMusicManage defaultPlayer] pause];
}
break;
case UIEventSubtypeRemoteControlNextTrack:
/// 下一曲 (自行处理)
if ([VLCMusicManage defaultPlayer].musicPlayList.count != 0) {
[[VLCMusicManage defaultPlayer] playNextItem];
}
break;
case UIEventSubtypeRemoteControlPreviousTrack:
/// 上一曲 (自行处理)
if ([VLCMusicManage defaultPlayer].musicPlayList.count != 0) {
[[VLCMusicManage defaultPlayer] playPreviousItem];
}
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
break;
default:
break;
}
}
}
4、锁屏时,歌曲信息的展示 借鉴源,写的很清楚
设置锁屏界面显示信息的原理是通过设置一个系统的字典,当音频开始播放时,系统会自动从这个字典中读取要显示的信息,如果需要动态显示,我们只需要不断更新这个字典即可。首先需要添加<MediaPlayer/MediaPlayer.h>这个头文件。
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//设置歌曲题目
[dict setObject:@"题目" forKey:MPMediaItemPropertyTitle];
//设置歌手名
[dict setObject:@"歌手" forKey:MPMediaItemPropertyArtist];
//设置专辑名
[dict setObject:@"专辑" forKey:MPMediaItemPropertyAlbumTitle];
//设置显示的图片
UIImage *newImage = [UIImage imageNamed:@"43.png"];
[dict setObject:[[MPMediaItemArtwork alloc] initWithImage:newImage] forKey:MPMediaItemPropertyArtwork];
//设置歌曲时长
[dict setObject:[NSNumber numberWithDouble:300] forKey:MPMediaItemPropertyPlaybackDuration];
//设置已经播放时长
[dict setObject:[NSNumber numberWithDouble:150] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
//更新字典
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
5、处理中断事件
我是在APPDelegate中处理的,代码如下:
// 处理中断事件的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
/// 实现方法
//处理中断事件
-(void)handleInterreption:(NSNotification *)sender
{
NSDictionary *info = sender.userInfo;
AVAudioSessionInterruptionType type = (AVAudioSessionInterruptionType)[info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
//中断开始 自行处理暂停或者播放
[[VLCMusicManage defaultPlayer] pause];
}else{
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
//中断恢复
[[VLCMusicManage defaultPlayer] pause];
}
}
}
打完收工
十六、关于webView加载html时,图片的自适应问题(2017.9.27修改)
(旧,不懂web前端,看不太懂,但是今天用的时候发现在图片小于屏幕的时候,不起效果)
#pragma mark - 修改html代码字符串,适应webview
- (NSString *)adaptWebViewForHtml:(NSString *) htmlStr {
NSMutableString *headHtml = [[NSMutableString alloc] initWithCapacity:0];
[headHtml appendString : @"<html>" ];
[headHtml appendString : @"<head>" ];
[headHtml appendString : @"<meta charset=\"utf-8\">" ];
[headHtml appendString : @"<meta id=\"viewport\" name=\"viewport\" content=\"width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=false\" />" ];
[headHtml appendString : @"<meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />" ];
[headHtml appendString : @"<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\" />" ];
[headHtml appendString : @"<meta name=\"black\" name=\"apple-mobile-web-app-status-bar-style\" />" ];
[headHtml appendString : @"<style>img{width:100%;}</style>" ];
[headHtml appendString : @"<style>table{width:100%;}</style>" ];
[headHtml appendString : @"<title>webview</title>" ];
NSString *bodyHtml;
bodyHtml = [NSString stringWithString:headHtml];
bodyHtml = [bodyHtml stringByAppendingString:htmlStr];
return bodyHtml;
}
最新的 写在这里方便查找
处理HTMLString的方法:
NSString *htmls = [NSString stringWithFormat:@"<header><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'></header> <html> \n" "<head> \n" "</style> \n" "</head> \n" "<body>" "<script type='text/javascript'>" "window.onload = function(){\n" "var $img = document.getElementsByTagName('img');\n" "for(var p in $img){\n" " $img[p].style.width = '100%%';\n" "$img[p].style.height ='auto'\n" "}\n" "}" "</script>%@" "</body>" "</html>",htmlStr];
[self.webView loadHTMLString:htmlString baseURL:nil];
处理HTMLString的原理:
原理就是用一个for循环,拿到所有的图片,对每个图片都处理一次,让图片的宽为100%,就是按照屏幕宽度自适应;让图片的高atuo,自动适应。
十七、UINavigationBar透明渐变情况下,tableView分区区头停留在顶部。
动图不太好看,实际效果还可以。
十八、 获取cell或者cell中的控件在屏幕中的位置
借鉴下,原文地址
主要使用方法:
-(CGRect)convertRect:(CGRect)rect toView:(UIView *)view;
-(CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;
这个两个方法的傻瓜式讲解
1、获取cell在屏幕中的位置
CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];//获取cell在tableView中的位置
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];
2、cell中的控件(比如按钮)在屏幕中的位置
UIWindow* window = [UIApplication sharedApplication].keyWindow;
CGRect rect1 = [sender convertRect:sender.frame fromView:self.contentView];//获取button在contentView的位置
CGRect rect2 = [sender convertRect:rect1 toView:window];
十九、 修改UINavigationController
的子视图控制器数组viewControllers
在开发过程中会有这种情况:从vc1
push到vc2
,再从vc2
push到vc3
,在vc3
中想要直接返回vc1
有很多种处理方法,但是在有系统侧滑手势时,侧滑返回就不好处理,这样就可以通过这种方法来修改。
VC3 *vc3 = [VC3 new];
[self.navigationController pushViewController:vc animated:YES];
// 控制器跳转完成后,修改viewControllers,移除中间控制器
NSMutableArray * controllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[controllers removeObjectsInRange:NSMakeRange(1, 1)];
self.navigationController.viewControllers = controllers;
大概就是这样,可以根据实际情况来修改,自测很好用。
二十、NSDateFormatter转换时间各种格式总结
年份
1、yyyy 表示四位年份,例如2017;
2、yy 表示年份的后两位,例如”2017“会显示“17”;
月份
1、MM 表示两位月份,例如:”十二月“显示为”12“、“4月”显示为“04”;
2、M 在两位月份时,依旧为两位,但是对于一位的月份,则去除前面的0,只显示一位,例如:”十二月“显示为”12“、“4月”显示为“4”。同理,天、小时、分钟、秒一样。
天
1、dd 表示当前月份的哪一天;
2、DD 表示是今年的第*天。
上下午
1、a 表示上下午。
小时
1、hh 表示12小时制;
2、HH 表示24小时制。
分钟
1、mm 表示分钟。
秒
1、s 表示秒。
星期
1、EEEE 表示“星期几”;
2、EEE 表示“周几”。
二十一、使用NSUserdefaults存储数据容易出现Bug
翻看以前的总结记录发现的这问题,复制过来
NSUserDefaults
存储的对象全是不可变的,例如不能存储NSMutableArray
,需要先转为不可变数组,否则会发生崩溃。
二十二、iPhone X适配之状态栏和导航栏高度
iPhone X的状态栏的长高了,这给我这个做外包的👨💻造成了致命的打击,前期写代码的时候,受培训的时候老师影响,死板的认为状态栏的高度就是20,一直是写死,现在前前后后写了十几个项目,基本都需要售后日常维护,这是一个惨痛教训,请大家引以为戒,下面留下获取状态栏高度的方法。
/// 状态栏高度
[[UIApplication sharedApplication] statusBarFrame].size.height
/// navigationbar高度
self.navigationController.navigationBar.frame.size.height
/// 我写的高度宏
/// 状态栏高度
#define StatusBarHeight [[UIApplication sharedApplication] statusBarFrame].size.height
/// 导航栏高度
#define NavigationBarHeight self.navigationController.navigationBar.frame.size.height
/// 状态栏+导航栏高度
#define JWTopBarHeight ([[UIApplication sharedApplication] statusBarFrame].size.height+self.navigationController.navigationBar.frame.size.height)
二十三、CocoaPods的基本操作
记录下CocoaPods的一些基本操作,我脑子有坑,记不住,每次都要百度很烦。
// 搜索三方
pod search
// 创建或打开Podfile文件
vim Podfile
// Podfile文件内容格式,'Test'为你的target的名字
platform :ios,'8.0'
target 'Test' do
# 三方的名字 和 版本号 不填则安装最新的
pod 'MJExtension', '~> 3.0.13'
end
// 添加新的三方,先修改Podfile文件,然后
pod install
// 跟新三方库
pod update
// 删除添加过的三方库,直接修改Podfile文件,删掉 “pod '需要删除三方' ” 一行,然后 pod instal 或者 pod update
二十四、UITableView刷新cell高度,且不重新加载cell
方法很简单,只需要UITableView
调用以下两个方法:
- (void)beginUpdates;
- (void)endUpdates;
这两个方法是必须是成对出现的,调用后会UITableView
的代理会重新走- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath;
方法,加载cell高度,且不会走- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
方法。
关于这两个方法,这里有介绍。
二十五、在 石家庄三秒软件科技有限公司 的感受
在这我也努力过、拼搏过、奋斗过,但是看不到希望。
1、重复的、机械式的工作让人颓废和反感,完全得不到技术上的成长;
2、我不反感加班,我也愿意加班,但是强制加班、把上班时间的工作强行挪到下班时间,不参加就扣100,这就过分了;
3、多次无故晚发工资,没有任何解释,问财务,财务说质量人员没有出绩效表,发不了;问质量,说上个月绩效没有核对出来,马上就出来了,在等一天,每次问都是这样,结果从15号等到了30号,我想问问你把员工当傻子吗?上个月的绩效,从月初就应该开始核对,我不知道公司的业务是多么复杂,需要一个月来核对,领导的解释是这是在李总考验员工,能不能经住金钱的考验,这样才能保证公司壮大了员工不会被金钱购买,心里有句***不知当讲不当讲,这或许是压倒骆驼的最后一根稻草;
4、唯一让我留恋的就是有些领导,他们很好,很喜欢在他们手下干活。
5、感谢公司让我成长,也非常抱歉不能和公司继续成长,江湖甚远,有缘再见。
二十六、人走向前
新公司入职以两周了,越发的感觉辞职的决定是明确的,感悟颇深。
二十七、UIButton文字内容太长,修改省略号位置
只需要修改button的titleLabel的lineBreakMode属性
button.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
二十八、UINavigationItem上的titleView位置问题
先记录下,改天总结
主要是translatesAutoresizingMaskIntoConstraints
属性,
// 不使用AutoLayout机制
self.titleView.translatesAutoresizingMaskIntoConstraints = NO;
二十九、关于Masonry
这篇文章记录的很全,iOS自动布局框架-Masonry详解,学习下。
三十、关于通讯录权限判断
看到一篇文章很好,分析了应该使用那种方式。
AddressBook权限的判断
ABAuthorizationStatus authorizationStatus = ABAddressBookGetAuthorizationStatus();
// 判断授权状态
if (authorizationStatus == kABAuthorizationStatusNotDetermined) {
// 首次访问通讯录
// 请求授权
ABAddressBookRef addressBookRef = ABAddressBookCreate();
__weak typeof(self) weakSelf = self;
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// 授权成功 获取通讯数据
} else {
// 授权失败
}
});
}else if (authorizationStatus == kABAuthorizationStatusDenied || authorizationStatus == kABAuthorizationStatusRestricted) {
// 拒绝授权 或者 未授权,且用户无法更新,如家长控制情况下
}else if (authorizationStatus == kABAuthorizationStatusAuthorized) {
// 同意授权 获取通讯数据
}
三十一、UILAbel
设置了attributedText
后省略号不显示问题
2018-04-27总结
因为设置了attributedText
后,UIlabel
的lineBreakMode = NSLineBreakByTruncatingTail
就会失效。参考如下:
self.contentLabel.attributedText = attributedString;
// 重新设置
self.contentLabel.lineBreakMode = NSLineBreakByTruncatingTail;
亲测有效,同理一些其他因为设置属性字符串导致的问题,也可以如此解决,例如文字居中等。
三十二、iOS开发学习路线
2018-05-15
今天逛简书看到的,感觉很发人深省,记录下。
三十三、用NSLayoutConstrain给控件做了约束以后,如何执行一定的UIView动画呢?
2018-05-15 原文地址
// 在修改了约束以后,只要执行下边的代码,就能做动画效果
[UIView animateWithDuration:2.0 animations:^{
[修改了约束的View layoutIfNeeded];
}];
三十四、淡入淡出切换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];
}
三十五、给APP评分无法连接到App Store解决方法
iOS 11后跳转到App Store评论需要适配,如下
if (@available(iOS 11.0, *)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://itunes.apple.com/cn/app/id%@?mt=8&action=write-review",kStoreAppId]]];
}else{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=%@&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=8",kStoreAppId]]];
}
iOS 10.3以后可以在APP内弹起评分框
if (__IPHONE_10_3) {
// 注意:打开次数一年不能多于3次。
[SKStoreReviewController requestReview];
} else {
[JDMessageView showMessage:@"版本不支持此方法"];
}
三十六、使用Safari打开网页
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.baidu.com"]];
三十七、iOS关于跳转到设置页面
最新,苹果的要求是不可以再使用prefs:root以及App-Prefs:root的接口来做app内部和系统设置的跳转了。
if ([[UIDevice currentDevice].systemVersion floatValue] <= 10.0) {
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
} else {
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
}
三十八、单独隐藏navigationBar底部黑线
1、找出黑线容器方法
#pragma mark -- 找到navigationbar底部黑线
- (UIImageView *)findHairlineImageViewUnder:(UIView *)view {
if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) {
return (UIImageView *)view;
}
for (UIView *subview in view.subviews) {
UIImageView *imageView = [self findHairlineImageViewUnder:subview];
if (imageView) {
return imageView;
}
}
return nil;
}
2、获取该容器
self.navBarHairlineImageView = [self findHairlineImageViewUnder:self.navigationController.navigationBar];
3、隐藏和显示
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navBarHairlineImageView.hidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navBarHairlineImageView.hidden = NO;
}
三十九、UITableViewCell
高亮和选中状态
实现效果:列表中点击高亮效果,松手后取消高亮效果
1、设置cell的selectionStyle,默认就是:UITableViewCellSelectionStyleDefault
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
2、在- tableView:didSelectRowAtIndexPath:方法中:
[tableView deselectRowAtIndexPath:indexPath animated:YES];
四十、关于MVC
模式的理解和优化
概念就不说了,但是对于MVC
中M
层很多人应该和我犯了一样的错误,M
层表示的是业务模型,而不是数据模型,数据模型只是M
中的一小部分,只要能充分利用M
层,把业务逻辑放到M
层中,在通过代理、block
、通知等等方式把处理结果回调给C
层,完全可以避免C
层臃肿。
对于MVC
的瘦身,参考各方文章后,我的一点小小感悟:
1、可以将数据的获取和处理分离到一个单独的类(requestManage
)中,也方便了其他模块的调用;
2、将视图相关的代码移到View
层,封装成View
子类,可以使代码更简洁,复用性更强。
3、借鉴MVVM,构建一个ViewModel类,来处理View层的传值以及一些代理回调方法。
四十一、使用NSProxy
解决NSTimer
循环引用问题(19.04.29)
个人愚见:利用消息转发机制,具体代码如下:
新建JWTimerProxy类,继承自NSProxy。
JWTimerProxy.h
@interface JWTimerProxy : NSProxy
/**
初始化方法
@param obj 代理的对象
@return proxy对象
*/
+ (instancetype)proxyWithObject:(id)obj;
@end
JWTimerProxy.M
@interface JWTimerProxy ()
/** 代理的对象 */
@property (weak, nonatomic) id obj;
@end
@implementation JWTimerProxy
+ (instancetype)proxyWithObject:(id)obj {
JWTimerProxy *proxy = [self alloc];
proxy.obj = obj;
return proxy;
}
/**
这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行
为给定消息提供参数类型信息
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.obj methodSignatureForSelector:sel];
}
/**
* NSInvocation封装了NSMethodSignature,通过invokeWithTarget方法将消息转发给其他对象.这里转发给控制器执行。
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:self.obj];
}
@end
使用方法:
self.timer = [NSTimer timerWithTimeInterval:1 target:[JWTimerProxy proxyWithObject:self] selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
// 定时器触发方法
- (void)timerAction {
NSLog(@"定时器正在运行----------");
}
- (void)dealloc {
NSLog(@"---- dealloc");
if (self.timer) {
// 必须要销毁定时器,否则会崩溃,因为定时器还在执行,但是找不到了触发方法。
[self.timer invalidate];
}
}
未完待续。。。