iOS日常问题记录

487 阅读26分钟

1、iOS8集成极光推送造成应用闪退问题,报错如下,把UserNotifications.framework的status状态改为Optional可选即可。

dyld: Library not loaded: /System/Library/Frameworks/UserNotifications.framework/UserNotifications
  Referenced from: /var/containers/Bundle/Application/********-****-****-****-************/****.app/****
  Reason: image not found

2、iOS导航条隐藏后无法支持手势侧滑

<UIGestureRecognizerDelegate,UINavigationControllerDelegate>
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:YES animated:NO];
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
    self.navigationController.delegate=self;
   
}

这样解决后发现导航根视图侧滑后页面跳转错乱,解决办法是在跟视图关闭手势

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    if (viewController == navigationController.viewControllers[0])
    {
        navigationController.interactivePopGestureRecognizer.enabled = NO;
    }else {
        navigationController.interactivePopGestureRecognizer.enabled = YES;
    }
}

导航侧滑手势监听,包含WKWebView设置setAllowsBackForwardNavigationGestures手势返回事件监听

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
   
    return YES;
}

3、AFNetwork错误解析

NSDictionary * errorInfo = error.userInfo;
if ([[errorInfo allKeys] containsObject: @"com.alamofire.serialization.response.error.data"]){
   NSData * errorData = errorInfo[@"com.alamofire.serialization.response.error.data"];
   NSDictionary * errorDict =  [NSJSONSerialization JSONObjectWithData: errorData options:NSJSONReadingAllowFragments error:nil];
    [SVProgressHUD showInfoWithStatus:[errorDict objectForKey:@"ExceptionMessage"]];
}

请求头传值token

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];//请求
[manager.requestSerializer setValue:token forHTTPHeaderField:@"Authorization"];

请求返回为非json时

manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html",@"image/png",nil];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];

4、xcode更新后项目报错如下

Conflicting types for 'xmlParseMemory'

解决方法在报错文件中引入头文件如下:

#include <libxml/parser.h>
#include <libxml/xmlmemory.h>

5、iOS导航栏设置背景色, 返回按钮图片在下面另存为即可

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:NO animated:NO];
    self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:75/255.0 green:89/255.0 blue:121/255.0 alpha:1];
    // 设置返回按钮
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [backButton setImage:[UIImage imageNamed:@"bar_back"] forState:UIControlStateNormal];
    [backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    
    [backButton sizeToFit];
    
    // 注意:一定要在按钮内容有尺寸的时候,设置才有效果
    backButton.contentEdgeInsets = UIEdgeInsetsMake(0, -25, 0, 0);
    
    // 设置返回按钮
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

//默认返回按钮
//self.navigationController.navigationBar.tintColor =[UIColor whiteColor];
//self.navigationController.navigationBar.titleTextAttributes=[NSDictionary //dictionaryWithObject:[UIColor whiteColor] forKey:UITextAttributeTextColor];
//self.tabBarController.tabBar.selectedImageTintColor=[UIColor whiteColor];

}

bar_back.png

6、微信支付客户端加签

#pragma mark ======获取当前时间戳=======

+ (NSString*)getCurrentTime {
    
    NSDate*datenow = [NSDate date];
    
    NSString*timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];
    
    NSTimeZone*zone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    
    NSInteger interval = [zone secondsFromGMTForDate:datenow];
    
    NSDate*localeDate = [datenow dateByAddingTimeInterval:interval];
    
    NSString*timeSpp = [NSString stringWithFormat:@"%f", [localeDate timeIntervalSince1970]];
    
    return timeSpp;
    
}
//创建package签名
+(NSString*) createMd5Sign:(NSMutableDictionary*)dict
{
    NSMutableString *contentString  =[NSMutableString string];
    NSArray *keys = [dict allKeys];
    //按字母顺序排序
    NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj1 compare:obj2 options:NSNumericSearch];
    }];
    //拼接字符串
    for (NSString *categoryId in sortedArray) {
        if (   ![[dict objectForKey:categoryId] isEqualToString:@""]
            && ![categoryId isEqualToString:@"sign"]
            && ![categoryId isEqualToString:@"key"]
            )
        {
            [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]];
        }
        
    }
    //添加key字段
    [contentString appendFormat:@"key=%@", @""];
    //得到MD5 sign签名
    NSString *md5Sign =[contentString MD5];
    
    return [md5Sign uppercaseString];
}
+(long)getRandomNumber:(int)from to:(int)to
{
    return (long)(from + (arc4random() % (to - from + 1)));
}
+ (NSString *)jumpToBizPay:(NSDictionary*)paramter {
    //调起微信支付
    PayReq* req             = [[PayReq alloc] init];
    //微信支付分配的商户号
    req.partnerId           = @"";
    /** 预支付订单 从服务器获取 */
    req.prepayId            = [paramter objectForKey:@"prepayID"];
    /** 随机串,防重发 */
    req.nonceStr            = [NSString stringWithFormat:@"%ld",[WXApiRequestHandler getRandomNumber:100 to:100000000]];
    /** 时间戳,防重发 */
    req.timeStamp           = [[WXApiRequestHandler getCurrentTime]intValue];
    /** 商家根据财付通文档填写的数据和签名 <暂填写固定值Sign=WXPay>*/
    req.package             = @"Sign=WXPay";
    /** 商家根据微信开放平台文档对数据做的签名, 可从服务器获取,也可本地生成*/
    NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
    [dic setObject:@"" forKey:@"appid"];
    [dic setObject:req.partnerId forKey:@"partnerid"];
    [dic setObject:req.prepayId forKey:@"prepayid"];
    [dic setObject:req.package forKey:@"package"];
    [dic setObject:req.nonceStr forKey:@"noncestr"];
    [dic setObject:[NSString stringWithFormat:@"%u",(unsigned int)req.timeStamp] forKey:@"timestamp"];
    req.sign                = [WXApiRequestHandler createMd5Sign:dic];
    
    [WXApi sendReq:req];

    return @"";
}

