iOS文件读写与预览

参考文章:

沙盒(sandbox)(官方文档

  • iOS沙盒主要包含以下三个文件夹:

    image
  • 获取沙盒路径
1
2
//沙盒根目录
NSString *homeDir = NSHomeDirectory();

注:每次编译代码会生成新的沙盒,注意是编译不是启动,所以模拟器或者真机运行你每次运行所得到的沙盒路径都是不一样,就是上面提到的标识符不一样,正式版app真机的话启动杀死,不会生成新的沙盒

1)、Documents

保存应用程序运行时生成的需要持久化数据,iTunes会自动备份该目录。

苹果公司建议将程序中建立的活在程序浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录。

1
2
// 获取Documents目录
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

2)、Library

存储程序的默认设置和其他状态信息,iTunes会自动备份该文件目录

  • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出时删除,通常存放体积比较大,但并不是很重要的资源
  • Library/Preferences:保存应用的所有偏好设置,iOS的Setting(设置)应用会在该目录中查找应用的设置信息,iTunes会自动备份该目录。

    ——PS:如果你想对偏好设置进行相应的操作,应该使用NSUserDefaults类来取得和设置应用程序的偏好,而不是直接创建偏好设置文件。
  • 包含两个文件夹

    image
1
2
3
4
5
6
7
8
9
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];

// 获取Library/Caches目录
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

// 获取Library/Preferences目录
NSString *prePath = [NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES) lastObject];
//通常情况下,Preferences由系统维护,我们很少去操作TA

3)、tmp

保存应用程序运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除,应用没有运行时,系统也有可能会清除该目录下的文件,iTunes不会同步该目录,你的iPhone重启时,该目录下的文件会被删除。

数据存储的几种方式

1)、property list(属性列表)

只能存储系统自带的数据类型,一般实际开发中存储字典、数组,自定义的模型无法进行存储。

1
2
3
4
5
6
7
8
9
#define kUserPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"local.plist"]

//iOS写入property list
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"小明" forKey:@"name"];
[dic writeToFile:kUserPath atomically:YES];

NSMutableDictionary *localDic = [[NSMutableDictionary alloc] initWithContentsOfFile:kUserPath];
NSLog(@"localDic = %@",localDic);

2)、Preference(偏好设置):NSUserDefaults

只能存储系统自带的数据类型,自定义的对象无法存储。

偏好设置好处:

1.不需要关心文件名(不需要设置路径)

2.键值对存储(账号相关信息) 对象存储

底层实现原理就是封装了一个字典

1
2
3
4
5
6
7
8
9
// Preference(偏好设置)
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//放到缓存里,并不会马上放到文件里面
[userDefaults setObject:@"123" forKey:@"account"]; //对象
[userDefaults setObject:@"123456" forKey:@"pwd"];
//BOOL类型
[userDefaults setBool:YES forKey:@"status"];
//在ios7 默认不会马上跟硬盘同步 同步操作 起到立即存储的作用
[userDefaults synchronize];

3)、文件读写

3.1 简单对象写入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    //字符串写入文件
//获取document路径
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPathStr = [array lastObject];
//构造字符串文件的存储路径
NSString *strPath = [docPathStr stringByAppendingPathComponent:@"/text.txt"];
//构造字符串对象
NSString *contant = @"Hello, world!";
//将字符串写入到文件
[contant writeToFile:strPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
//数组写入文件
NSString *arrayPath = [docPathStr stringByAppendingString:@"/arry.txt"];
NSArray *arr = @[ @"Jobs", @"Cook", @"Apple" ];
[arr writeToFile:arrayPath atomically:YES];
//字典写入文件
NSString *dicPath = [docPathStr stringByAppendingString:@"/dic.txt"];
NSDictionary *dic = @{ @"name" : @"Jobs", @"sex" : @"男" };
[dic writeToFile:dicPath atomically:YES];
//Data写入文件
UIImage *img = [UIImage imageNamed:@"avatar1"];
NSString *dataPath = [docPathStr stringByAppendingPathComponent:@"data.txt"];
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:dataPath atomically:YES];

