iOS 开发记录

打印方法名

NSLog(@"%s",__FUNCTION__);
NSLog(@"%@",NSStringFromSelector(_cmd));

类、方法判断

// 用于判断是否包含某个类方法
respondsToSelector:(SEL)                      

conformsToProtocol:(Protocol *)          
 // 判定某个类     
isKindOfClass:(__unsafe_unretained Class)    

isMemberOfClass:(__unsafe_unretained Class)   

// 获取某个类的类名字符串
NSStringFromClass([UITextView class]);

// 通过字符串获取某个类
NSClassFromString(@"UITextView");

emoji表情转换

// 表情转UTF8
[contentstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

// UTF8转表情
[content stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

数组和字典与NSData互转

NSArray * arr1 = [[NSArray alloc]initWithObjects:@"0",@"5",nil];

// NSArray->NSData(转字典也使用该方法)
NSData  * data = [NSKeyedArchiver archivedDataWithRootObject:arr1];

// NSData->NSArray(转字典也使用该方法)
NSArray * arr2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];

字典、JSON互转

// json格式字符串转字典:
+ (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString
{
    if (jsonString == nil) {
        return nil;
    }

    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

    if(err) {
        NSLog(@"json解析失败:%@",err);
        return nil;
    }
    return dic;
}

// 字典转json格式字符串:
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
    NSError *parseError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

自定义结构体(类似于CGSizeMake)

/**
 *  分页
 */
struct LBPage{
    NSInteger page;
    NSInteger number;
};
typedef struct LBPage LBPage;

/**
 *  创建分页结构体
 */
CG_INLINE LBPage
LBPageMake(NSInteger page, NSInteger number)
{
  LBPage p;
  p.page = page; 
  p.number = number; 
  return p;
}

实现多选参数(如UIAlertView初始化)

// 定义方法:
- (void)buttonTitles:(nullable NSString *)titles,...;

// 实现方法
- (void)buttonTitles:(nullableNSString *)titles,...{
  NSString *other = nil;
  va_list args; //用于指向第一个参数
  NSMutableArray *fruits = [NSMutableArray array];
  if(titles){
    [fruits addObject:titles];
    // 对args进行初始化,让它指向可变参数表里面的第一个参数
    va_start(args, titles);
    // 获取指定类型的值
    while((other = va_arg(args, NSString*))){
      [fruits addObject:other];
    }
    va_end(args);//将args关闭
  }
  NSLog(@"%@", fruits);
}

// 方法调用
[self buttonTitles:@“1”,@“2”,nil];

判断当前设备是模拟器还是真机

#if TARGET_IPHONE_SIMULATOR // 模拟器
#define SIMULATOR_TEST 0
#else
#define SIMULATOR_TEST 1
#endif

调试打印

#ifdef DEBUG
  #define DLog(fmt, ...) NSLog((@"<%s : %d> %s  " fmt), [[[NSString stringWithUTF8String:__FILE__] lastPathComponent]   UTF8String], __LINE__, __PRETTY_FUNCTION__,  ##__VA_ARGS__);
  #define NSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
  #define DLog(...) /* */
  #define NSLog(FORMAT, ...) nil
#endif

枚举定义

typedef NS_ENUM(NSInteger,YMDatePickerMode){
    YMDatePickerModeTime,
    YMDatePickerModeDate,
    YMDatePickerDateAndTime,
    YMDatePickerModeCountDownTimer
};

typedef enum {
    YMDateUnitYear,
    YMDateUnitMonth,
    YMDateUnitDay
}YMDateUnit;

统音量控制(通过UISlider直接控制系统音量)

// 导入头文件
#import <MediaPlayer/MediaPlayer.h> 

// 控制系统音量只需要对slider进行控制即可控制
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
[volumeView sizeToFit];
NSLog(@"%@",volumeView.subviews);

// 将系统的音量调整视图赋给自定义的UISlider,实现拖动自定义UISlider直接改变系统音量
UISlider * slider = [[UISlider alloc]init];
slider.backgroundColor = [UIColor blueColor];
for (UIControl *view in volumeView.subviews) {
     if ([view.superclass isSubclassOfClass:[UISlider class]]) {
     slider = (UISlider *)view;
     }
}

slider.autoresizesSubviews = NO;
slider.autoresizingMask = UIViewAutoresizingNone;
slider.hidden = YES;
NSLog(@"_backVolumeView.value = %lf",_backVolumeView.value);

获取系统音量

#import <AVFoundation/AVFoundation.h> 监听系统音量

// 创建监听
NSError *error;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(volumeChanged:)
                                             name:@"AVSystemController_SystemVolumeDidChangeNotification"
                                           object:nil];

// 开始接收远程控制事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

// 结束接收远程控制事件
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];