7、支付宝加签

#define ALI_NOTIFY_URL      @""
#define ALI_APPID           @""
#define ALI_RSA2PRIVATEKEY  @""

@implementation OrderPay
//
//选中商品调用支付宝极简支付
//
- (void)toAliPay:(NSDictionary*)paramter blockcompletion:(BackDic)backResultDic{
    //重要说明
    //这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;
    //真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;
    //防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;
/*============================================================================*/
    /*=======================需要填写商户app申请的===================================*/
    /*============================================================================*/
    NSString *appID = ALI_APPID;
    
    // 如下私钥,rsa2PrivateKey 或者 rsaPrivateKey 只需要填入一个
    // 如果商户两个都设置了,优先使用 rsa2PrivateKey
    // rsa2PrivateKey 可以保证商户交易在更加安全的环境下进行,建议使用 rsa2PrivateKey
    // 获取 rsa2PrivateKey,建议使用支付宝提供的公私钥生成工具生成,
    // 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1
    NSString *rsa2PrivateKey = ALI_RSA2PRIVATEKEY;
    NSString *rsaPrivateKey = @"";
    /*============================================================================*/
    /*============================================================================*/
    /*============================================================================*/
    
    //partner和seller获取失败,提示
    if ([appID length] == 0 ||
        ([rsa2PrivateKey length] == 0 && [rsaPrivateKey length] == 0))
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"缺少appId或者私钥。"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil];
        [alert show];
        return;
    }
    
    /*
     *生成订单信息及签名
     */
    //将商品信息赋予AlixPayOrder的成员变量
    Order* order = [Order new];
    
    // NOTE: app_id设置
    order.app_id = appID;
    
    // NOTE: 支付接口名称
    order.method = @"alipay.trade.app.pay";
    
    // NOTE: 参数编码格式
    order.charset = @"utf-8";
    
    // NOTE: 当前时间点
    NSDateFormatter* formatter = [NSDateFormatter new];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    order.timestamp = [formatter stringFromDate:[NSDate date]];
    
    //NOTE:回调
    order.notify_url = ALI_NOTIFY_URL;
    order.passback_params = [NSString stringWithFormat:@"%@",[paramter objectForKey:@"tradeID"]];
    // NOTE: 支付版本
    order.version = @"1.0";
    
    // NOTE: sign_type 根据商户设置的私钥来决定
    order.sign_type = (rsa2PrivateKey.length > 1)?@"RSA2":@"RSA";
    
    // NOTE: 商品数据
    order.biz_content = [BizContent new];
    order.biz_content.body = [paramter objectForKey:@"body"];
    order.biz_content.subject = [paramter objectForKey:@"title"];
    order.biz_content.out_trade_no = [paramter objectForKey:@"tradeID"]; //订单ID(由商家自行制定)
    order.biz_content.timeout_express = @"30m"; //超时时间设置
    order.biz_content.total_amount = [NSString stringWithFormat:@"%@",[paramter objectForKey:@"totalPrice"]]; //商品价格
    
    //将商品信息拼接成字符串
    NSString *orderInfo = [order orderInfoEncoded:NO];
    NSString *orderInfoEncoded = [order orderInfoEncoded:YES];
    NSLog(@"orderSpec = %@",orderInfo);
    
    // NOTE: 获取私钥并将商户信息签名,外部商户的加签过程请务必放在服务端,防止公私钥数据泄露;
    //       需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
    NSString *signedString = nil;
    RSADataSigner* signer = [[RSADataSigner alloc] initWithPrivateKey:((rsa2PrivateKey.length > 1)?rsa2PrivateKey:rsaPrivateKey)];
    if ((rsa2PrivateKey.length > 1)) {
        signedString = [signer signString:orderInfo withRSA2:YES];
    } else {
        signedString = [signer signString:orderInfo withRSA2:NO];
    }
    
    // NOTE: 如果加签成功,则继续执行支付
    if (signedString != nil) {
        //应用注册scheme,在AliSDKDemo-Info.plist定义URL types
        NSString *appScheme = @"chinafortuneparkalidemo";
        
        // NOTE: 将签名成功字符串格式化为订单字符串,请严格按照该格式
        NSString *orderString = [NSString stringWithFormat:@"%@&sign=%@",
                                 orderInfoEncoded, signedString];
        
        // NOTE: 调用支付结果开始支付
        [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            backResultDic(resultDic);
            if ([[resultDic objectForKey:@"resultStatus"] intValue] ==9000) {
                
                [SVProgressHUD showSuccessWithStatus:@"支付成功!"];
            }else{
                [SVProgressHUD showErrorWithStatus:@"支付失败!"];
            }
        }];
    }
}

8、网络环境监测

// 2.设置网络状态改变后的处理
            [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
                // 当网络状态改变了, 就会调用这个block
                switch (status) {
                    case AFNetworkReachabilityStatusUnknown: // 未知网络
                        NSLog(@"未知网络");
                        break;
                        
                    case AFNetworkReachabilityStatusNotReachable: // 没有网络(断网)
                        NSLog(@"没有网络(断网)");
                        break;
                        
                    case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
                    {
                    }
                        break;
                        
                    case AFNetworkReachabilityStatusReachableViaWiFi: // WIFI
                    {
                    }
                        break;
                }
            }];
            [[AFNetworkReachabilityManager sharedManager] startMonitoring];

9、mp4视频播放

#import <MediaPlayer/MediaPlayer.h>

//本地视频
//NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"xxx.mp4" ofType:nil];
//NSURL * videoURL =[NSURL fileURLWithPath:urlStr];

