Xcode9新特性/iOS11适配-收编

更新中......(部分省略)

10.19已更新
10.20已更新
11.4 已更新

Xcode9新特性

1.启动多个模拟器

方案一

  • Xcode选择不同的模拟器,启动新的模拟器。

方案二

  • 从模拟器 menu 上的 Hardware---Device,选择新的模拟器启动。
    u=2475746809,941184978&fm=173&s=00095C320EB7729A8761914D0300B0F1&w=640&h=455&img.JPEG

2.WiFi无线安装App

  • 操作的步骤如下(注意:必须是iOS11iPhoneMac 处于同一 WiFi 下。):
    1. 接线连接iPhone。点选Xcode menuWindow,选择Devices and Simulators
    2. u=2364080816,4230627080&fm=173&s=C801CC1A215C6DCC0ADDC9DB0100C0B2&w=640&h=399&img.JPEG
    3. 勾选 Connect via network,连线到iPhone。如果连线顺利,iPhone 旁会出现如下选项。
    4. u=1977584014,4032251507&fm=173&s=E010693A53BFF9EB087D98C30100A0B0&w=640&h=489&img.JPEG
    5. 如果刚刚没有出现网路图示,或是拔线后,iPhone不理我们,在Devices and Simulators视窗显示Disconnected。此时Xcode找不到iPhone,也就无法顺利安装App
    6. u=3864239553,1403343358&fm=173&s=70C2D81A2F7A5E09104780D2030010BA&w=488&h=262&img.JPEG
    7. Devices and Simulators视窗上选择iPhone后,从右键选单点选Connect via IP Address,输入iPhoneIP后,点选Connect连线。
    8. u=4111917007,1633424150&fm=173&s=3076C83297C875414EEF1DCC0300E0B0&w=639&h=263&img.JPEG
    9. IP 地址,则可从iPhone设定AppWiFi页面查询。

3.错误提示

  • 错误可以多行显示,Fix会自动出现,点选即可修正问题。
  • u=3931270588,1281757378&fm=173&s=0FC4FD17CFE47C03565591F40300503E&w=640&h=187&img.JPEG

4.按住 command+可将代码放大,command-可将代码縮小。

5.提示选单

  • 將鼠标移到 { }( ) 或是 classfunciffor 等关键字,按住 command 建,Xcode 將表示对应的 classfunctioniffor 代码块。
    • 鼠标移到方法上,按 command建,会出现提示选单。
    • Snip20171017_1.png
    • 按住 command 建再点击Jump To Definition,跳到方法的定义。
    • 如果想利用快速建跳到定义,按住 command + control 再点击。
    • 回到以前的样子:
    • 20170922150758282.png
  • 介绍:
    • Jump to Definition 跳转到定义的方法
    • Fold 折叠方法
    • Rename 重命名

6.swift重新命名、自动补全协议的required方法,Extract Method自动提取方法

  • swift重新命名
  • u=4067943835,489730897&fm=173&s=811EED3280E079015255CCC60300F0B3&w=639&h=221&img.JPEG
  • u=2013514408,4020135435&fm=173&s=401AE032515E45CC4E5121DA000080B2&w=639&h=528&img.JPEG
  • u=3062817103,696550176&fm=173&s=4018EC32511ED5CC1E5160DB0000D0B2&w=640&h=540&img.JPEG
  • Extract Method自动提取方法
  • Snip20171018_1.png
  • Snip20171018_2.png

7.深度整合 github

  • Xcode的代码上传到github
    • 以前:先在github上建立远程仓库,然后再在Xcode中设置远程仓库,最后执行push上传。
    • 现在:先本地git初始化代码,进行git管理。
    • 再在打开项目的工具条选择第二个图标,选择Create Remote...
    • u=458925110,1703897494&fm=173&s=F190CB38114E7549106984DE0300D0B3&w=640&h=468&img.JPEG
    • 填写信息
    • u=3554539748,1548131435&fm=173&s=08887432119FE5CE4A6100DB0000D0B3&w=640&h=548&img.JPEG