#pragma mark - NSFileManager
NSError *error;
//文件管理,创建单例对象
NSFileManager *manger = [NSFileManager defaultManager];
//创建文件夹
NSString *filePath = [docPathStr stringByAppendingString:@"/test1"];
[manger createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
//可以创建,移动,复制,删除文件
//创建文件
NSString *filePath1 = [filePath stringByAppendingString:@"/test.png"];
[manger createFileAtPath:filePath1 contents:data attributes:nil];
// 复制文件
NSString *filePath2 = [docPathStr stringByAppendingString:@"/test1/test2.png"];
[manger copyItemAtPath:filePath1 toPath:filePath2 error:&error];
//移动
NSString *filePath3 = [docPathStr stringByAppendingString:@"/test2"];
[manger createDirectoryAtPath:filePath3 withIntermediateDirectories:YES attributes:nil error:&error];
NSString *filePath4 = [filePath3 stringByAppendingString:@"/test4.png"];
[manger moveItemAtPath:filePath1 toPath:filePath4 error:&error];
//删除
[manger removeItemAtPath:filePath4 error:&error];
//判断文件是否存在
BOOL isExist = [manger fileExistsAtPath:filePath4];
NSLog(@"isExist = %d",isExist);

// 获取目录列里所有文件名
NSArray *fileArr = [manger subpathsOfDirectoryAtPath: docPathStr error:nil];
NSLog(@"fileArr = %@",fileArr);
NSArray *fileArr2 = [manger subpathsAtPath: docPathStr ];
NSLog(@"fileArr2 = %@",fileArr2);

3.2、NSKeyedArchiver(归档)+ 文件读写

该方式一般都是保存自定义对象的时候使用.因为plist文件不能够保存自定义对象.

如果一个字典当中保存有自定义对象,如果把这个字典写入到文件当中,它是不会生成plist文件的.

  • 复杂对象写入文件的过程(复杂对象->归档->NSData->writeToFile)
  • 从文件中读取出复杂对象过程(读取文件->NSData->反归档->复杂对象)

    1. 首先,复杂对象所属的类要遵守
    1. 其次,实现协议中的两个方法:

      • -(void)encodeWithCoder:(NSCoder *)aCoder; //序列化
      • -(id)initWithCoder:(NSCoder *)aDecoder; //反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface UserModel : NSObject <NSCoding>

@property (nonatomic, assign) int ID;
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *pwd;
@property(nonatomic,assign)int age;

@end

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
self.ID = [aDecoder decodeIntForKey:@"ID"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.pwd = [aDecoder decodeObjectForKey:@"pwd"];
self.age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeInt:self.ID forKey:@"ID"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.pwd forKey:@"pwd"];
[aCoder encodeInt:self.age forKey:@"age"];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
UserModel *userModel = [[UserModel alloc] init];
userModel.ID = 10;
userModel.name = @"Jobs";
userModel.pwd = @"123456";
userModel.age = 26;

//方法一:
// //归档保存数据
// NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
// NSString *path=[documentPath stringByAppendingPathComponent:@"UserInfo.plist"];
// [NSKeyedArchiver archiveRootObject:userModel toFile:path];
//
// // 反归档
// UserModel *localUser = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

//方法二:
// 准备一个NSMutableData, 用于保存归档后的对象
NSMutableData *data = [NSMutableData data];
// 创建归档工具
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
// 归档
[archiver encodeObject:userModel forKey:@"user"];
// 结束
[archiver finishEncoding];
NSString *mulDataPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dataPath = [mulDataPath stringByAppendingString:@"/archiver.plist"];
[data writeToFile:dataPath atomically:YES];

//将对象反归档
// 1.从文件中读取二进制数据
NSData *readData = [NSData dataWithContentsOfFile:dataPath];
// 2.定义反归档工具类
NSKeyedUnarchiver *unAchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:readData];
// 3.对对象进行解码
UserModel *localUser1 = [unAchiver decodeObjectForKey:@"user"];
// 4.完成反归档
[unAchiver finishDecoding];