//在线视频
NSURL *videoURL = URL;
MPMoviePlayerViewController *moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
moviePlayerController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
moviePlayerController.moviePlayer.shouldAutoplay = YES;
[moviePlayerController.moviePlayer prepareToPlay];
[moviePlayerController.moviePlayer play];
[self presentViewController:moviePlayerController animated:YES completion:nil];

AVFoundation播放视频

#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
playerViewController.player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:savedImagePath]];
[_delegate presentViewController:playerViewController animated:YES completion:nil];
[playerViewController.player play];

10、确认弹出框

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"手机网络是否播放" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                            
                        }])];
[alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                        }])];
[self presentViewController:alertController animated:YES completion:nil];

11、生成高清二维码

 // 1. 创建一个二维码滤镜实例(CIFilter)
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    // 滤镜恢复默认设置
    [filter setDefaults];
    
    // 2. 给滤镜添加数据
    NSString *string = qcode;
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    // 使用KVC的方式给filter赋值
    [filter setValue:data forKeyPath:@"inputMessage"];
    
    // 3. 生成二维码
    CIImage *image = [filter outputImage];
    _testimageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, kWindowWidth, kWindowWidth)];
    // 4. 显示二维码
    _testimageView.image = [self createNonInterpolatedUIImageFormCIImage:image withSize:kWindowWidth];
    [self.view addSubview:_testimageView];
**
 * 根据CIImage生成指定大小的UIImage
 *
 * @param image CIImage
 * @param size 图片宽度
 */
- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size
{
    CGRect extent = CGRectIntegral(image.extent);
    CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
    // 1.创建bitmap;
    size_t width = CGRectGetWidth(extent) * scale;
    size_t height = CGRectGetHeight(extent) * scale;
    CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
    CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
    CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
    CGContextScaleCTM(bitmapRef, scale, scale);
    CGContextDrawImage(bitmapRef, extent, bitmapImage);
    // 2.保存bitmap到图片
    CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
    CGContextRelease(bitmapRef);
    CGImageRelease(bitmapImage);
    return [UIImage imageWithCGImage:scaledImage];
}

12、状态栏让视图下移20px

  //网页
    UIView* stausBg =[[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)];
    stausBg.backgroundColor = [UIColor clearColor];
    [self.view addSubview:stausBg];
- (void)setAdjustmentNever
{
    if (@available(iOS 11.0, *)) {
        [[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
    }
}

- (void)setAdjustmentAutomatic
{
    if (@available(iOS 11.0, *)) {
        [[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentAutomatic];
    }
}

13、获取iOS唯一标识 NSUUID,卸载后重新安装会该值会改变

NSString *uuid = [[NSUUID UUID] UUIDString];

IDFV-identifierForVendor获取广告位标识,卸载后不会改变但上架App Store如果未接入广告服务可能会被拒

#import <AdSupport/ASIdentifierManager.h>

NSString *deviceID = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];

以上各方法均为最终解决问题,经查阅资料写了下面的类才获取唯一标识UUID,卸载不变(2018-9-29更新)

#import <Foundation/Foundation.h>
#import <Security/Security.h>


@interface UUIDStrengthen : NSObject
/**
 本方法是得到 UUID 后存入系统中的 keychain 的方法
 不用添加 plist 文件
 程序删除后重装,仍可以得到相同的唯一标示
 但是当系统升级或者刷机后,系统中的钥匙串会被清空,此时本方法失效
 */
-(NSString *)getDeviceIDInKeychain;
@end
//
//  UUIDStrengthen.m
//  bitech
//
//  Created by BITECH on 2018/9/29.
//  Copyright © 2018年 bitech. All rights reserved.
//

#import "UUIDStrengthen.h"

@implementation UUIDStrengthen
-(NSString *)getDeviceIDInKeychain
{
    NSString *getUDIDInKeychain = (NSString *)[self load:@"com.myapp.udid.test"];
    
    NSLog(@"从keychain中获取到的 UDID_INSTEAD %@",getUDIDInKeychain);
    if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
        CFUUIDRef puuid = CFUUIDCreate( nil );
        CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
        NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
        CFRelease(puuid);
        CFRelease(uuidString);
        NSLog(@"\n \n \n _____重新存储 UUID _____\n \n \n  %@",result);
        [self save:@"com.myapp.udid.test" data:result];
        getUDIDInKeychain = (NSString *)[self load:@"com.myapp.udid.test"];
    }
    NSLog(@"最终 ———— UDID_INSTEAD %@",getUDIDInKeychain);
    return getUDIDInKeychain;
}

#pragma mark - private

- (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (id)kSecClassGenericPassword,(id)kSecClass,
            service, (id)kSecAttrService,
            service, (id)kSecAttrAccount,
            (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
            nil];
}

- (void)save:(NSString *)service data:(id)data {
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Delete old item before add new item
    SecItemDelete((CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}

- (id)load:(NSString *)service {
    id ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Configure the search setting
    //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    OSStatus status =  SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData);
    if (status == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
            return @"解析设备错误,请返回页面重新获取";
        } @finally {
        }
    } else if (status != errSecItemNotFound) {
       return @"查询设备错误,请返回页面重新获取";
    }
    if (keyData)
    CFRelease(keyData);
    return ret;
}

- (void)delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end

NSString *deviceID = [[UUIDStrengthen alloc]getDeviceIDInKeychain];
if([deviceID isEqualToString:@"查询设备错误,请滑动返回页面重新获取"]||[deviceID isEqualToString:@"解析设备错误,请滑动返回页面重新获取"]){
      [SVProgressHUD showInfoWithStatus:deviceID];
      [self performSelector:@selector(dismiss) withObject:nil afterDelay:2.0f];
} else {
      [wkWebView evaluateJavaScript:[NSString stringWithFormat:@"iosSetLocation('%f','%f','%s','%@')",location.coordinate.latitude,location.coordinate.longitude,"str",deviceID] completionHandler:nil];
 }

14、iOS原生实现人脸识别

#import <AVFoundation/AVFoundation.h>
#import "SVProgressHUD.h"
#import <AssetsLibrary/ALAsset.h>

#import <AssetsLibrary/ALAssetsLibrary.h>

#import <AssetsLibrary/ALAssetsGroup.h>

#import <AssetsLibrary/ALAssetRepresentation.h>
@interface CameralViewController ()<AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureMetadataOutputObjectsDelegate>
{
    WBProgressBlock _successBlock;
}
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureDevice *captureDevice;
@property (nonatomic, strong) AVCaptureDeviceInput *captureVideoDeviceInput;
@property (nonatomic, strong) AVCaptureMetadataOutput *metaDataOutput;
@property (nonatomic, strong) AVCaptureVideoDataOutput *captureOutput;
@property (nonatomic, strong) AVCaptureConnection *captureConnection;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
@property (nonatomic, strong) NSMutableArray <UIView *> *faceViewArrM;

@end
@implementation CameralViewController
-(instancetype)initWithBitechSuccess:(void (^)(NSString *filepath))success;
{
    self =[super init];
    _successBlock = success;
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.faceViewArrM = [NSMutableArray array];
    _hasFace=NO;
    _isShowPic=NO;
    self.captureSession = [[AVCaptureSession alloc] init];
    if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
        self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
    }
    else {
        self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    //    for (AVCaptureDevice *device in [AVCaptureDevice devices]) {
    //        if ([device hasMediaType:AVMediaTypeVideo]) {
    //            if (device.position == AVCaptureDevicePositionFront) {
    //                self.captureDevice = device;
    //            }
    //        }
    //    }
    self.captureDevice = [self cameroWithPosition:AVCaptureDevicePositionFront];
    
    //添加输入
    [self addVideoInput];
    
    //添加输出
    [self addVideoOutput];
    
    //添加预览图层
    [self addPreviewLayer];
    
    [self.captureSession commitConfiguration];
    [self.captureSession startRunning];
}


//获取可用的摄像头
- (AVCaptureDevice *)cameroWithPosition:(AVCaptureDevicePosition)position{
    
    if (@available(iOS 10.2, *)) {
        AVCaptureDeviceDiscoverySession *dissession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInDualCamera,AVCaptureDeviceTypeBuiltInTelephotoCamera,AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:position];
        for (AVCaptureDevice *device in dissession.devices) {
            if ([device position] == position ) {
                return device;
            }
        }
    } else {
        NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
        for (AVCaptureDevice *device in devices) {
            if ([device position] == position) {
                return device;
            }
        }
    }
    return nil;
}


- (void)addVideoInput
{
    NSError *error;
    self.captureVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.captureDevice error:&error];
    if (error) {
        return;
    }
    if ([self.captureSession canAddInput:self.captureVideoDeviceInput]) {
        [self.captureSession addInput:self.captureVideoDeviceInput];
    }
}