8.给顏色添加名字

  • Assets.xcassets 现在除了加入图片,还可以加入有名字的颜色,方便我们之后在 Storyboard 或程式里使用。
  • u=3651431358,4056156254&fm=173&s=81025D32110F754D02D0B4C80100F0B3&w=640&h=654&img.JPEG
  • 可以在Storyboard控件选择颜色时,在Named Colors下看到我們取的顏色名字。
  • [UIColor colorNamed:@"Color"]iOS11新方法

9.新的编译系统

  • 新的编译器已经用Swift 重写了,性能得到了很大的提升。
  • 1452146-1cd1541b5b6e291b.jpg

10.FinderSimulator 共享文件

  • Simulator 具有 Finder 扩展,我们可以直接从 Finder 窗口共享文件给 Simulator.
  • Snip20171018_5.png

11.折叠代码

20170926164432230.jpeg
  • 菜单的Fold,用来折叠方法
  • command+option+左键,快速折叠方法

12.Folder和Group的同步问题

  • Xcode9中重名命Folder,Finder中的也同步的改变了,我们之前建议一个虚拟的group,并不会在对应的文件夹中建立真实的目录.Xcode9中,默认行为改变了, 变成了会建立对应的真实文件夹, 如果你需要像之前那样只是建立虚拟的group, 选择New Group without Folder 即可!
  • 苹果给出了标识来区分的, 虚拟的左下角有个小的三角形.

iOS11适配

1.定位相关

  • 如果原来申请的权限是始终允许NSLocationAlwaysUsageDescription,那么需要在保留原来的key的基础上增加NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUsageDescription

2.系统相册相关(待验证)

  • iOS11之前相册对应的keyNSPhotoLibraryUsageDescription,iOS11对应的KeyNSPhotoLibraryAddUsageDescription。同定位的Key一样,由于key没有兼容性,所以需要保留原key以兼容iOS10及之前版本。

3.关于UIScrollView初始位置变化的问题

  • iOS11废弃了UIViewControllerautomaticallyAdjustsScrollViewInsets属性,位置需要手动调整。
  • iOS11中为UIScrollView新增了contentInsetAdjustmentBehavior属性
// Swift
if #available(iOS 11.0, *) {
    UIScrollView.appearance().contentInsetAdjustmentBehavior = .never
}