注:归档不是数据持久化手段,而是为文件读写做准备。文件读写才是数据持久化的方式。

Office等文件预览

方法一:UIDocumentInteractionController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//遵循代理方法`<UIDocumentInteractionControllerDelegate>`
- (void)readDocWithDucument1 {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];



// copy 到沙盒的cache 路径下,否则不能用第三方app 打开
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
cachePath = [cachePath stringByAppendingPathComponent:@"temp.docx"];
NSURL *cacheUrl = [NSURL fileURLWithPath:cachePath];

NSFileManager *fileManger = [NSFileManager defaultManager];
NSError *error = nil;
[fileManger copyItemAtURL:url toURL:cacheUrl error:&error];


_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:cacheUrl];
_documentInteractionController.delegate = self;
// [_documentInteractionController presentPreviewAnimated:YES];
[_documentInteractionController presentOptionsMenuFromRect:CGRectZero inView:self.view animated:YES];
}
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
return self;
}
- (UIView *)documentInteractionControllerViewForPreview:(UIDocumentInteractionController *)controller {
return self.view;
}
- (CGRect)documentInteractionControllerRectForPreview:(UIDocumentInteractionController *)controller {
return self.view.bounds;
}

方法二:QLPreviewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)quickLook {
QLPreviewController *preVC = [[QLPreviewController alloc] init];
preVC.dataSource = self;
[self presentViewController:preVC animated:YES completion:nil];
}
#pragma mark - QLPreviewControllerDataSource 代理方法
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller {
return 1;
}

- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];
return url;
}

方法三:UIWebView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)readDocfile {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];

UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.frame];
webView.delegate = self;
webView.multipleTouchEnabled = YES;
webView.scalesPageToFit = YES;

NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

[self.view addSubview:webView];
}

时间&日期 NSDate、NSDateFormatter、NSCalendar

一、获取当前时间

1
2
3
4
5
6
7
8
//获取当前时间
- (NSString *)currentDateStr{
NSDate *currentDate = [NSDate date];//获取当前时间,日期
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];// 创建一个时间格式化对象
[dateFormatter setDateFormat:@"YYYY/MM/dd hh:mm:ss SS "];//设定时间格式,这里可以设置成自己需要的格式
NSString *dateString = [dateFormatter stringFromDate:currentDate];//将时间转化成字符串
return dateString;
}

二、获取当前时间戳

1
2
3
4
5
6
7
//获取当前时间戳
- (NSString *)currentTimeStr{
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
NSTimeInterval time=[date timeIntervalSince1970]*1000;// *1000 是精确到毫秒,不乘就是精确到秒
NSString *timeString = [NSString stringWithFormat:@"%.0f", time];
return timeString;
}

三、时间戳转时间

1
2
3
4
5
6
7
8
9
10
// 时间戳转时间,时间戳为13位是精确到毫秒的,10位精确到秒
- (NSString *)getDateStringWithTimeStr:(NSString *)str{
NSTimeInterval time=[str doubleValue]/1000;//传入的时间戳str如果是精确到毫秒的记得要/1000
NSDate *detailDate=[NSDate dateWithTimeIntervalSince1970:time];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; //实例化一个NSDateFormatter对象
//设定时间格式,这里可以设置成自己需要的格式
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss SS"];
NSString *currentDateStr = [dateFormatter stringFromDate: detailDate];
return currentDateStr;
}

四、字符串转时间戳

1
2
3
4
5
6
7
8
//字符串转时间戳 如:2017-4-10 17:15:10
- (NSString *)getTimeStrWithString:(NSString *)str{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];// 创建一个时间格式化对象
[dateFormatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //设定时间的格式
NSDate *tempDate = [dateFormatter dateFromString:str];//将字符串转换为时间对象
NSString *timeStr = [NSString stringWithFormat:@"%ld", (long)[tempDate timeIntervalSince1970]*1000];//字符串转成时间戳,精确到毫秒*1000
return timeStr;
}

五、获取NSDate的年月日

1.NSDateFormat获取
1
2
3
4
5
6
7
8
9
10
11
NSDate *date =[NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];