- (void)addVideoOutput
{
    self.metaDataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.metaDataOutput setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)];
    self.captureOutput=[[AVCaptureVideoDataOutput alloc] init];
    [self.captureOutput setSampleBufferDelegate:self queue:dispatch_queue_create("sample",NULL)];
    if ([self.captureSession canAddOutput:self.metaDataOutput]) {
        [self.captureSession addOutput:self.metaDataOutput];
    }
    if ([self.captureSession canAddOutput:self.captureOutput]) {
        [self.captureSession addOutput:self.captureOutput];
    }
    
    self.metaDataOutput.metadataObjectTypes =  @[AVMetadataObjectTypeFace];
    NSString     *key           = (NSString *)kCVPixelBufferPixelFormatTypeKey;
    NSNumber     *value         = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
    NSDictionary *videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
    
    [self.captureOutput setVideoSettings:videoSettings];
    
    //设置链接管理对象
    self.captureConnection = [self.metaDataOutput connectionWithMediaType:AVMediaTypeVideo];
    //视频旋转方向设置
    self.captureConnection.videoScaleAndCropFactor = self.captureConnection.videoMaxScaleAndCropFactor;;
    if ([self.captureConnection isVideoOrientationSupported]){
        
        [self.captureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
        
    }
    //视频稳定设置
    if ([self.captureConnection isVideoStabilizationSupported]) {
        self.captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
    }
}

- (void)addPreviewLayer
{
    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
    self.previewLayer.frame = self.view.bounds;
    [self.view.layer addSublayer:self.previewLayer];
    
    self.faceLayer=[[CALayer alloc]init];
    self.faceLayer.borderColor=[UIColor redColor].CGColor;
    self.faceLayer.borderWidth=1;
    [self.view.layer addSublayer:self.faceLayer];
}

#pragma mark -  AVCaptureVideoDataOutputSampleBufferDelegate

- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    NSLog(@"检测到了人脸,数目为%lu", (unsigned long)metadataObjects.count);
    if (metadataObjects.count > 0) {
        _hasFace=YES;
        NSLog(@"%@", metadataObjects);
        NSMutableArray *bounds = [NSMutableArray arrayWithCapacity:0];
        for (AVMetadataFaceObject *faceobject in metadataObjects) {
            AVMetadataObject *face = [output transformedMetadataObjectForMetadataObject:faceobject connection:connection];
            [bounds addObject:[NSValue valueWithCGRect:face.bounds]];
        }
        
        for (NSValue *rect in bounds) {
            CGRect r = [rect CGRectValue];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                int swidth =  self.view.frame.size.width;
                int sheight = self.view.frame.size.height;
                self.faceLayer.hidden=NO;
                self.faceLayer.frame=CGRectMake(swidth*r.origin.y, sheight*r.origin.x, swidth*r.size.height, sheight*r.size.width);
            });
        }
    }
    else {
        _hasFace=NO;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            self.faceLayer.hidden=YES;
        });
    }
}
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    if (!_isShowPic) {
        //转换成UIImage
        UIImage *image = [self getImageBySampleBufferref:sampleBuffer];
        //这里不考虑性能 直接怼Image
        dispatch_async(dispatch_get_main_queue(), ^{
            _pictureImageView.image = image;
        });
    }
}
// 将获取的数据转换成图片,网上很多转换方式转出来的图片都是错误的,最后找到这个方法