// 获取音量
- (void)volumeChanged:(NSNotification *)notification
{
     NSLog(@"volume = %@",notification.userInfo);
}

截取当前屏幕

// currentView 当前的view  创建一个基于位图的图形上下文并指定大小为
UIGraphicsBeginImageContext(self.view.bounds.size);

// renderInContext呈现接受者及其子范围到指定的上下文
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];

// 返回一个基于当前图形上下文的图片
_currentScreen = UIGraphicsGetImageFromCurrentImageContext();

// 移除栈顶的基于当前位图的图形上下文
UIGraphicsEndImageContext();

// 然后将该图片保存到图片图
UIImageWriteToSavedPhotosAlbum(_currentScreen, nil, nil, nil);

截取视频帧图片

- (void)thumbnailImageForVideo:(NSString *)videoPath atTime:(NSTimeInterval)time success:(void(^)(UIImage *image))success
{
    dispatch_queue_t   globQueue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globQueue, ^{
        // 通过视频路径获取AVURLAsset
        NSURL * vedioURL = [NSURL URLWithString:videoPath];
        AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:vedioURL options:nil];
        
        NSParameterAssert(asset);
        AVAssetImageGenerator *assetImageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
        assetImageGenerator.appliesPreferredTrackTransform = YES;
        assetImageGenerator.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels;
        
        CGImageRef thumbnailImageRef = NULL;
        CFTimeInterval thumbnailImageTime = time;
        NSError *thumbnailImageGenerationError = nil;
        thumbnailImageRef = [assetImageGenerator copyCGImageAtTime:CMTimeMake(thumbnailImageTime, 60) actualTime:NULL error:&thumbnailImageGenerationError];
        
        if (!thumbnailImageRef) {
            NSLog(@"thumbnailImageGenerationError %@", thumbnailImageGenerationError);
        }
        UIImage *thumbnailImage = thumbnailImageRef ? [[UIImage alloc] initWithCGImage:thumbnailImageRef] : nil;
        dispatch_async(dispatch_get_main_queue(), ^{
            success(thumbnailImage);
        });
    });
}

获取当前设备是iPad还是iPhone

// 判断是否为pad
UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad

