101.PDF
// 获取沙盒地址
NSArray *pathArr=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *str=pathArr[0];
//创建的文件地址
NSString *path=[str stringByAppendingPathComponent:@”image.pdf”];
NSLog(@”%@”,path);
//创建PDF图形上下文
UIGraphicsBeginPDFContextToFile(path, CGRectZero, NULL);
//给PDF文件添加内容 612 *792
for (int i=0; i<6; i++) {
if (i%2==0) {
//重新创建新的一页
UIGraphicsBeginPDFPage();
}
UIImage *image=[UIImage imageNamed:[NSString stringWithFormat:@”%d_full.jpg”,i]];
//调用这个方法的时候,会把图片绘制到当前页
[image drawInRect:CGRectMake(0, 396*(i%2), 612, 396)];
}
//关闭图形上下文
UIGraphicsEndPDFContext();
}
102.系统声音并震动
// 需要 #import <AudioToolbox/AudioToolbox.h>
AudioServicesPlaySystemSound(1007); //系统的通知声音,只有声音并震动模式才自带震动
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);//震动
103.iOS9 以上50个scheme限制问题
canOpenUrl可以用来判断用户是否安装了某个APP。也许是出于用户隐私的考虑,iOS9上对canOpenUrl做了限制,最多只能对50个scheme做判断。
需要在plist里面声明这些scheme,没有声明的会直接返回NO,所以在iOS9beta刚出来的时候,有些用户无法从微信跳转到第三方app,就是因为已经达到了限制数量,系统直接返回NO,程序以为用户没有安装该APP,就没有去跳转。
解决办法是加白名单,并且尽量减少不必要的canOpenUrl调用,以免超过50个名额的限制。
然后 if (!openUrl(scheme)) then xxx;
104.图片局部拉伸
[[UIImage imageNamed:@""] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
[[UIImage imageNamed:@""] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
105.苹果自带的多行注释(iOS8 later)
Command + Option + / (开启方式sudo /usr/libexec/xpccachectl)
106.热更新会不会被拒
苹果禁止的是“基于反射的热更新“,而不是 “基于沙盒接口的热更新”。而大部分的应用框架(如 React-Native)和游戏引擎(比如 Unity ,Cocos2d-x,白鹭引擎等)都属于后者,所以不在被警告范围内。参见知乎
107.double精度血案
double a = 2.01;
double b = 1.02;
double c = a - b;
DDYInfoLog(@"%@",@(c));
DDYInfoLog(@"%lf",c);
if (c == 0.99)
{
DDYInfoLog(@"double 0.99"); // 不打印
}
if (c == 0.98999999999999976)
{
DDYInfoLog(@"double 0.98999999999999976");// 打印
}
if ([[NSString stringWithFormat:@"%g",c] isEqualToString:@"0.99"])
{
DDYInfoLog(@"string 0.99");// 打印
}
108.查看是否违反热更新
This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes. This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior and/or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.
nm -u libName.a >> xxx.txt, 然后查找dlopen 、 dlsym
109.私有API被拒,查看哪些库用到了这些私有API
grep -r 涉及的api名字 工程所在的文件地址
例如: grep -r UIPeripheralHostView /Users/634778311/Desktop/app_ios
110.傻逼苹果不懂你的流程,让你录制视屏,录制后还被要求录制
要把苹果审核人员当十足的傻逼才行,也就是录制必须从登录注册这里开始到达你真的需要展示给审核人员的地方,同时备注加图片解释
111.沙盒视屏保存到相册
// 姿势1
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(video_path))
{
UISaveVideoAtPathToSavedPhotosAlbum(video_path, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);
}
// 姿势2
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:filePath completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"Save video fail:%@",error);
} else {
NSLog(@"Save video succeed.");
NSFileManager *fileManger = [[NSFileManager alloc] init];
[fileManger removeItemAtURL:filePath error:nil];
}
}];
112.禁用第三方键盘(APPDelegate)
#pragma mark - 禁用第三方键盘
- (BOOL)application:(UIApplication
*)application shouldAllowExtensionPointIdentifier:(NSString
*)extensionPointIdentifier {
return NO;
}
113.获取启动图的名称[项目启动后会对launchImage中的图片进行2次命名]
- (NSString *)fetchLaunchImageName
{
NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
for (NSDictionary* dict in imagesDict) {
if(CGSizeEqualToSize(CGSizeFromString(dict[@"UILaunchImageSize"]),[UIScreen mainScreen].bounds.size))
{
return dict[@"UILaunchImageName"];
}
}
return nil;
}
114.iOS 从网页缓存中获取图片
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSArray *a=[self getImgs];
for (NSString *str in a) {
NSURLCache *sharedCache = (NSURLCache *)[NSURLCache sharedURLCache];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:str]];
__block NSString *wstr = str;
[sharedCache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse * _Nullable cachedResponse) {
NSString *path = [NSString stringWithFormat:@"/Users/whde/Desktop/t/%@", [wstr lastPathComponent]];
[cachedResponse.data writeToFile:path options:NSDataWritingAtomic error:nil];
}];
}
}
// 获取所有图片链接
- (NSArray *)getImgs
{
NSMutableArray *arrImgURL = [[NSMutableArray alloc] init];
for (int i = 0; i < [self nodeCountOfTag:@"img"]; i++) {
NSString *jsString = [NSString stringWithFormat:@"document.getElementsByTagName('img')[%d].src", i];
[arrImgURL addObject:[webView stringByEvaluatingJavaScriptFromString:jsString]];
}
return arrImgURL;
}
// 获取某个标签的结点个数
- (int)nodeCountOfTag:(NSString *)tag
{
NSString *jsString = [NSString stringWithFormat:@"document.getElementsByTagName('%@').length", tag];
int len = [[webView stringByEvaluatingJavaScriptFromString:jsString] intValue];
return len;
}
115.控制器生命周期
1 + (void)initialize;
2 - (instancetype)init;
3 - (void)loadView;
4 - (void)viewDidLoad;
5 -(void)viewWillLayoutSubviews;
6 -(void)viewDidLayoutSubviews;
7 - (void)viewWillAppear:(BOOL)animated;
8 - (void)viewDidAppear:(BOOL)animated
9 - (void)viewWillDisAppear:(BOOL)animated;
10 - (void)viewDidDisAppear:(BOOL)animated;
11 - (void)dealloc;
116.UITableView iOS8 later 蒙层
tableview.backgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"detail.jpg"]];
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVibrancyEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:blurEffect];
tableview.separatorEffect = vibrancyEffect;
117. 视图镂空(遮罩,相机)
holeView.png自定义视图初始化时调用
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupContentView];
}
return self;
}
方法实现
- (void)setupContentView
{
CGMutablePathRef path =CGPathCreateMutable();
CGPathAddRect(path, nil, self.bounds);
CGPathAddRect(path, nil, CGRectMake(DDYSCREENW/2.0-120, 140, 240, 240));
// CGPathAddArc(path, nil, DDYSCREENW/2.0, DDYSCREENH/2.0, 120, 0, 2*M_PI, 0); // 圆形
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path;
shapeLayer.fillRule = kCAFillRuleEvenOdd;
shapeLayer.fillColor = DDYColor(0, 0, 0, 0.5).CGColor;
[self.layer addSublayer:shapeLayer];
}
或者
- (void)setupContentView
{
UIBezierPath *screenPath = [UIBezierPath bezierPathWithRect:self.bounds];
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(DDYSCREENW/2.0-80, 160, 160, 160)];
// [UIBezierPath bezierPathWithArcCenter:]; // 圆形
[screenPath appendPath:rectPath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.fillColor = DDYColor(0, 0, 0, 0.5).CGColor;
shapeLayer.path = screenPath.CGPath;
shapeLayer.fillRule = kCAFillRuleEvenOdd;
[self.layer addSublayer:shapeLayer];
}
118. 去除Pointer is missing a nullability type specifier...警告
NS_ASSUME_NONNULL_BEGIN
// 中间为爆警告的属性或方法定义
NS_ASSUME_NONNULL_END
http://www.jianshu.com/p/83525d8c834f
119.字典转字符串
#pragma mark 字典/数组转json字符串
- (NSString *)ddy_ChangeToJsonString:(id)obj {
if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:obj options:NSJSONWritingPrettyPrinted error:nil];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
return @"";
}
// [data dataUsingEncoding:NSUTF8StringEncoding];
120.SHA3 Keccak-256加密/SHA256加密
#pragma mark SHA256加密 http://www.jianshu.com/p/157d84c2d020
- (NSString *)ddy_SHA256 {
const char *s = [self cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH] = {0};
CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
NSData *out = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
NSString *hash = [out description];
hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
return hash;
}
#pragma mark SHA3 Keccak-256加密 bitsLength:224/256/384/512
- (NSString *)ddy_SHA3:(NSUInteger)bitsLength
{
int bytes = (int)(bitsLength/8);
const char * string = [self cStringUsingEncoding:NSUTF8StringEncoding];
int size=(int)strlen(string);
uint8_t md[bytes];
keccak((uint8_t*)string, size, md, bytes);
NSMutableString *sha3 = [[NSMutableString alloc] initWithCapacity:bitsLength/4];
for(int32_t i=0;i<bytes;i++)
[sha3 appendFormat:@"%02X", md[i]];
return sha3;
}
#pragma mark SHA265加密 后台对key加密
- (NSString *)ddy_SHA256WithKey:(NSString *)key {
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [self cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[HMAC appendFormat:@"%02x", buffer[i]];
}
return HMAC;
}
Sha3 Keccak 256 用到的文件,
keccak.h
#ifndef KECCAK_H
#define KECCAK_H
#include <stdint.h>
#include <string.h>
#ifndef KECCAK_ROUNDS
#define KECCAK_ROUNDS 24
#endif
#ifndef ROTL64
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif
// compute a keccak hash (md) of given byte length from "in"
int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen);
// update the state
void keccakf(uint64_t st[25], int norounds);
#endif
keccak.c
#include "keccak.h"
const uint64_t keccakf_rndc[24] =
{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
const int keccakf_rotc[24] =
{
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
};
const int keccakf_piln[24] =
{
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
};
// update the state with given number of rounds
void keccakf(uint64_t st[25], int rounds)
{
int i, j, round;
uint64_t t, bc[5];
for (round = 0; round < rounds; round++) {
// Theta
for (i = 0; i < 5; i++)
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
for (i = 0; i < 5; i++) {
t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
for (j = 0; j < 25; j += 5)
st[j + i] ^= t;
}
// Rho Pi
t = st[1];
for (i = 0; i < 24; i++) {
j = keccakf_piln[i];
bc[0] = st[j];
st[j] = ROTL64(t, keccakf_rotc[i]);
t = bc[0];
}
// Chi
for (j = 0; j < 25; j += 5) {
for (i = 0; i < 5; i++)
bc[i] = st[j + i];
for (i = 0; i < 5; i++)
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
}
// Iota
st[0] ^= keccakf_rndc[round];
}
}
// compute a keccak hash (md) of given byte length from "in"
int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen)
{
uint64_t st[25];
uint8_t temp[144];
int i, rsiz, rsizw;
rsiz = 200 - 2 * mdlen;
rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (i = 0; i < rsizw; i++)
st[i] ^= ((uint64_t *) in)[i];
keccakf(st, KECCAK_ROUNDS);
}
// last block and padding
memcpy(temp, in, inlen);
temp[inlen++] = 1;
memset(temp + inlen, 0, rsiz - inlen);
temp[rsiz - 1] |= 0x80;
for (i = 0; i < rsizw; i++)
st[i] ^= ((uint64_t *) temp)[i];
keccakf(st, KECCAK_ROUNDS);
memcpy(md, st, mdlen);
return 0;
}
用下面的方式确保Objective-C语言的文件引用
#ifdef __OBJC__
// oc文件
#endif
121.不小心修改了系统SDK造成错误
cd ~/Library/Developer/Xcode/DerivedData/ModuleCache (删除该文件中的缓存即可)
122. VC中监听进入前后台
- (void)addObserverActive
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification object:nil]; //监听是否触发home键挂起程序.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:nil]; //监听是否重新进入程序程序.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
DDYInfoLog( @" Application did become active." );
}
- (void)applicationWillResignActive:(UIApplication *)application
{
DDYInfoLog( @" Application will resign active." );
}
123.系统相机修改按钮
-(UIView *)findView:(UIView *)aView withName:(NSString *)name{
Class cl = [aView class];
NSString *desc = [cl description];
if ([name isEqualToString:desc])
return aView;
for (NSUInteger i = 0; i < [aView.subviews count]; i++)
{
UIView *subView = [aView.subviews objectAtIndex:i];
subView = [self findView:subView withName:name];
if (subView)
return subView;
}
return nil;
}
-(void)addSomeElements:(UIViewController *)viewController{
UIView *PLCameraView=[self findView:viewController.view withName:@"PLCameraView"];
UIView *bottomBar=[self findView:PLCameraView withName:@"PLCropOverlayBottomBar"];
UIImageView *bottomBarImageForSave = [bottomBar.subviews objectAtIndex:0];
UIButton *retakeButton=[bottomBarImageForSave.subviews objectAtIndex:0];
[retakeButton setTitle:@"重拍" forState:UIControlStateNormal]; //左下角按钮
UIButton *useButton=[bottomBarImageForSave.subviews objectAtIndex:1];
[useButton setTitle:@"上传" forState:UIControlStateNormal]; //右下角按钮
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
[self addSomeElements:viewController];
}
123.监听横竖屏旋转,系统通知
UIDeviceOrientationDidChangeNotification
124.cocoapod search 不到绝对存在的库
rm ~/Library/Caches/CocoaPods/search_index.json 执行完重新 search 就行
125.iOS9 后UICollectionView header/footer 吸附悬停
// header
flowLayout.sectionHeadersPinToVisibleBounds = YES;
//footer
flowLayout.sectionFootersPinToVisibleBounds = YES;
126.判断触摸点是否在某个范围上
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return YES;
}
127.scrollView(包括tableView,TextView,collectionView)滚动退键盘
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
128.tableView/CollectionView自适应高度(常用于嵌套)
[_collectionView performBatchUpdates:^{
// 更新collection的约束一定要写在reload完成之前,否则会导致crash
} completion:^(BOOL finished) {
// 注意防止循环调用该方法
CGFloat h = _collectionView.collectionViewLayout.collectionViewContentSize.height;
// 不能超过某个临界值,这里不能超过屏幕高度
_collectionView.ddy_h = h > DDYSCREENH ? DDYSCREENH : h;
}];
129.延迟执行之坑
- (void)fireBlock:(dispatch_block_t)block {
block();
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(fireBlock:) withObject:^{
NSLog(@"hello world");
} afterDelay:0.3];
});
上面的代码片段原有的目的是异步延迟0.3秒后输出Hello world。但是运行之后发现不会输出Hello world。
原因是:非主线程的NSRunLoop默认没有开启,而
-performSelector:withObject:afterDelay:;函数内部是通过NSTimer定时器实现,在NSRunLoop没有开启的情况下,NSTimer不会得到正常运行。
130.CABaseAnimation 按home键后台后切回前台动画停止解决
animation.removedOnCompletion = NO;
131.监听挂起和重新进入程序
#pragma mark 监听挂起和重新进入程序
#pragma mark 添加监听
- (void)addObserverActive
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification object:nil]; //监听home键挂起.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:nil]; //监听重新进入程序.
}
#pragma mark 进入前台
- (void)applicationDidBecomeActive:(UIApplication *)application
{
DDYInfoLog( @"[LC_UIApplication] Application did become active." );
_canInvite = YES;
}
#pragma mark 挂起程序
- (void)applicationWillResignActive:(UIApplication *)application
{
DDYInfoLog( @"[LC_UIApplication] Application will resign active." );
_canInvite = NO;
}
131.AVFoundation录制视屏返回后tabbar向下移动了20像素的解决
// 对应控制器设置
_cameraVC.modalPresentationStyle=UIModalPresentationOverFullScreen;
132. 音频播放中断后恢复播放
iOS8 前
遵循 AVAudioPlayerDelegate
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
// 音频会话被中断。玩家将在这里暂停
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)playerwithFlags:(NSUInteger)flags {
if(flags== AVAudioSessionInterruptionFlags_ShouldResume&&player!= nil) {
[player play];
}
}
当中断发生时,AVAudioPlayer 实例的 audioPlayerBeginInterruption: delegate 方法会被调用。这时,你的音频 session 已经被打断了。如果是来电,用户只能听到铃声。当通话结束或用户拒绝来电后,AVAudioPlayer 类的 udioPlayerEndInterruption:withFlags: delegate 方法会被调用。如果参数 withFlags 包含AVAudioSessionInterruptionFlags_ShouldResume 标识,你就可以用 AVAudioPlayer 的播放实例来立即恢复播放音频。
iOS8之后
// 注册打断通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVAudioSessionInterruptionNotification:) name:AVAudioSessionInterruptionNotification object:session];
然后实现方法
// 接收通知方法
- (void)AVAudioSessionInterruptionNotification: (NSNotification *)notificaiton {
NSLog(@"%@", notificaiton.userInfo);
AVAudioSessionInterruptionType type = [notificaiton.userInfo[AVAudioSessionInterruptionTypeKey] intValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
[self.player pause];
} else {
[self.player play];
}
}
133.NSTimer block形式(iOS 10 later)兼容iOS9 before
写个分类
NSTimer+DDYExtension.h
#import <Foundation/Foundation.h>
@interface NSTimer (DDYExtension)
+ (NSTimer *)ddy_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
@end
NSTimer+DDYExtension.m
#import "NSTimer+DDYExtension.h"
#import <objc/runtime.h>
@implementation NSTimer (DDYExtension)
+ (void)load {
// get orignal method
Method orignalMethod = class_getInstanceMethod(self, @selector(scheduledTimerWithTimeInterval:repeats:block:));
// get my method
Method myMethod = class_getInstanceMethod(self, @selector(ddy_scheduledTimerWithTimeInterval:repeats:block:));
// change
method_exchangeImplementations(orignalMethod, myMethod);
}
+ (NSTimer *)ddy_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block {
if (@available(iOS 10.0, *)) {
return [self scheduledTimerWithTimeInterval:interval repeats:repeats block:^(NSTimer * _Nonnull timer) { block(timer); }];
} else {
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(ddy_timerInvoke:) userInfo:[block copy] repeats:repeats];
}
}
+ (void)ddy_timerInvoke:(NSTimer *)timer {
void (^block)(NSTimer *timer) = timer.userInfo;
if (block) {
block(timer);
}
}
@end
然后正常调用+ scheduledTimerWithTimeInterval:repeats:block: 时内部实现了交换方法
或者用接口+ ddy_scheduledTimerWithTimeInterval:repeats:block:
134.label多行显示和Masonry自动布局
label.preferredMaxLayoutWidth = (WidthScreen - margin * 2);
[label setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
label.numberOfLines = 0;
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.mas_equalTo(self);
// 自适应label多行显示时,无需设置label高度
// make.height.mas_equalTo(40.0);
}];
135. 替换系统音量提示
- 激活 AudioSession
- 创建一个 MPVolumeView,并将其添加到当前可见的视图层级当中,同时将其 frame 设置到不可见区域
- 监听音量按钮触发事件,改变音量提示(监听方式有两种:KVO、NSNotification)
- KVO 在音量调节至最大/最小时,这个时候再调大/调小音量,由于 outputVolume 的值不变,所以不会触发 KVO,也就无法展示自定义音量视图,
- 监听系统私有(未公开的)通知,名字是 AVSystemController_SystemVolumeDidChangeNotification,这个监听不会受到最大/最小音量时,调大/调小音量的影响,只要音量键按下,始终都会触发。但是这个通知由于是私有的,可能存在被拒风险,而且将来系统版本该通知名字发生改变,由于是硬编码而不像其它系统通知使用的是常量,会导致监听不到的问题。
// KVO
- (void)dealloc {
[[AVAudioSession sharedInstance] removeObserver:self
forKeyPath:NSStringFromSelector(@selector(outputVolume))];
}
- (void)addObserver {
[[AVAudioSession sharedInstance] addObserver:self
forKeyPath:NSStringFromSelector(@selector(outputVolume))
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([change isKindOfClass:[NSDictionary class]]) {
NSNumber *volumeNum = change[@"new"];
if (volumeNum) {
[self volumeDidChange:[volumeNum floatValue]];
}
}
}
- (void)volumeDidChange:(CGFloat)volume {
// 显示自定义音量提示
}
// Notification
static NSNotificationName const kSystemVolumeDidChangeNotification = @"AVSystemController_SystemVolumeDidChangeNotification";
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)addObserver {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(volumeDidChange:)
name:kSystemVolumeDidChangeNotification
object:nil];
}
- (void)volumeDidChange:(NSNotification *)notification {
NSString *category = notification.userInfo[@"AVSystemController_AudioCategoryNotificationParameter"];
NSString *changeReason = notification.userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
if (![category isEqualToString:@"Audio/Video"] || ![changeReason isEqualToString:@"ExplicitVolumeChange"]) {
return;
}
CGFloat volume = [[notification userInfo][@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
// 显示自定义音量提示
}
136.可变字典几种初始化方式
// 第 1 种
NSMutableDictionary *dict1 = [[NSMutableDictionary alloc] init];
// 第 2 种
NSMutableDictionary *dict2 = [NSMutableDictionary new];
// 第 3 种
NSMutableDictionary *dict3 = [NSMutableDictionary dictionary];
// 第 4 种
NSMutableDictionary *dict4 = [NSMutableDictionary dictionaryWithCapacity:10];
// 第 5 种
NSMutableDictionary *dict5 = @{}.mutableCopy;
第 1 种就是我们常见初始化一个 NSObject
对象的写法,其中 alloc
为 NSObject
的类方法,它用于创建(分配内存)并返回指定类一个的新对象,而 init
为 NSObject
的实例方法,一般由子类重新实现,用于初始化一个刚创建 (allocated) 的对象。
第 2 种写法,对于 NSObject
的 new
方法,苹果文档 是这么说的:Allocates a new instance of the receiving class, sends it an init message, and returns the initialized object. 因此,它就是 alloc
和 init
方法的组合,与第 1 种写法是等价的。
第 3 种,文档描述:Creates and returns an empty dictionary. 它也是一种快速的初始化写法。在 ARC 下,它与 [[NSMutableDictionary alloc] init]
是相同的;但在 MRC 手动管理内存时,使用 [[NSMutableDictionary alloc] init]
创建并初始化对象,后续我们需要手动调用 release
方法释放,而 [NSMutableDictionary dictionary]
相当于 [[[NSMutableDictionary alloc] init] autorelease]
,区别在于你不用再调用 release 方法去释放它了。
第 4 种,相当于调用 [[NSMutableDictionary alloc] initWithCapacity:10]
方法,它用于创建一个可变字典对象并初始化分配给它足够的内存空间以存储指定长度(10)个内容对象,且当动态添加的数据超过初始化时指定的长度,也会自动增加分配新的内存,所以如果你可以确定要用的可变字典大致的存储个数,推荐使用这种方式。
对于第 5 种,我们知道 @{}
字面值相当于创建了一个不可变的 NSDictionary
空对象,然后调 NSObject 的 mutableCopy
拷贝成一个新的可变对象赋给 dict5
。
137 数组筛选
读入了一个array1,然后想把array2中符合array1中内容的元素过滤出来。
傻瓜式做法就是两个for循环,一个一个进行比较,效率极低。
其实一个循环甚至无循环就可以搞定了,那就需要用NSPredicate
一个循环
NSMutableArray *filterArray = [NSMutableArray arrayWithObjects:@"pict", @"blackrain", @"ip", nil];
NSMutableArray *contentArray = [NSMutableArray arrayWithObjects:@"I am a picture.", @"I am a guy", @"I am gagaga", @"ipad", @"iphone", nil];
for (NSString *tempFilter in filterArray) {
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", tempFilter];
NSArray *tempResultArray = [contentArray filteredArrayUsingPredicate:filterPredicate];
NSLog(@"result:%@",tempResultArray);
}
无需循环 过滤出来不包含filterArray中的所有item的元素。
NSMutableArray *filterArray = [NSMutableArray arrayWithObjects:@"abc1", @"abc2", nil];
NSMutableArray *contentArray = [NSMutableArray arrayWithObjects:@"a1", @"abc1", @"abc4", @"abc2", nil];
NSPredicate *thePredicate = [NSPredicate predicateWithFormat:@"NOT (SELF in %@)", filterArray];
[contentArray filterUsingPredicate:thePredicate];
138 生成文件路径下文件集合列表
NSString *defaultPath = [[NSBundle mainBundle] resourcePath];
NSArray *contentsDict = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:defaultPath error:nil];
139 CoreMotion框架测气压和相对高度
- (CMAltimeter *)altimeter {
if (!_altimeter) {
_altimeter = [[CMAltimeter alloc]init];
}
return _altimeter;
}
// 是否支持气压计
if ([CMAltimeter isRelativeAltitudeAvailable]) {
//开始监测
[self.altimeter startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAltitudeData * _Nullable altitudeData, NSError * _Nullable error) {
// 相对高度
altitudeData.relativeAltitude;
// 气压
altitudeData.pressure;
}];
}
# 140 宏定义中## 和 #
在宏定义define中经常看到两个字符串##和#,这里把它的用法做一下说明:
\#\#是一个连接符号,用于把参数连在一起
例如:
\#define FOO(arg) my\#\#arg
则 FOO(abc) 相当于 myabc
\#是“字符串化”的意思。出现在宏定义中的\#是把跟在后面的参数转换成一个字符串
例如:
\#define STRCPY(dst, src) strcpy(dst, \#src) 则
STRCPY(buff, abc) 相当于 strcpy(buff, "abc")
另外,如果##后的参数本身也是一个宏的话,\#\#会阻止这个宏的展开
# 141 3DTouch Peak预览
// 1 遵循代理
UIViewControllerPreviewingDelegate
// 2 注册按压要预览的View,比如某个cell
[self registerForPreviewingWithDelegate:self sourceView:cell];
// 实现代理方法
pragma mark Peek预览
- (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location
{
// 获取按压的cell所在行,[previewingContext sourceView]就是按压的那个视图
NSIndexPath indexPath = [_tableView indexPathForCell:(UITableViewCell )[previewingContext sourceView]];
if (indexPath.section==0 && indexPath.row==0) {
// 设定预览的界面
UIViewController *childVC = [NSClassFromString(self.dataArray[indexPath.row]) vc];
childVC.preferredContentSize = CGSizeMake(0.0f, 500.0f);
// 调整不被虚化的范围,按压的那个cell不被虚化(轻轻按压时周边会被虚化,再少用力展示预览,再加力跳页至设定界面)
CGRect rect = CGRectMake(0, 0, DDYSCREENW,40);
previewingContext.sourceRect = rect;
// 返回预览界面
return childVC;
}
return nil;
}
pragma mark Pop用力按则进入
- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
[self showViewController:viewControllerToCommit sender:self];
}
// 4 previewActionItems getter
-
(NSArray<id<UIPreviewActionItem>> *)previewActionItems {
UIPreviewAction *action1 = [UIPreviewAction actionWithTitle:@"Aciton1" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"Aciton1");
}];
UIPreviewAction *action2 = [UIPreviewAction actionWithTitle:@"Aciton2" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"Aciton2");
}];
UIPreviewAction *action3 = [UIPreviewAction actionWithTitle:@"Aciton3" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
NSLog(@"Aciton3");
}];
// 先展示分组,点击分组进入分组内的actions
UIPreviewActionGroup *group1 = [UIPreviewActionGroup actionGroupWithTitle:@"1" style:UIPreviewActionStyleDefault actions:@[action1,action2]];
UIPreviewActionGroup *group2 = [UIPreviewActionGroup actionGroupWithTitle:@"2" style:UIPreviewActionStyleDestructive actions:@[action1,action3]];
UIPreviewActionGroup *group3 = [UIPreviewActionGroup actionGroupWithTitle:@"3" style:UIPreviewActionStyleSelected actions:@[action2,action3]];NSArray *actions = @[group1, group2, group3];
// 如果不分组直接填入action NSArray *actions = @[action1, action2, action3];
return actions;
}
# 142 自动布局时获取NavigationBar或者TabBar区域
topLayoutGuide和bottomLayoutGuide
masonry也支持
make.top.equalTo(self.mas_topLayoutGuide);
make.bottom.equalTo(self.mas_bottomLayoutGuide);
# 143 父视图 touchesBegan只让某个view响应
//touch的响应方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGPoint point = [[touches anyObject] locationInView:self.view];
//convert point to the white layer's coordinates拿到在self.view上但同时在whiteView上的点,下面的同这里一样,不一一解释了
point = [whiteView.layer convertPoint:point fromLayer:self.view.layer]; //get layer using containsPoint:
if ([whiteView.layer containsPoint:point]) {
//convert point to blueLayer’s coordinates
point = [blueLayer convertPoint:point fromLayer:whiteView.layer];
if ([blueLayer containsPoint:point])
{
[[[UIAlertView alloc] initWithTitle:@"Inside Blue Layer"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
else
{
[[[UIAlertView alloc] initWithTitle:@"Inside White Layer"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles: nil]show];
}
}
//----------华丽丽的分割线,从这里开始是我写的点击的方法,相对来说比上面使用起来更方便点
// CGPoint point=[[touches anyObject]locationInView:self.view];
// CALayer *layer=[whiteView.layer hitTest:point];
// if (layer==whiteView.layer) {
// UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:@"" message:@"Inside white layer" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
// [alertView show];
// }
// else if (layer==blueLayer){
// UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:@"" message:@"Inside blue layer" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
// [alertView show];
// }
}
<!-- http://www.cnblogs.com/Camier-myNiuer/p/5589215.html -->
<!-- https://github.com/awesome-tips/iOS-Tips -->
网友评论