- (UIImage *)getImageBySampleBufferref:(CMSampleBufferRef)sampleBuffer

{
// 方法1
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(imageBuffer,0);
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace,                                                  kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    
    CGImageRef newImage = CGBitmapContextCreateImage(newContext);
    
    CGContextRelease(newContext);
    CGColorSpaceRelease(colorSpace);

    UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationLeftMirrored];
    // release
    CGImageRelease(newImage);
    
    return image;
    
// 方法2
//    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
//
//    /*Lock the image buffer*/
//
//    CVPixelBufferLockBaseAddress(imageBuffer,0);
//
//    /*Get information about the image*/
//
//    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
//
//    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
//
//    size_t width = CVPixelBufferGetWidth(imageBuffer);
//
//    size_t height = CVPixelBufferGetHeight(imageBuffer);
//    /*We unlock the  image buffer*/
//
//    CVPixelBufferUnlockBaseAddress(imageBuffer,0);
//    /*Create a CGImageRef from the CVImageBufferRef*/
//
//    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//
//    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
//
//    CGImageRef newImage = CGBitmapContextCreateImage(newContext);
//
//
//
//    /*We release some components*/
//
//    CGContextRelease(newContext);
//
//    CGColorSpaceRelease(colorSpace);
//    UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationRight];
//
//    NSLog(@"%@", image);
//    /*We relase the CGImageRef*/
//
//    CGImageRelease(newImage);
//
//    return image;
    
}

图片转成base64串

UIImage *image = _pictureImageView.image;
NSData *imageData = UIImageJPEGRepresentation(image, 0.3f);
NSString *imageBase64Str = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

15、监测网络是否畅通(有网络请求数据不成功)

if ([self checkNetworkCanUse]) {
        NSLog(@"网络畅通");
}else{
      NSLog(@"网络不通");
}
#define kAppleUrlTocheckWifi @"http://captive.apple.com"
-(BOOL)checkNetworkCanUse{
    
    // 1.将网址初始化成一个OC字符串对象
    NSString *urlStr = kAppleUrlTocheckWifi;
    // 如果网址中存在中文,进行URLEncode
    NSString *newUrlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    // 2.构建网络URL对象, NSURL
    NSURL *url = [NSURL URLWithString:newUrlStr];
    // 3.创建网络请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:3];
    // 创建同步链接
    NSURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
    NSString* result1 = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    //解析html页面
    NSString *str = [self flattenHTML:result1];
    //除掉换行符
    NSString *nstr = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    
    if ([nstr isEqualToString:@"SuccessSuccess"])
    {
        // NSLog(@"可以上网了");
        //   [PronetwayGeneralHandle shareHandle].NetworkCanUse = YES;
        return YES;
    }else {
        // NSLog(@"未联网");
        //[self showNetworkStatus:@"未联网"];
        //   [PronetwayGeneralHandle shareHandle].NetworkCanUse = NO;
        return NO;
    }
}

16、扫描二维码跳转appstore

https://itunes.apple.com/app/id1371231677
// itms-apps://itunes.apple.com/cn/app/id1371231677?mt=8

17、base64与图片互转

// 64base字符串转图片