// 获取当前iPhone型号
- (NSString*)deviceVersion
{
    // 需要#import "sys/utsname.h"
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];

    //CLog(@"%@",deviceString);
    if ([deviceString isEqualToString:@"iPhone1,1"])    return @"iPhone 1G";
    if ([deviceString isEqualToString:@"iPhone1,2"])    return @"iPhone 3G";
    if ([deviceString isEqualToString:@"iPhone2,1"])    return @"iPhone 3GS";
    if ([deviceString isEqualToString:@"iPhone3,1"])    return @"iPhone 4";
    if ([deviceString isEqualToString:@"iPhone3,2"])    return @"Verizon iPhone 4";
    if ([deviceString isEqualToString:@"iPhone4,1"])    return @"iPhone 4S";
    if ([deviceString isEqualToString:@"iPhone5,2"])    return @"iPhone 5";
    if ([deviceString isEqualToString:@"iPhone6,2"])    return @"iPhone 5S";
    if ([deviceString isEqualToString:@"iPhone7,2"])    return @"iPhone 6";
    if ([deviceString isEqualToString:@"iPod1,1"])      return @"iPod Touch 1G";
    if ([deviceString isEqualToString:@"iPod2,1"])      return @"iPod Touch 2G";
    if ([deviceString isEqualToString:@"iPod3,1"])      return @"iPod Touch 3G";
    if ([deviceString isEqualToString:@"iPod4,1"])      return @"iPod Touch 4G";
    if ([deviceString isEqualToString:@"iPad1,1"])      return @"iPad";
    if ([deviceString isEqualToString:@"iPad2,1"])      return @"iPad 2 (WiFi)";
    if ([deviceString isEqualToString:@"iPad2,2"])      return @"iPad 2 (GSM)";
    if ([deviceString isEqualToString:@"iPad2,3"])      return @"iPad 2 (CDMA)";
    if ([deviceString isEqualToString:@"iPad3,4"])      return @"iPad 4 (WiFi)";
    if ([deviceString isEqualToString:@"i386"])         return @"Simulator";
    if ([deviceString isEqualToString:@"x86_64"])       return @"Simulator";

    //CLog(@"NOTE: Unknown device type: %@", deviceString);

    return deviceString;
}

block

// 定义
typedef void (^MyBlock)();
@property (nonatomic, copy) MyBlock buttonAction;

// 定义
@property (nonatomic, copy)void(^dimissCompleteBlock)(void);

// 定义
- (void)checkBlockResult:(void(^)(NSInteger))result;

// block可抽出来:
// 定义
- (void)didFinish:(void (^)(void))didFinish;
// 调用
[self didFinish:didFinishPlay]]
// 实现
void (^didFinishPlay)(void) = ^(){
  
};

判断字符串中是否有表情

- (BOOL)stringContainsEmoji:(NSString *)string
{
    __block BOOL returnValue = NO;
    void (^didFinishPlay)() = ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop){
        const unichar hs = [substring characterAtIndex:0];
        // surrogate pair
        if (0xd800 <= hs && hs <= 0xdbff) {
            if (substring.length > 1) {
                const unichar ls = [substring characterAtIndex:1];
                const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
                if (0x1d000 <= uc && uc <= 0x1f77f) {
                    returnValue = YES;
                }
            }
        } else if (substring.length > 1) {
            const unichar ls = [substring characterAtIndex:1];
            if (ls == 0x20e3) {
                returnValue = YES;
            }
        } else {
            // non surrogate
            if (0x2100 <= hs && hs <= 0x27ff) {
                returnValue = YES;
            } else if (0x2B05 <= hs && hs <= 0x2b07) {
                returnValue = YES;
            } else if (0x2934 <= hs && hs <= 0x2935) {
                returnValue = YES;
            } else if (0x3297 <= hs && hs <= 0x3299) {
                returnValue = YES;
            } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {
                returnValue = YES;
            }
        }
    };

    [string enumerateSubstringsInRange:NSMakeRange(0, [string length])
                               options:NSStringEnumerationByComposedCharacterSequences
                            usingBlock:didFinishPlay];
    
    return returnValue;
}

CALayout图片平铺(可用于AVPlayer的预览图设置)

self.view.layer.contents = (__bridge id)[UIImage imageNamed:@"demo"].CGImage;

屏幕旋转

// 允许屏幕自动旋转
// 该方法在只有UIVIewController的状况下 会被调用
// 是否允许自转
- (BOOL)shouldAutorotate
{
    return YES;
}

// 当将UIViewController加载到UINavigationController上时 - (BOOL)shouldAutorotate不被调用(解决方案查看下一个方法)
// 在工程需要中需要调用的控制器中加入下列代码(LoadingViewController)
@implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate {
     return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
     return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
     return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
@end

// 屏幕旋转
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation ==  UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation ==  UIInterfaceOrientationLandscapeRight );
}

