iOS日常问题总结

总结一些遇到的问题。
日常工作中用到的一些方法总结,有很简单介绍,可能也有错误,如果您看到了希望可以告诉我,会不间断更新。

一、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];// 动画// 动画
修改UITableView头视图的高度.gif

十三、iOS中获取各种文件的目录路径的方法

记录下 IOS中获取各种文件的目录路径的方法很不错。

十四、外包公司的感受

很累,貌似很充实,但是自己成长的太慢,每天都是重复的工作内容,简单无趣,再下去要废掉了,决定逃离。

十五、音乐的后台播放、控制、信息的展示和处理中断事件

1、打开后台设置
TARGETS —> Capabilities —> Background Modes —> 勾选 Audio,AirPlay。。。这项。如图:

后台设置.png

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分区区头停留在顶部。

动图不太好看,实际效果还可以。


gif5新文件.gif

十八、 获取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

在开发过程中会有这种情况:从vc1push到vc2,再从vc2push到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后,UIlabellineBreakMode = 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模式的理解和优化

概念就不说了,但是对于MVCM层很多人应该和我犯了一样的错误,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];
    }
}

未完待续。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容