// OC
if (@available(iOS 11.0, *)) {
    [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    [UITableView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    [UICollectionView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    [UIWebView appearance].scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    //底下这三句是解决mjrefresh 上拉偏移的bug
    [UITableView appearance].estimatedRowHeight = 0;
    [UITableView appearance].estimatedSectionHeaderHeight = 0;
    [UITableView appearance].estimatedSectionFooterHeight = 0;
} else {
    // Fallback on earlier versions
}
    
// 或者
#ifdef __IPHONE_11_0   
#endif
  
  
    if (UIScreen.mainScreen.bounds.size.height == 812) {
      NSLog(@"this is iPhone X");
    }
}

4.UIVisualEffectView相关的崩溃

  • iOS11之前可以将UIView直接加到(addSubviewUIVisualEffectView上面,
  • iOS11上面这么做会导致crash
    • 正确的姿势是:将UIView addSubviewUIVisualEffectViewcontentView上。

5.关于上传商店相关改变

  • 之前没有1024*1024icon,同样可以提交商店审核,但是现在不行了。你会在用Application Loader上传完成后收到一个warning,但是在提交审核(包括beta测试版本)时被告知不允许提交。
  • 而且注意,这个1024的图片一定要去掉alpha通道。可以在github上搜索Alpha-Channel-Remover,用这个工具去掉alpha通道。
  • Assets.xcassets添加。

6.针对 iPhoneX启动图 适配

  • 如果之前的APPiPhoneX屏幕没填充满,上下有黑色区域,应该是你的app之前未用LaunchScreen.Storyboard作为启动页面,可以使用LaunchScreen来当做入场页面,这样APP才会自动适配为iPhoneX的大小。或者修改Assets中的LaunchImage,添加iPhoneX的尺寸图如下(1125*2436)。

7.iOS11 安全区域 适配

  • 安全区域定义了view中可视区域的部分,帮助我们将view放置在整个屏幕的可视的部分。
  • iPhone X 取消了 Home键,实现了高屏占比。所以默认 View 的区域是全屏幕屏幕,四周有圆角、顶部有“刘海”、底部有手势区域
  • 导航栏高度是 44 + 44
  • 安全区域: 顶部从44到距离底部34 为安全区域
  • 1944396-07171b00b426320f.png
  • Safe Area要求最低支持iOS9.0(待考证)
iOS11的UIViewController和UIView新加了-(void)viewSafeAreaInsetsDidChange方法,当安全区域改变后该方法会被调用。然后在该方法中根据safeAreaInses属性更新子视图中控件的布局位置。
当然如果你要改变一个UIViewController的safeAreaInsets值, 可以通过设置addtionalSafeAreaInsets属性来实现, 例如你要自定义一些特殊的样式时。
需要注意的是 viewSafeAreaInsetsDidChange 在UIViewController 中第一次调用的时间是在 -(void)viewWillAppear:(BOOL)animated 调用之后, 在 - (void)viewWillLayoutSubviews 调用之前。所以可以在 viewWillAppear 里设置受影响的页面的 addtionalSafeAreaInsets 属性。
  
- (void)viewSafeAreaInsetsDidChange{
    [super viewSafeAreaInsetsDidChange];
    if (@available(iOS 11.0, *)) {
        NSLog(@"safeAreaInset list= %@",NSStringFromUIEdgeInsets(self.view.safeAreaInsets));
        NSLog(@"safeAreaLayout list= %@",self.view.safeAreaLayoutGuide);
    }
}

8.导航栏高度的变化

  • iOS11之前导航栏默认高度为64pt(这里高度指statusBar + NavigationBar)

  • iOS11之后如果设置了prefersLargeTitles = YES则为96pt,默认情况下还是64pt,但在iPhoneX上由于刘海的出现statusBar由以前的20pt变成了44pt,所以iPhoneX上高度变为88pt,如果项目里隐藏了导航栏加了自定义按钮之类的,这里需要注意适配一下。

  • iOS11导航栏多了一个LargeTitleView,专门显示大字标题用的,整个导航栏的高度达到了96p,这不包括状态栏的高度,也就是说,整个app顶部高度达到了116p,其中statusbar=20title=44largetitle=52,不过默认是64p;当然,iPhoneX的高度会更高点,如果不显示大字标题,顶部的高度也达到了88statusbar=44title=44,如果显示大字标题,则高度变成了140statusbar=44title=44largetitle=52,也就是说,iPhoneX的刘海高度为44p,大字标题如下图

  • 968977-89a88f618da51832.png
  • 968977-b19a09df0a51bf50.png

9. UITableView

  • ios11里默认启用Self-SizingtableView不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个,滑动时,tableView不停地得到新的cell,更新自己的contenSize,在滑到最后的时候,会得到正确的contenSize,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension,如果不想开启的话可以用下面的方法关闭.
tableView.estimatedRowHeight = 0;
tableView.estimatedSectionHeaderHeight = 0;
tableView.estimatedSectionFooterHeight = 0;

官方视频总结

1.iOS9 and later

Snip20171019_7.png

Snip20171019_8.png

Snip20171019_9.png

2.

Snip20171019_12.png

Snip20171019_11.png

Snip20171019_10.png

3.

Snip20171019_14.png

Snip20171019_13.png

4.

Snip20171019_15.png

Snip20171019_16.png

Snip20171019_18.png

Snip20171019_21.png

Snip20171019_20.png

5.新方法介绍

UIBarButtonItem的方法
largeContentSizeImage   用于视觉障碍的用户显示的图像。
landscapeImagePhone     用于以横向方向表示项目的图像。
navigationBar.prefersLargeTitles 标题是否应以大格式显示。
navigationItem.largeTitleDisplayMode 显示导航栏的标题时使用的模式。
navigationItem.searchController 搜索控制器集成到您的导航界面
navigationItem.hidesSearchBarWhenScrolling  在滚动任何底层内容时是否隐藏综合搜索栏。
UIView的方法
layoutMarginsGuide 表示视图边缘的布局指南。(iOS9的方法)
directionalLayoutMargins 在视图中布局内容时使用的默认间距,同时考虑到当前的语言方向。
控制器的方法
viewRespectsSystemMinimumLayoutMargins 如果你设置该属性为"false",你就可以改变你的layout margins为任意你想设置的值,包括0
...

自己适配问题

1.导航条

  • [[UIBarButtonItem alloc] initWithCustomView:button];,这里button是使用XIB加载。
  • 使用以上方法时,注意button要使用autolayout布局,保证父视图的宽高。否则无响应事件
  • 还可通过在对应的view中实现- intrinsicContentSize方法
- (CGSize)intrinsicContentSize {
    return UILayoutFittingExpandedSize;
}

2.TZImagePickerController iPhoneX适配问题

  • iPhoneX导航条过长导致cell被覆盖问题
Snip20171020_3.png

Snip20171020_1.png
  • iPhoneX图片浏览的工具条等位置问题
Snip20171020_4.png

3.iPhoneX跳转页面时tabbar上移问题

  • 当跳转页面的时候,只要加上hidesBottomBarWhenPushed = YES这行代码,当跳转页面的时候,tabbar都会上移一下,返回的时候也是回到原位的。

  • 第一步:写一个类继承自UITabBar,这里我取名叫 CustomTabBar,然后在.m文件里重写两个方法

#pragma mark - Override Methods
- (void)setFrame:(CGRect)frame
{
    if (self.superview &&CGRectGetMaxY(self.superview.bounds) !=CGRectGetMaxY(frame)) {
        frame.origin.y =CGRectGetHeight(self.superview.bounds) -CGRectGetHeight(frame);
    }
    [super setFrame:frame];
}

#pragma mark - Initial Methods
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [superinitWithFrame:frame];
    if (self) {
        self.translucent =false;
        self.backgroundColor = [UIColorwhiteColor];
    }
    return self;
}
  • 第二步:自定义UITabBarController取名叫TabBarVC ,在自定义的tabbar实例化的时候,加一行代码如下:
[self setValue:[[CustomTabBar alloc]init]forKey:@"tabBar"];

4.加载 UIImagePickerController 时,图片列表被导航条遮挡

// 由于这句代码的原因
if #available(iOS 11.0, *) {
    UITableView.appearance().contentInsetAdjustmentBehavior = .never
    UICollectionView.appearance().contentInsetAdjustmentBehavior = .never
}

谢谢DeviceUitility

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

@interface DeviceUitility : NSObject
/// 屏幕宽度
+ (CGFloat)getDeviceScreenWidth;
/// 屏幕高度
+ (CGFloat)getDeviceScreenHeight;

/// 获取设备型号然后手动转化为对应名称
- (NSString *)getDeviceName;

/// 获取iPhone名称
+ (NSString *)getiPhoneName;

/// 获取app版本号
+ (NSString *)getAPPVerion;

/// 获取电池电量
+ (CGFloat)getBatteryLevel;
/// 当前系统名称
+ (NSString *)getSystemName ;

/// 当前系统版本号
+ (NSString *)getSystemVersion;

/// 通用唯一识别码UUID
+ (NSString *)getUUID;
// 获取当前设备IP
+ (NSString *)getDeviceIPAdress;
/// 获取精准电池电量
+ (CGFloat)getCurrentBatteryLevel;

/// 获取电池当前的状态,共有4种状态
+ (NSString *) getBatteryState;
/// 获取当前语言
+ (NSString *)getDeviceLanguage;

/// CPU总数目
+ (NSUInteger)getCPUCount;
/// 获取磁盘总空间
+ (int64_t)getTotalDiskSpace;
/// 获取未使用的磁盘空间
+ (int64_t)getFreeDiskSpace;
/// 获取已使用的磁盘空间
+ (int64_t)getUsedDiskSpace;
/// 系统总内存空间
+ (int64_t)getTotalMemory;
/// 活跃的内存,正在使用或者很短时间内被使用过
+ (int64_t)getActiveMemory;
/// 最近使用过,但是目前处于不活跃状态的内存
+ (int64_t)getInActiveMemory;

/// 空闲的内存空间
+ (int64_t)getFreeMemory;

/// 已使用的内存空间
+ (int64_t)getUsedMemory;

/// 用来存放内核和数据结构的内存,framework、用户级别的应用无法分配
+ (int64_t)getWiredMemory;

/// 可释放的内存空间:内存吃紧自动释放,针对大对象存放所需的大块内存空间
+ (int64_t)getPurgableMemory;
@end
// ----------------------------------.m----------------------------------------
#import "DeviceUitility.h"
#import <sys/utsname.h>
#import <ifaddrs.h>
#import <arpa/inet.h>
#import <mach/mach.h>
#import <objc/runtime.h>

@implementation DeviceUitility
/// 屏幕宽度
+ (CGFloat)getDeviceScreenWidth {
    return [UIScreen mainScreen].bounds.size.width;
}
/// 屏幕高度
+ (CGFloat)getDeviceScreenHeight {
    return [UIScreen mainScreen].bounds.size.height;
}

// 获取设备型号然后手动转化为对应名称
- (NSString *)getDeviceName
{
    // 需要#import "sys/utsname.h"
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    
    if ([deviceString isEqualToString:@"iPhone3,1"])    return @"iPhone 4";
    if ([deviceString isEqualToString:@"iPhone3,2"])    return @"iPhone 4";
    if ([deviceString isEqualToString:@"iPhone3,3"])    return @"iPhone 4";
    if ([deviceString isEqualToString:@"iPhone4,1"])    return @"iPhone 4S";
    if ([deviceString isEqualToString:@"iPhone5,1"])    return @"iPhone 5";
    if ([deviceString isEqualToString:@"iPhone5,2"])    return @"iPhone 5 (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPhone5,3"])    return @"iPhone 5c (GSM)";
    if ([deviceString isEqualToString:@"iPhone5,4"])    return @"iPhone 5c (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPhone6,1"])    return @"iPhone 5s (GSM)";
    if ([deviceString isEqualToString:@"iPhone6,2"])    return @"iPhone 5s (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPhone7,1"])    return @"iPhone 6 Plus";
    if ([deviceString isEqualToString:@"iPhone7,2"])    return @"iPhone 6";
    if ([deviceString isEqualToString:@"iPhone8,1"])    return @"iPhone 6s";
    if ([deviceString isEqualToString:@"iPhone8,2"])    return @"iPhone 6s Plus";
    if ([deviceString isEqualToString:@"iPhone8,4"])    return @"iPhone SE";
    // 日行两款手机型号均为日本独占,可能使用索尼FeliCa支付方案而不是苹果支付
    if ([deviceString isEqualToString:@"iPhone9,1"])    return @"iPhone 7";
    if ([deviceString isEqualToString:@"iPhone9,2"])    return @"iPhone 7 Plus";
    if ([deviceString isEqualToString:@"iPhone9,3"])    return @"iPhone 7";
    if ([deviceString isEqualToString:@"iPhone9,4"])    return @"iPhone 7 Plus";
    if ([deviceString isEqualToString:@"iPhone10,1"])   return @"iPhone_8";
    if ([deviceString isEqualToString:@"iPhone10,4"])   return @"iPhone_8";
    if ([deviceString isEqualToString:@"iPhone10,2"])   return @"iPhone_8_Plus";
    if ([deviceString isEqualToString:@"iPhone10,5"])   return @"iPhone_8_Plus";
    if ([deviceString isEqualToString:@"iPhone10,3"])   return @"iPhone_X";
    if ([deviceString isEqualToString:@"iPhone10,6"])   return @"iPhone_X";
    
    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:@"iPod5,1"])      return @"iPod Touch (5 Gen)";
    
    if ([deviceString isEqualToString:@"iPad1,1"])      return @"iPad";
    if ([deviceString isEqualToString:@"iPad1,2"])      return @"iPad 3G";
    if ([deviceString isEqualToString:@"iPad2,1"])      return @"iPad 2 (WiFi)";
    if ([deviceString isEqualToString:@"iPad2,2"])      return @"iPad 2";
    if ([deviceString isEqualToString:@"iPad2,3"])      return @"iPad 2 (CDMA)";
    if ([deviceString isEqualToString:@"iPad2,4"])      return @"iPad 2";
    if ([deviceString isEqualToString:@"iPad2,5"])      return @"iPad Mini (WiFi)";
    if ([deviceString isEqualToString:@"iPad2,6"])      return @"iPad Mini";
    if ([deviceString isEqualToString:@"iPad2,7"])      return @"iPad Mini (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPad3,1"])      return @"iPad 3 (WiFi)";
    if ([deviceString isEqualToString:@"iPad3,2"])      return @"iPad 3 (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPad3,3"])      return @"iPad 3";
    if ([deviceString isEqualToString:@"iPad3,4"])      return @"iPad 4 (WiFi)";
    if ([deviceString isEqualToString:@"iPad3,5"])      return @"iPad 4";
    if ([deviceString isEqualToString:@"iPad3,6"])      return @"iPad 4 (GSM+CDMA)";
    if ([deviceString isEqualToString:@"iPad4,1"])      return @"iPad Air (WiFi)";
    if ([deviceString isEqualToString:@"iPad4,2"])      return @"iPad Air (Cellular)";
    if ([deviceString isEqualToString:@"iPad4,4"])      return @"iPad Mini 2 (WiFi)";
    if ([deviceString isEqualToString:@"iPad4,5"])      return @"iPad Mini 2 (Cellular)";
    if ([deviceString isEqualToString:@"iPad4,6"])      return @"iPad Mini 2";
    if ([deviceString isEqualToString:@"iPad4,7"])      return @"iPad Mini 3";
    if ([deviceString isEqualToString:@"iPad4,8"])      return @"iPad Mini 3";
    if ([deviceString isEqualToString:@"iPad4,9"])      return @"iPad Mini 3";
    if ([deviceString isEqualToString:@"iPad5,1"])      return @"iPad Mini 4 (WiFi)";
    if ([deviceString isEqualToString:@"iPad5,2"])      return @"iPad Mini 4 (LTE)";
    if ([deviceString isEqualToString:@"iPad5,3"])      return @"iPad Air 2";
    if ([deviceString isEqualToString:@"iPad5,4"])      return @"iPad Air 2";
    if ([deviceString isEqualToString:@"iPad6,3"])      return @"iPad Pro 9.7";
    if ([deviceString isEqualToString:@"iPad6,4"])      return @"iPad Pro 9.7";
    if ([deviceString isEqualToString:@"iPad6,7"])      return @"iPad Pro 12.9";
    if ([deviceString isEqualToString:@"iPad6,8"])      return @"iPad Pro 12.9";
    
    if ([deviceString isEqualToString:@"AppleTV2,1"])      return @"Apple TV 2";
    if ([deviceString isEqualToString:@"AppleTV3,1"])      return @"Apple TV 3";
    if ([deviceString isEqualToString:@"AppleTV3,2"])      return @"Apple TV 3";
    if ([deviceString isEqualToString:@"AppleTV5,3"])      return @"Apple TV 4";
    
    if ([deviceString isEqualToString:@"i386"])         return @"Simulator";
    if ([deviceString isEqualToString:@"x86_64"])       return @"Simulator";
    
    return deviceString;
}