// 屏幕旋转
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

// 屏幕支持的方向
-(NSUInteger)supportedInterfaceOrientations
{
     return UIInterfaceOrientationMaskLandscape;
}

// 手动旋转屏幕(电池栏也会跟着旋转)
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
     SEL selector = NSSelectorFromString(@"setOrientation:");
     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
     [invocation setSelector:selector];
     [invocation setTarget:[UIDevice currentDevice]];
     int val = (int)UIInterfaceOrientationLandscapeRight;
     [invocation setArgument:&val atIndex:2];
     [invocation invoke];
}

// 默认旋转方向
1.打开项目Supporting Files文件夹中的info.plist
2.找到Supported interface orientations
3.改变item的顺序,如果right在前面 left在后面,那么屏幕默认以left方向为准

解决控制器dismiss时,被覆盖的界面自动旋转

// 1、在 - (void)viewDidLoad 或者 - (void)viewWillAppear:(BOOL)animated中加入以下代码
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
        //orientation
        [[UIApplication sharedApplication]
         setStatusBarOrientation:UIInterfaceOrientationPortrait
         animated:NO];

        [[UIDevice currentDevice]
         setValue:[NSNumber
                   numberWithInteger:UIInterfaceOrientationPortrait]
         forKey:@"orientation"];
}

// 2、重写以下方法
- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    // 返回所需要的方向,可以是多个方向
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    // 返回所需要的方向,可以是多个方向
    returnUIInterfaceOrientationPortrait;
}

获取启动页和设置启动页延时(LaunchView)

UIView * launchView =  [[NSBundle mainBundle] loadNibNamed:@"LaunchScreen" owner:nil options:nil][0];
launchView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
[self.window addSubview:launchView];
[NSThread sleepForTimeInterval:0.4];

点击推送通知进入APP 显示通知内容

// 当点击推送通知时 下面方法中的launchOptions会有数据
// 因此可以由此去判断当前是否是点击推送进入的APP
// 可以设置全局变量来记录
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

// 此时必然会调用下面的方法
// 可以获取userInfo中的内容
// 字段分别是 alert badge sound
// 通过userInfo[@"aps"][@"alert”]获取
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo

iOS 7.0 用该方法
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

// 判断是否通过推送界面进入
NSDictionary* remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotification)
{
     _isLaunchedByNotification = 1;
}

获取版本号Version