- (UIImage *)stringToImage:(NSString *)str {
   //html获取的base64需要截取后才能使用
    NSArray *arr=[str componentsSeparatedByString:@"base64,"];
    NSData *decodedImageData = [[NSData alloc]initWithBase64EncodedString:arr[1] options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    UIImage *decodedImage = [UIImage imageWithData:decodedImageData];
    _showImageView.image = decodedImage;
    
    return decodedImage;
    
}

// 图片转64base字符串

- (NSString *)imageToString:(UIImage *)image {
    
    NSData *imagedata = UIImagePNGRepresentation(image);
    
    NSString *image64 = [imagedata base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    return image64;
}

18、iOS上传服务器图片旋转问题

-(UIImage *)fixOrientation:(UIImage *)aImage {
    // No-op if the orientation is already correct
    if (aImage.imageOrientation ==UIImageOrientationUp)
        return aImage;
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform =CGAffineTransformIdentity;
    switch (aImage.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width,0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        default:
            break;
    }
    switch (aImage.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            
            transform = CGAffineTransformTranslate(transform, aImage.size.width,0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.height,0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        default:
            break;
    }
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx =CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
                                            CGImageGetBitsPerComponent(aImage.CGImage),0,
                                            CGImageGetColorSpace(aImage.CGImage),
                                            CGImageGetBitmapInfo(aImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (aImage.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
            break;
        default:
            CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
            break;
    }
    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg =CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}

19、appstore隐私策略

一、总则
1.1 XXXX的所有权和运营权归XXXX所有。
1.2 用户在注册之前,应当仔细阅读本协议,并同意遵守本协议后方可成为注册用户。一旦注册成功,则用户与XXXX之间自动形成协议关系,用户应当受本协议 的约束。用户在使用特殊的服务或产品时,应当同意接受相关协议后方能使用。
1.3 本协议则可由XXXX随时更新,用户应当及时关注并同意本站不承担通知义务。本站的通知、公告、声明或其它类似内容是本协议的一部分。

二、服务内容
 2.1 XXXX的具体内容由本站根据实际情况提供。
 2.2 本站仅提供相关的网络服务,除此之外与相关网络服务有关的设备(如个人电脑、手机、及其他与接入互联网或移动网有关的装置)及所需的费用(如为接入互联 网而支付的电话费及上网费、为使用移动网而支付的手机费)均应由用户自行负担。
 三、用户帐号
 3.1 经本站注册系统完成注册程序并通过身份认证的用户即成为正式用户,可以获得本站规定用户所应享有的一切权限;未经认证仅享有本站规定的部分会员权限 。保宝网有权对会员的权限设计进行变更。
 3.2 用户只能按照注册要求使用真实手机号注册。用户有义务保证密码和帐号的安全,用户利用该密码和帐号所进行的一切活动引起的任何损失或损害,由用户自 行承担全部责任,本站不承担任何责任。如用户发现帐号遭到未授权的使用或发生其他任何安全问题,应立即修改帐号密码并妥善保管,如有必要,请通知本站。 因黑客行为或用户的保管疏忽导致帐号非法使用,本站不承担任何责任。
 四、使用规则
 4.1 遵守中华人民共和国相关法律法规,包括但不限于《中华人民共和国计算机信息系统安全保护条例》、《计算机软件保护条例》、《最高人民法院关于审理涉 及计算机网络著作权纠纷案件适用法律若干问题的解释(法释[2004]1号)》、《全国人大常委会关于维护互联网安全的决定》、《互联网电子公告服务管理规定》、 《互联网新闻信息服务管理规定》、《互联网著作权行政保护办法》和《信息网络传播权保护条例》等有关计算机互联网规定和知识产权的法律和法规、实施办法 。
 4.2 用户对其自行发表、上传或传送的内容负全部责任,所有用户不得在本站任何页面发布、转载、传送含有下列内容之一的信息,否则本站有权自行处理并不通 知用户:

 (1)违反宪法确定的基本原则的;
 (2)危害国家安全,泄漏国家机密,颠覆国家政权,破坏国家统一的;
 (3)损害国家荣誉和利益的;
 (4)煽动民族仇恨、民族歧视,破坏民族团结的;
 (5)破坏国家宗教政策,宣扬邪教和封建迷信的;
 (6)散布谣言,扰乱社会秩序,破坏社会稳定的;
 (7)散布淫秽、色情、赌博、暴力、恐怖或者教唆犯罪的;
 (8)侮辱或者诽谤他人,侵害他人合法权益的;
 (9)煽动非法集会、结社、游行、示威、聚众扰乱社会秩序的;
 (10)以非法民间组织名义活动的;
 (11)含有法律、行政法规禁止的其他内容的。
 4.3 用户承诺对其发表或者上传于本站的所有信息(即属于《中华人民共和国著作权法》规定的作品,包括但不限于文字、图片、音乐、电影、表演和录音录像制品 和电脑程序等)均享有完整的知识产权,或者已经得到相关权利人的合法授权;如用户违反本条规定造成本站被第三人索赔的,用户应全额补偿本站一切费用(包括 但不限于各种赔偿费、诉讼代理费及为此支出的其它合理费用);
 4.4 当第三方认为用户发表或者上传于本站的信息侵犯其权利,并根据《信息网络传播权保护条例》或者相关法律规定向本站发送权利通知书时,用户同意本站可 以自行判断决定删除涉嫌侵权信息,除非用户提交书面证据材料排除侵权的可能性,本站将不会自动恢复上述删除的信息;
 (1)不得为任何非法目的而使用网络服务系统;
 (2)遵守所有与网络服务有关的网络协议、规定和程序; (3)不得利用本站进行任何可能对互联网的正常运转造成不利影响的行为;
 (4)不得利用本站进行任何不利于本站的行为。
 4.5 如用户在使用网络服务时违反上述任何规定,本站有权要求用户改正或直接采取一切必要的措施(包括但不限于删除用户张贴的内容、暂停或终止用户使用网络 服务的权利)以减轻用户不当行为而造成的影响。


 五、隐私保护
 5.1 本站不对外公开或向第三方提供单个用户的注册资料及用户在使用网络服务时存储在本站的非公开内容,但下列情况除外:
 (1)事先获得用户的明确授权;
 (2)根据有关的法律法规要求;
 (3)按照相关政府主管部门的要求;
 (4)为维护社会公众的利益。
 5.2 本站可能会与第三方合作向用户提供相关的网络服务,在此情况下,如该第三方同意承担与本站同等的保护用户隐私的责任,则本站有权将用户的注册资料等 提供给该第三方。
 5.3 在不透露单个用户隐私资料的前提下,本站有权对整个用户数据库进行分析并对用户数据库进行商业上的利用。


 六、版权声明
 6.1 本站的文字、图片、音频、视频等版权均归上海瑞谷拜特软件技术有限公司享有或与作者共同享有,未经本站许可,不得任意转载。
 6.2 本站特有的标识、版面设计、编排方式等版权均属上海瑞谷拜特软件技术有限公司享有,未经本站许可,不得任意复制或转载。
 6.3 使用本站的任何内容均应注明“来源于XXXX” 及署上作者姓名,按法律规定需要支付稿酬的,应当通知本站及作者及支付稿酬,并独立承担一切法律责任。
 6.4 本站享有所有作品用于其它用途的优先权,包括但不限于网站、电子杂志、平面出版等,但在使用前会通知作者,并按同行业的标准支付稿酬。
 6.5 本站所有内容仅代表作者自己的立场和观点,与本站无关,由作者本人承担一切法律责任。
 6.6 恶意转载本站内容的,本站保留将其诉诸法律的权利。


 七、责任声明
 7.1 用户明确同意其使用本站网络服务所存在的风险及一切后果将完全由用户本人承担,保宝网对此不承担任何责任。
 7.2 本站无法保证网络服务一定能满足用户的要求,也不保证网络服务的及时性、安全性、准确性。
 7.3 本站不保证为方便用户而设置的外部链接的准确性和完整性,同时,对于该等外部链接指向的不由本站实际控制的任何网页上的内容,本站不承担任何责任。
 7.4 对于因不可抗力或本站不能控制的原因造成的网络服务中断或其它缺陷,本站不承担任何责任,但将尽力减少因此而给用户造成的损失和影响。
 7.5 对于站向用户提供的下列产品或者服务的质量缺陷本身及其引发的任何损失,本站无需承担任何责任:
 (1)本站向用户免费提供的各项网络服务;
 (2)本站向用户赠送的任何产品或者服务。
 7.6 本站有权于任何时间暂时或永久修改或终止本服务(或其任何部分),而无论其通知与否,本站对用户和任何第三人均无需承担任何责任。


 八、附则
 8.1 本协议的订立、执行和解释及争议的解决均应适用中华人民共和国法律。
 8.2 如本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,本协议的其余条款仍应有效并且有约束力。
 8.3 本协议解释权及修订权归上海瑞谷拜特软件技术有限公司所有。

20、iOS上架获取专用密码地址 专用密码

https://appleid.apple.com/#!&page=signin

21、瑞谷老项目CGRectMake被锁死,使用下面替换

    webviewn.size=CGSizeMake(self.view.frame.size.width,scrollView.frame.size.height);
    webviewn.center=CGPointMake(sender.currentPage*self.view.frame.size.width+self.view.frame.size.width/2, scrollView.frame.size.height/2);

22、自定义极光推送提示音 (1)转换mp3到caf

afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v

(2)拖入bundle resources中

拖入项目
(3)极光后台测试
极光

23、检测当前应用是否开启定位并跳转设置

#pragma mark 判断是否打开定位没有提示设置

-(void)determineWhetherTheAPPOpensTheLocation{
   
   if (!([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways))) {
       //定位功能不可用
       // 初始化对话框
       UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"请到设置->隐私->定位服务中开启【未来城】定位服务,以便于距离筛选能够准确获得你的位置信息" preferredStyle:UIAlertControllerStyleAlert];
       // 确定注销
       UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
             [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
       }];
       UIAlertAction *cancelAction =[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
       
       [alert addAction:okAction];
       [alert addAction:cancelAction];
       // 弹出对话框
       [self presentViewController:alert animated:true completion:nil];
   }
}

24、首次网络加载失败,从新请求数据

 //开启定时器
    if (!m_netSpeed) {
        m_netSpeed=[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkNetworkCanUse) userInfo:nil repeats:YES];
        //        [[NSRunLoop mainRunLoop] addTimer:m_netSpeed forMode:NSDefaultRunLoopMode];
    }

#define kAppleUrlTocheckWifi @"http://captive.apple.com"
-(BOOL)checkNetworkCanUse{
    // 1.将网址初始化成一个OC字符串对象
    NSString *urlStr = kAppleUrlTocheckWifi;
    // 如果网址中存在中文,进行URLEncode
    NSString *newUrlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    // 2.构建网络URL对象, NSURL
    NSURL *url = [NSURL URLWithString:newUrlStr];
    // 3.创建网络请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:4];
    // 创建同步链接
    NSURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
    NSString* result1 = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    //解析html页面
    NSString *str = [self flattenHTML:result1];
    //除掉换行符
    NSString *nstr = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    
    if ([nstr isEqualToString:@"SuccessSuccess"])
    {
        // NSLog(@"可以上网了");
        //   [PronetwayGeneralHandle shareHandle].NetworkCanUse = YES;
        if (m_netSpeed.valid==YES)
        {
            [m_netSpeed invalidate];
            m_netSpeed=nil;
            [self refreshWebView];
        }
        return YES;
    } else {
        // NSLog(@"未联网");
        //[self showNetworkStatus:@"未联网"];
        //   [PronetwayGeneralHandle shareHandle].NetworkCanUse = NO;
        return NO;
    }
}

//过滤后台返回字符串中的标签
- (NSString *)flattenHTML:(NSString *)html {
    
    NSScanner *theScanner;
    NSString *text = nil;
    
    theScanner = [NSScanner scannerWithString:html];
    
    while ([theScanner isAtEnd] == NO) {
        // find start of tag
        [theScanner scanUpToString:@"<" intoString:NULL] ;
        // find end of tag
        [theScanner scanUpToString:@">" intoString:&text] ;
        // replace the found tag with a space
        //(you can filter multi-spaces out later if you wish)
        html = [html stringByReplacingOccurrencesOfString:
                [NSString stringWithFormat:@"%@>", text]
                                               withString:@""];
    }
    return html;
}

-(void)refreshWebView {
    [wkWebView reload];
}


25、检测iOS越狱

    if ([self isJailBreak] || [self isJailBreak2] || [self isJailBreak3]) {
        [SVProgressHUD showInfoWithStatus:@"该设备已越狱,请谨慎使用!"];
        [self performSelector:@selector(dismiss) withObject:nil afterDelay:1.5f];
    }
#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])
#define USER_APP_PATH                 @"/User/Applications/"
const char* jailbreak_tool_pathes[] = {
    "/Applications/Cydia.app",
    "/Library/MobileSubstrate/MobileSubstrate.dylib",
    "/bin/bash",
    "/usr/sbin/sshd",
    "/etc/apt"
};
- (BOOL)isJailBreak
{
    for (int i=0; i<ARRAY_SIZE(jailbreak_tool_pathes); i++) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:jailbreak_tool_pathes[i]]]) {
            NSLog(@"The device is jail broken!");
            return YES;
        }
    }
    NSLog(@"The device is NOT jail broken!");
    return NO;
}