/// 获取iPhone名称
+ (NSString *)getiPhoneName {
    return [UIDevice currentDevice].name;
}

/// 获取app版本号
+ (NSString *)getAPPVerion {
    return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
}

/// 获取电池电量
+ (CGFloat)getBatteryLevel {
    return [UIDevice currentDevice].batteryLevel;
}
/// 当前系统名称
+ (NSString *)getSystemName {
    return [UIDevice currentDevice].systemName;
}

/// 当前系统版本号
+ (NSString *)getSystemVersion {
    return [UIDevice currentDevice].systemVersion;
}

/// 通用唯一识别码UUID
+ (NSString *)getUUID {
    return [[UIDevice currentDevice] identifierForVendor].UUIDString;
}

// 获取当前设备IP
+ (NSString *)getDeviceIPAdress {
    NSString *address = @"an error occurred when obtaining ip address";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
    if (success == 0) { // 0 表示获取成功
        temp_addr = interfaces;
        while (temp_addr != NULL) {
            if( temp_addr->ifa_addr->sa_family == AF_INET) {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    freeifaddrs(interfaces);
    return address;
}

/// 获取总内存大小
+ (long long)getTotalMemorySize {
    return [NSProcessInfo processInfo].physicalMemory;
}

/// 获取当前可用内存
+ (long long)getAvailableMemorySize {
    vm_statistics_data_t vmStats;
    mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
    kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
    if (kernReturn != KERN_SUCCESS)
    {
        return NSNotFound;
    }
    return ((vm_page_size * vmStats.free_count + vm_page_size * vmStats.inactive_count));
}

/// 获取精准电池电量
+ (CGFloat)getCurrentBatteryLevel {
    UIApplication *app = [UIApplication sharedApplication];
    if (app.applicationState == UIApplicationStateActive||app.applicationState==UIApplicationStateInactive) {
        Ivar ivar=  class_getInstanceVariable([app class],"_statusBar");
        id status  = object_getIvar(app, ivar);
        for (id aview in [status subviews]) {
            int batteryLevel = 0;
            for (id bview in [aview subviews]) {
                if ([NSStringFromClass([bview class]) caseInsensitiveCompare:@"UIStatusBarBatteryItemView"] == NSOrderedSame&&[[[UIDevice currentDevice] systemVersion] floatValue] >=6.0) {
                    Ivar ivar=  class_getInstanceVariable([bview class],"_capacity");
                    if(ivar) {
                        batteryLevel = ((int (*)(id, Ivar))object_getIvar)(bview, ivar);
                        if (batteryLevel > 0 && batteryLevel <= 100) {
                            return batteryLevel;
                        } else {
                            return 0;
                        }
                    }
                }
            }
        }
    }
    return 0;
}

/// 获取电池当前的状态,共有4种状态
+ (NSString *) getBatteryState {
    UIDevice *device = [UIDevice currentDevice];
    if (device.batteryState == UIDeviceBatteryStateUnknown) {
        return @"UnKnow";
    } else if (device.batteryState == UIDeviceBatteryStateUnplugged){
        return @"Unplugged";
    } else if (device.batteryState == UIDeviceBatteryStateCharging){
        return @"Charging";
    } else if (device.batteryState == UIDeviceBatteryStateFull){
        return @"Full";
    }
    return nil;
}

/// 获取当前语言
+ (NSString *)getDeviceLanguage {
    NSArray *languageArray = [NSLocale preferredLanguages];
    return [languageArray objectAtIndex:0];
}


// CPU总数目
+ (NSUInteger)getCPUCount {
    return [NSProcessInfo processInfo].activeProcessorCount;
}

// 获取磁盘总空间
+ (int64_t)getTotalDiskSpace {
    NSError *error = nil;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) return -1;
    int64_t space =  [[attrs objectForKey:NSFileSystemSize] longLongValue];
    if (space < 0) space = -1;
    return space;
}
// 获取未使用的磁盘空间
+ (int64_t)getFreeDiskSpace {
    NSError *error = nil;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) return -1;
    int64_t space =  [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
    if (space < 0) space = -1;
    return space;
}
// 获取已使用的磁盘空间
+ (int64_t)getUsedDiskSpace {
    int64_t totalDisk = [self getTotalDiskSpace];
    int64_t freeDisk = [self getFreeDiskSpace];
    if (totalDisk < 0 || freeDisk < 0) return -1;
    int64_t usedDisk = totalDisk - freeDisk;
    if (usedDisk < 0) usedDisk = -1;
    return usedDisk;
}

// 系统总内存空间
+ (int64_t)getTotalMemory {
    int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
    if (totalMemory < -1) totalMemory = -1;
    return totalMemory;
}

// 活跃的内存,正在使用或者很短时间内被使用过
+ (int64_t)getActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.active_count * page_size;
}

// 最近使用过,但是目前处于不活跃状态的内存
+ (int64_t)getInActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.inactive_count * page_size;
}

// 空闲的内存空间
+ (int64_t)getFreeMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.free_count * page_size;
}

// 已使用的内存空间
+ (int64_t)getUsedMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
}

// 用来存放内核和数据结构的内存,framework、用户级别的应用无法分配
+ (int64_t)getWiredMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.wire_count * page_size;
}

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

推荐阅读更多精彩内容