[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

字体大小自动调整

// 按钮、标签都可以进行自动调整
button.titleLabel.adjustsFontSizeToFitWidth = YES;
// 调增范围(字体大小最多缩小到某个比率)
button.titleLabel.minimumScaleFactor = 0.5;

获取runtime中的私有属性

获取下图中红框中的属性名


私有属性
// 1.申明一个类目并写好实现逻辑
@interface NSObject (RuntimeUntility)
- (id)getPrivateProperty:(NSString *)propertyName;
@end

@implementation NSObject (RuntimeUntility)
- (id)getPrivateProperty:(NSString *)propertyName
{
    Ivar iVar = class_getInstanceVariable([self class], [propertyName UTF8String]);
    
    if (iVar == nil) {
        iVar = class_getInstanceVariable([self class], [[NSString stringWithFormat:@"_%@",propertyName] UTF8String]);
    }
    
    id propertyVal = object_getIvar(self, iVar);
    return propertyVal;
}
@end


// 2.进行调用,传入参数填写要获取的属性的名称,如果runtime中有有下滑线(如_bqmmExtra)要对其删除
id bqmmExtra = [messageContent getPrivateProperty:@"bqmmExtra"];

pragma mark - 安装包获

1.打开活动监视器
2.找到storedownload
3.点击图标



4.复制一下路径,到指定路径下获取包,在安装之前把他拖出来


处理本地缓存

1.搜索路径:/Users/huangyuzhou(用户名)/Library/Developer/Xcode
(注:Library资源库是隐藏的文件夹)
2.删除里面文件即可(里面有DerivedData和Snapshots两个文件夹,)

自动内存管理 手动内存管理混编

可以在build phases 下的compile sources 中找到不想自动管理的.m文件 ,给它加compiler flags 为 -fno-objc-arc

Xcode真机调试包

Xcode版本不支持高版本手机时,可拖入高版本包,从而不需要更新Xcode,而进行真机测试。路径:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

正则表达式

// 含有字母和数字的6~16位字符串
NSString * passwordRegex = @"^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$";

模糊效果

//加模糊效果,image是图片,blur是模糊度
- (UIImage *)blurryImage:(UIImage *)image withBlurLevel:(CGFloat)blur {
    //模糊度,
    if ((blur < 0.1f) || (blur > 2.0f)) {
        blur = 0.5f;
    }
    
    //boxSize必须大于0
    int boxSize = (int)(blur * 100);
    boxSize -= (boxSize % 2) + 1;
    NSLog(@"boxSize:%i",boxSize);
    //图像处理
    CGImageRef img = image.CGImage;
    //需要引入
    /*
     This document describes the Accelerate Framework, which contains C APIs for vector and matrix math, digital signal processing, large number handling, and image processing.
     本文档介绍了Accelerate Framework,其中包含C语言应用程序接口(API)的向量和矩阵数学,数字信号处理,大量处理和图像处理。
     */
    
    //图像缓存,输入缓存,输出缓存
    vImage_Buffer inBuffer, outBuffer;
    vImage_Error error;
    //像素缓存
    void *pixelBuffer;
    
    //数据源提供者,Defines an opaque type that supplies Quartz with data.
    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    // provider’s data.
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
    
    //宽,高,字节/行,data
    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);
    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
    
    //像数缓存,字节行*图片高
    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    
    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    
    // 第三个中间的缓存区,抗锯齿的效果
    void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    vImage_Buffer outBuffer2;
    outBuffer2.data = pixelBuffer2;
    outBuffer2.width = CGImageGetWidth(img);
    outBuffer2.height = CGImageGetHeight(img);
    outBuffer2.rowBytes = CGImageGetBytesPerRow(img);
    
    //Convolves a region of interest within an ARGB8888 source image by an implicit M x N kernel that has the effect of a box filter.
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    
    
    if (error) {
        NSLog(@"error from convolution %ld", error);
    }
    
    //    NSLog(@"字节组成部分:%zu",CGImageGetBitsPerComponent(img));
    //颜色空间DeviceRGB
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    //用图片创建上下文,CGImageGetBitsPerComponent(img),7,8
    CGContextRef ctx = CGBitmapContextCreate(
                                             outBuffer.data,
                                             outBuffer.width,
                                             outBuffer.height,
                                             8,
                                             outBuffer.rowBytes,
                                             colorSpace,
                                             CGImageGetBitmapInfo(image.CGImage));
    
    //根据上下文,处理过的图片,重新组件
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
    
    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
    
    free(pixelBuffer);
    free(pixelBuffer2);
    CFRelease(inBitmapData);
    
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(imageRef);
    
    return returnImage;
}

Gif图的两种播放方式

#import "ViewController.h"
#import <WebKit/WebKit.h>
#import <ImageIO/ImageIO.h>

/*
 经过测试,从加载速度上来说,通过UIImageView类别加载的方式更加快速,UIWebView的方式加载时间会稍长,但是从性能上来比较,WebView的方式性能更优,播放的gif动态图更加流畅。
 在开发中,可以根据需求,适当选择,例如虽然WebView加载的方式性能更好,但是在许多情况下,原生的UIImageView能够更加自由的让开发者进行扩展。
 */

/*
  UIImage+GitImageView
*/
@interface UIImageView (GitImageView)
@end