- (BOOL)isJailBreak2
{
    if ([[NSFileManager defaultManager] fileExistsAtPath:USER_APP_PATH]) {
        NSLog(@"The device is jail broken!");
        NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APP_PATH error:nil];
        NSLog(@"applist = %@", applist);
        return YES;
    }
    NSLog(@"The device is NOT jail broken!");
    return NO;
}
- (BOOL)isJailBreak3
{
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
        NSLog(@"The device is jail broken!");
        return YES;
    }
    NSLog(@"The device is NOT jail broken!");
    return NO;
}

26、iOS检测是否被调试

#import <sys/sysctl.h>
if (checkDebugger()) {
        asm("mov X0,#0\n"
            "mov w16,#1\n"
            "svc #0x80"
            );
    }
bool checkDebugger(){
    //控制码
    int name[4];//放字节码-查询信息
    name[0] = CTL_KERN;//内核查看
    name[1] = KERN_PROC;//查询进程
    name[2] = KERN_PROC_PID; //通过进程id查进程
    name[3] = getpid();//拿到自己进程的id
    //查询结果
    struct kinfo_proc info;//进程查询信息结果
    size_t info_size = sizeof(info);//结构体大小
    int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
    assert(error == 0);//0就是没有错误
    
    //结果解析 p_flag的第12位为1就是有调试
    //p_flag 与 P_TRACED =0 就是有调试
    return ((info.kp_proc.p_flag & P_TRACED) !=0);
    
}