[formatter setDateFormat:@"yyyy"];
NSInteger currentYear=[[formatter stringFromDate:date] integerValue];
[formatter setDateFormat:@"MM"];
NSInteger currentMonth=[[formatter stringFromDate:date]integerValue];
[formatter setDateFormat:@"dd"];
NSInteger currentDay=[[formatter stringFromDate:date] integerValue];

NSLog(@"currentDate = %@ ,year = %ld ,month=%ld, day=%ld",date,currentYear,currentMonth,currentDay);
2.NSDateComponents获取
1
2
3
4
5
6
7
8
NSDate  *currentDate = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:currentDate];

NSInteger year=[components year];
NSInteger month=[components month];
NSInteger day=[components day];
NSLog(@"currentDate = %@ ,year = %ld ,month=%ld, day=%ld",currentDate,year,month,day);

iOS 录音功能的实现

1.初始化录音设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
AVAudioSession * session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[session setActive:YES error:nil];
//录音设置
NSMutableDictionary * recordSetting = [[NSMutableDictionary alloc]init];
//设置录音格式
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
//设置录音采样率(HZ)
[recordSetting setValue:[NSNumber numberWithFloat:4000] forKey:AVSampleRateKey];
//录音通道数
[recordSetting setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
//线性采样位数
[recordSetting setValue:[NSNumber numberWithInt:8] forKey:AVLinearPCMBitDepthKey];
//录音的质量
[recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
//获取沙盒路径 作为存储录音文件的路径
NSString * strUrl = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSLog(@"path = %@",strUrl);
//创建url
NSURL * url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/voice.aac",strUrl]];
self.urlPlay = url;
NSError * error ;
//初始化AVAudioRecorder
self.recorder = [[AVAudioRecorder alloc]initWithURL:url settings:recordSetting error:&error];
//开启音量监测
self.recorder.meteringEnabled = YES;
self.recorder.delegate = self;
if(error){
NSLog(@"创建录音对象时发生错误,错误信息:%@",error.localizedDescription);
}

2.开始录音

1
2
3
4
5
if([_recorder prepareToRecord]){
        //开始
        [_recorder record];
        NSLog(@"开始录音");
    }

3.录音结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//获取当前录音时长
    float voiceSize = self.recorder.currentTime;
    
    NSLog(@"录音时长 = %f",voiceSize);
    
    if(voiceSize < 3){
        [self.recorder deleteRecording];
        UIAlertView * altView = [[UIAlertView alloc]initWithTitle:nil
                                                          message:@"时长小于3秒,重新录制" delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
        [altView show];
        
        [self performSelector:@selector(performDismiss:) withObject:altView afterDelay:1.5];
        
    }else if (voiceSize > 60){
        
        [self.recorder deleteRecording];
        UIAlertView * altView = [[UIAlertView alloc]initWithTitle:nil
                                                          message:@"时长大于1分钟,重新录制" delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
        [altView show];
        
        [self performSelector:@selector(performDismiss:) withObject:altView afterDelay:1.5];
    }
    
    [self.recorder stop];

获取当前iOS设备的IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#import <arpa/inet.h>
#import <ifaddrs.h>
+ (NSString *)getIPAddress {
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
NSString *wifiAddress = nil;
NSString *cellAddress = nil;

// retrieve the current interfaces - returns 0 on success
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL) {
sa_family_t sa_type = temp_addr->ifa_addr->sa_family;
if(sa_type == AF_INET || sa_type == AF_INET6) {
NSString *name = [NSString stringWithUTF8String:temp_addr->ifa_name];
NSString *addr = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; // pdp_ip0
NSLog(@"NAME: \"%@\" addr: %@", name, addr); // see for yourself

if([name isEqualToString:@"en0"]) {
// Interface is the wifi connection on the iPhone
wifiAddress = addr;
} else
if([name isEqualToString:@"pdp_ip0"]) {
// Interface is the cell connection on the iPhone
cellAddress = addr;
}
}
temp_addr = temp_addr->ifa_next;
}
// Free memory
freeifaddrs(interfaces);
}
NSString *addr = wifiAddress ? wifiAddress : cellAddress;
return addr ? addr : @"0.0.0.0";
}

iOS键盘弹出与隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[center addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardWillHideNotification object:nil];
// 键盘弹出触发该方法
- (void)keyboardDidShow:(NSNotification *)aNotification {
NSLog(@"键盘弹出");
//获取键盘的高度
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [aValue CGRectValue];
int height = keyboardRect.size.height;
}
// 键盘隐藏触发该方法
- (void)keyboardDidHide:(NSNotification *)aNotification {
NSLog(@"键盘隐藏");
}

- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

iOS 配置

iOS 配置

修改状态栏颜色

1
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:NO];

关闭强制验证https

1
2
3
4
5
<!-- 关闭强制验证https -->
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

iOS 国际化如何指定默认语言

1
2
3
<!-- 指定默认语言 -->
<key>CFBundleDevelopmentRegion</key>
<string>zh-Hans</string>

iOS权限设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!-- 相册 -->   
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<!-- 相机 -->
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相机</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能访问麦克风</string>
<!-- 位置 -->
<key>NSLocationUsageDescription</key>
<string>App需要您的同意,才能访问位置</string>
<!-- 在使用期间访问位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>App需要您的同意,才能在使用期间访问位置</string>
<!-- 始终访问位置 -->
<key>NSLocationAlwaysUsageDescription</key>
<string>App需要您的同意,才能始终访问位置</string>
<!-- 日历 -->
<key>NSCalendarsUsageDescription</key>
<string>App需要您的同意,才能访问日历</string>
<!-- 提醒事项 -->
<key>NSRemindersUsageDescription</key>
<string>App需要您的同意,才能访问提醒事项</string>
<!-- 运动与健身 -->
<key>NSMotionUsageDescription</key>
<string>App需要您的同意,才能访问运动与健身</string>
<!-- 健康更新 -->
<key>NSHealthUpdateUsageDescription</key>
<string>App需要您的同意,才能访问健康更新 </string>
<!-- 健康分享 -->
<key>NSHealthShareUsageDescription</key>
<string>App需要您的同意,才能访问健康分享</string>
<!-- 蓝牙 -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>App需要您的同意,才能访问蓝牙</string>
<!-- 媒体资料库 -->
<key>NSAppleMusicUsageDescription</key>
<string>App需要您的同意,才能访问媒体资料库</string>

Mac 安装 Node

mac 安装 Node.js

1、Node.js是什么?

Node.js官网:
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。 Node.js 使用高效、轻量级的事件驱动、非阻塞 I/O 模型。它的包生态系统,npm,是目前世界上最大的开源库生态系统。

维基百科:
Node.js 是一个能够在服务器端运行 JavaScript 的 开放源代码、跨平台 JavaScript 运行环境。

简单的说 Node.js 就是运行在服务端的 JavaScript。使JavaScript 可以脱离浏览器运行;

2、安装Node.js

方式一:通过NVM安装指定版本的node

  1. 安装NVM(前提是你安装了homebrew)

    1
    $ brew install nvm
  2. 在终端使用 NVM命令

    $ cd ~

    $ vim .bash_profile

    在 .bash_profile文件中添加

    export NVM_DIR=~/.nvm 
    source $(brew --prefix nvm)/nvm.sh
    
    然后重新source
    ` $ source .bash_profile `
    **如果你安装了zsh,还需要配置.zshrc**
    `$ vim .zshrc`
    export PATH=/usr/local/bin:$PATH
    export NVM_DIR="$HOME/.nvm"
    . "$(brew --prefix nvm)/nvm.sh"
    
  3. 使用nvm安装node
    查看所有的node可用版本

    $ nvm ls-remote

    下载你想要的版本

    $ nvm install xxx

    使用指定版本的node

    $ nvm use xxx

    每次启动终端都使用该版本的node

    $ nvm alias default xxx

    卸载指定版本号

    $ nvm uninstall xxx
  4. 安装完成检测 ,输出版本号证明安装成功

    $ node -v

    $ npm -v