@implementation UIImageView (GitImageView)

// 为UIImageView添加一个设置gif图内容的方法:
-(void)yh_setImage:(NSURL *)imageUrl{
    __weak id __self = self;
    [self getGifImageWithUrk:imageUrl returnData:^(NSArray<UIImage *> *imageArray, NSArray<NSNumber *> *timeArray, CGFloat totalTime, NSArray<NSNumber *> *widths, NSArray<NSNumber *> *heights) {
        // 展示第一张图片
        if (0) {
            UIImage * image = imageArray[0];
            self.image = image;
        }
        // 展示帧动画
        else{
            //添加帧动画
            CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
            NSMutableArray * times = [[NSMutableArray alloc]init];
            float currentTime = 0;
            
            NSMutableArray * imageRefs = [NSMutableArray array];
            for (UIImage * newImage in imageArray) {
                CGImageRef imageRef = newImage.CGImage;
                [imageRefs addObject:(__bridge UIImage *)imageRef];
            }
            //设置每一帧的时间占比
            for (int i=0; i<imageRefs.count; i++) {
                [times addObject:[NSNumber numberWithFloat:currentTime/totalTime]];
                currentTime+=[timeArray[i] floatValue];
            }
            [animation setKeyTimes:times];
            [animation setValues:imageRefs];
            [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
            //设置循环
            animation.repeatCount= MAXFLOAT;
            //设置播放总时长
            animation.duration = totalTime;
            //Layer层添加
            [[(UIImageView *)__self layer]addAnimation:animation forKey:@"gifAnimation"];
        }
    }];
}

//解析gif文件数据的方法 block中会将解析的数据传递出来
-(void)getGifImageWithUrk:(NSURL *)url
               returnData:(void(^)(NSArray<UIImage *> * imageArray,
                                   NSArray<NSNumber *>*timeArray,
                                   CGFloat totalTime,
                                   NSArray<NSNumber *>* widths,
                                   NSArray<NSNumber *>* heights))dataBlock{
    //通过文件的url来将gif文件读取为图片数据引用
    CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
    
    //获取gif文件中图片的个数
    size_t count = CGImageSourceGetCount(source);
    
    //定义一个变量记录gif播放一轮的时间
    float allTime=0;
    
    //存放所有图片
    NSMutableArray * imageArray = [[NSMutableArray alloc]init];
    
    //存放每一帧播放的时间
    NSMutableArray * timeArray = [[NSMutableArray alloc]init];
    
    //存放每张图片的宽度 (一般在一个gif文件中,所有图片尺寸都会一样)
    NSMutableArray * widthArray = [[NSMutableArray alloc]init];
    
    //存放每张图片的高度
    NSMutableArray * heightArray = [[NSMutableArray alloc]init];
    //遍历
    for (size_t i=0; i<count; i++) {
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL);
        UIImage * image = [UIImage imageWithCGImage:imageRef];
        [imageArray addObject:image];
        CGImageRelease(imageRef);
        //获取图片信息
        NSDictionary * info = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
        CGFloat width = [[info objectForKey:(__bridge NSString *)kCGImagePropertyPixelWidth] floatValue];
        CGFloat height = [[info objectForKey:(__bridge NSString *)kCGImagePropertyPixelHeight] floatValue];
        [widthArray addObject:[NSNumber numberWithFloat:width]];
        [heightArray addObject:[NSNumber numberWithFloat:height]];
        NSDictionary * timeDic = [info objectForKey:(__bridge NSString *)kCGImagePropertyGIFDictionary];
        CGFloat time = [[timeDic objectForKey:(__bridge NSString *)kCGImagePropertyGIFDelayTime]floatValue];
        allTime+=time;
        [timeArray addObject:[NSNumber numberWithFloat:time]];
    }
    CFRelease(source);
    dataBlock(imageArray,timeArray,allTime,widthArray,heightArray);
}