27、反动态注入

对于 insert_dylib,我们可以通过在 Xcode的Build Settings中找到“Other Linker Flags”在其中加上

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

28、iOS代码混淆

29、集成极光im时报错

Remove the build settings from the target

pod install 报这个错误

解决办法 target Build Settings -> Other linker flags -> double click . Add $(inherited)

添加prefix head文件 解决办法 target Build Settings -> prefix head -> ${SRCROOT}/PrefixHeader.pch

30、极光推送启动跳转

NSDictionary *pushInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         NSString *custom = [pushInfo valueForKey:@"custom"];
           NSString *jusername = [custom valueForKey:@"jusername"];
           UIViewController* currentView = self.window.visibleViewController;
           if ([[currentView class] isSubclassOfClass:[HomeViewController class]]) {
               if (jusername) {
                   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                       [(HomeViewController*)currentView sendMessageToUsername:jusername];
                   });
               }
           }
    });

点击消息弹框跳转

// iOS 10 Support  app点击推送消息时
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    // Required
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    NSString *tableid = [userInfo valueForKey:@"tableid"]; //推送显示的内容
    
    NSString *tablename = [userInfo valueForKey:@"tablename"];
    
    NSString *custom = [userInfo valueForKey:@"custom"];
    NSString *jusername = [custom valueForKey:@"jusername"];
    
    
    UIViewController* currentView = self.window.visibleViewController;
    if ([[currentView class] isSubclassOfClass:[HomeViewController class]] || [[currentView class] isSubclassOfClass:[GroupTableViewController class]] || [[currentView class] isSubclassOfClass:[ConversationTableViewController class]]) {
        if (tableid) {
            [(HomeViewController*)currentView goToMssageViewControllerWith:tableid and:tablename];
        }
        if (jusername) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [(HomeViewController*)currentView sendMessageToUsername:jusername];
            });
        }
    }

    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [JPUSHService handleRemoteNotification:userInfo];
    }
    completionHandler();  // 系统要求执行这个方法
}

31、UITableViewController的使用 uitableviewcontroller中使用

[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"wbcellconversation"];
[self.tableView registerNib:[UINib nibWithNibName:@"GroupTableViewCell"bundle:nil]forCellReuseIdentifier:@"wbcellconversation"];

cell注册

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     GroupTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"wbcellconversation" forIndexPath:indexPath];
    cell.tTextLabel.text = @"";
    cell.bTextLabel.text=@"";
    cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
    return cell;
}

去除底部网格

UIView *v = [[UIView alloc] initWithFrame:CGRectZero];
[self.tableView setTableFooterView:v];

点击效果取消

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
}

黑暗模式下cell去除分割线

#import "GroupTableViewCell.h"

@implementation GroupTableViewCell
- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
    self.hLabel.backgroundColor = [UIColor redColor] ;
}
- (void)addSubview:(UIView *)view
{
    if (![view isKindOfClass:[NSClassFromString(@"_UITableViewCellSeparatorView") class]] && view)
          [super addSubview:view];
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
    [super setHighlighted:highlighted animated:animated];
    //自己的label及自己要设置的label背景色
    self.hLabel.backgroundColor = [UIColor redColor] ;
}
@end
32、iOS自动签名证书automatically manage signing怎么更新provisioning profile到期时间

1.Xcode切到相应的target的General tab下,然后点击(!)按钮展示provisioning profile文件详情,然后拖动左上角的profile图标到桌面获取provisioning profile文件名。

2.打开~/Library/MobileDevice/Provisioning Profiles目录,然后删除相同文件名的provisioning profile文件。

33、ipv6适配

ipv6审核被拒绝的解决方案 ipv6测试 在线代理

34、iOS上架被拒记录

1、项目页面中无用按钮或者空白页面未移出(微信登录按钮、无效路由)

2、项目中含有测试图片及demo、test等测试数据或者数据内容、功能重复

3、提供展示图片和app本身页面不符(6.5、5.5)

4、app内有接口报错或者弹出更新提示

5、提供测试账号错误

6、项目中没有支付,苹果检测到支付api(支付宝)

7、NAT64(ipv6)环境下访问不通(接口)

8、定位、拍照、获取语音授权提示不够详细

  • Privacy - Camera Usage Description 我们需要在扫码,上传照片等服务中使用您的相机
  • Privacy - Microphone Usage Description 访问您的麦克风,进行语音录制
  • Privacy - Contacts Usage Description 访问您的通讯录,为您匹配好友
  • Privacy - Location Always Usage Description 我们需要获取您的位置,以便根据您的位置定位到距您最近的园区
  • Privacy - Photo Library Usage Description 访问您的相册,进行照片上传
  • Privacy - Location When In Use Usage Description 我们需要获取您的位置,以便根据您的位置定位到距您最近的园区

9、iOS上线却伤出口合规证明 App Uses Non-Exempt Encryption NO

10、录音功能提供哪里使用了录音视频,后台语音未关闭

持续更新中...