@end

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 加载Gif图方法一:使用webView加载,需要导入#import <WebKit/WebKit.h>
    WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(100, 100, 150, 150)];
    [self.view addSubview:webView];
    NSURL * URL = [NSURL URLWithString:@"http://qq.yh31.com/tp/zr/zr168.gif"];
    NSURLRequest * request = [NSURLRequest requestWithURL:URL];
    [webView loadRequest:request];
    // 取消回弹效果
    webView.scrollView.bounces=NO;
    webView.backgroundColor = [UIColor clearColor];
    // 设置缩放模式
    webView.userInteractionEnabled = NO;
    
    
    // 加载Gif图方法二:使用ImageIO库
    UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100 ,300 , 150, 150)];
    NSURL * url = URL;
    [imageView yh_setImage:url];
    [self.view addSubview:imageView];
}


@end

自定义代码模板

  1. 进入到/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/Source目录下
  2. 任意拷贝其中一个模板,并改名为MyClass.xctemplate,后缀.xctemplate新建文件试试,是不是有MyClass这个文件模板了呢

快捷打包方式

  1. 切换到图中的情况,进行commond + B 编译一次。


  2. 找到图中.app后缀的文件,到文件所在路径,将文件拷贝一份到桌面


  3. 在桌面创建一个名为"Payload"的文件夹,将刚刚的.app文件拖入,然后压缩,压缩完成后将安装包后缀改成.ipa


文件/文件夹隐藏

文件/文件夹隐藏

保证计时器在切换到后台时,还能继续运行

#pragma mark - 程序进入后台后 继续倒计时功能
//程序进入后台不会影响到计时功能
- (void)beginBackGroundTask:(UIApplication *)application {
    self.bgIdentifier =[application beginBackgroundTaskWithExpirationHandler:^(void) {
        
        // 当应用程序留给后台的时间快要到结束时(应用程序留给后台执行的时间是有限的), 这个Block块将被执行
        // 我们需要在次Block块中执行一些清理工作。
        // 如果清理工作失败了,那么将导致程序挂掉
        // 清理工作需要在主线程中用同步的方式来进行
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        AppDelegate *weakSelf = self;
        dispatch_async(mainQueue, ^(void) {
            
            AppDelegate *strongSelf = weakSelf;
            if (strongSelf != nil){
                [self endBackGroundTask];
            }
        });
    }];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
//    [super applicationDidEnterBackground:application];
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    
    [self beginBackGroundTask:application];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    //application.applicationIconBadgeNumber = 0;
    
    [self endBackGroundTask];
    
}
//结束程序进入后台的任务
- (void)endBackGroundTask {
    // 每个对 beginBackgroundTaskWithExpirationHandler:方法的调用,必须要相应的调用 endBackgroundTask:方法。这样,来告诉应用程序你已经执行完成了。
    // 也就是说,我们向 iOS 要更多时间来完成一个任务,那么我们必须告诉 iOS 你什么时候能完成那个任务。
    // 也就是要告诉应用程序:“好借好还”嘛。
    // 标记指定的后台任务完成
    [[UIApplication sharedApplication] endBackgroundTask:self.bgIdentifier];
    // 销毁后台任务标识符
    self.bgIdentifier = UIBackgroundTaskInvalid;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,266评论 7 249
  • 从前,你说,时光很慢 你喜欢一个人坐在阶前 摘花,听雨 像个安静的少年 那些从远方飘来的信笺 满载着深深思念 却在...
    边城幻影阅读 274评论 27 5
  • 科学界有一种主流说法,地球原本没有水,是因一颗彗星(由大量冰凝结而成)撞到地球上,这颗土球才变为一颗蔚蓝星球。在后...
    裸足阅读 841评论 0 0
  • 很多的职业经理人是从基层出身的,有着一身摸爬滚打的好本事,实务经验非常丰富,堪称为一员猛将,但是一碰到电脑,有时候...
    周明达老师阅读 1,013评论 0 6