UIKit框架基础

UIKit框架基础UIKit是CocoaTouch的一部分,提供了一组类来创建和管理iOSApp的窗口和视图、处理事件、绘图和动画等。常用的UIKit组件有UIView及子类,包括UIScrollView(UITableView、UICollectionView、UITextView)、UILabel、UIControl(UIButton、UITextField)、UIPickerView等;与控件相关但不能被人所直观看到的图形、绘图、打印、文本等的配置与控制。…

iOS 需要在info.plist中 添加的权限

1 概述

UIKit 框架提供了 iOS 或 Apple tvOS App 所需的基础架构。它提供了用于实施界面的窗口和视图架构,用于向 App 提供多点触控和其他类型输入的事件处理基础架构,以及管理用户、系统和 App 之间互动所需的主运行循环。该框架提供的其他功能包括动画支持、文档支持、绘图和打印支持、当前设备的相关信息、文本管理和显示、搜索支持、辅助功能支持、App 扩展支持和资源管理。

// 框架的入口
  #import <UIKit/UIKit.h> 

UIKit是Cocoa Touch的一部分,提供了一组类来创建和管理iOS App的窗口和视图、处理事件、绘图和动画等,框架中的类具有如下继承关系结构

常用的UIKit组件有

·UIView及子类,包括UIScrollView(UITableView、UICollectionView、UITextView)、UILabel、UIControl(UIButton、UITextField)、UIPickerView等;

·与控件相关但不能被人所直观看到的图形、绘图、打印、文本等的配置与控制,包括UIViewController、UIImage等。

1.1 界面构建

·xib:轻量级的,可通过拖拽进行界面创作和布局,本质是个XML文件。

·nib(NeXT Interface Builder):xib编译后得到的二进制文件。

·storyboard:重量级的,自iOS 5引入,用来描述整个软件的多个界面,也是个XML文件。

storyboard使用方式:

·通过拖放对象来完成静态的界面布局,通过连线把静态的元素与代码进行关联,如IBOutlet和IBAction。

storyboard优缺点:

·它可以用于描述不同视图之间的关联,具有原型表项(prototype cell)和静态表项(static cell)特性和简化自动布局等特点;

·性能较差,容易发生冲突,不便于进行模块化管理。

1.2 生命周期

UIApplication对象是应用程序的象征,一个 UIApplication 对象就代表一个应用程序。一个 iOS 程序启动后创建的第一个对象就是 UIApplication 对象。利用 UIApplication 对象,能进行一些应用级别的操作。每个iOS APP都只有一个UIApplication(或者很少有的UIApplication子类)实例。当应用程序启动时,系统会调用UIApplicationMain(_:_:_:_:)函数。每一个应用都有自己的单例的UIApplication 对象,如果试图在程序中新建一个UIApplication对象,那么将报错。

The Main Run Loop主运行循环负责处理用户相关的事件。UIApplication对象在程序启动时启动Main Run Loop,它处理事件和更新视图的界面,它是运行在程序的主线程上的,这样保证了接收到用户相关操作的事件是按顺序处理的。UIApplication对象处理传入用户事件的初始路由,它将UIControl类对象转发给它的动作消息分派给适当的目标对象。UIApplication对象维护一个打开的窗口(UIWindow对象)列表,它可以用来检索任何应用程序的UIView对象。

UIApplication对象会被赋予一个代理对象,以处理应用程序的生命周期事件(比如程序启动和关闭)。在移动操作系统中,应用程序很容易受到来电、锁屏等干扰,这时UIApplication对象也会通知它的代理对象,让代理对象来处理这些系统事件

UIApplication状态:1)Not running(未运行)程序没启动;2)Inactive(未激活)程序在前台运行没有接收到事件;3)Active(激活)程序在前台运行而且接收到了事件;4)Background(后台) 程序在后台而且能执行代码,执行完毕或超时后会进入挂起状态 (Suspended),有的程序经过特殊的请求后可以长期处于 Background 状态;5)Suspended(挂起)程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。

UIApplication详细介绍: Apple Developer Documentation iOS应用程序生命周期(前后台切换,应用的各种状态)详解_空杯子_的博客-CSDN博客

懒加载的优势

懒加载(延迟加载),把对象的实例化尽量延迟,即启动应用程序时不加载这个资源,只有在运行时用到才加载(按需加载

例如,如果启动APP后一次性加载大量数据、图片和音视频等资源,就有可能会耗尽移动设备内存。这时就可以使用懒加载技术。具体来说,重写属性的getter方法,先判断该属性是否为nil,如果为空再进行实例化,否则直接返回属性。

懒加载的好处有:不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强;每个控件分别负责各自的实例化处理,代码的耦合程度低;不必在初始化阶段加载所有数据,节省内存,也就是系统的内存占用率会减少;减少服务器端压力。

1.2.1 UIApplication

·UIApplicationMain

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    appDelegateClassName = NSStringFromClass([AppDelegate class]);
    /**
     * 前两个参数和C语言一致
     * 第三个参数是NSString *principalClassName,如果是nil,那么它的值将从Info.plist获取,如果Info.plist没有,则默认为 UIApplication。principalClass这个类除了管理整个程序的生命周期之外什么都不做,它只负责监听事件然后交给delegateClass处理。
     * 第四个参数是NSString *delegateClassName,作用是对一系列系统事件,如程序将要启动、程序启动完成、程序进入后台、程序进入前台、程序退出等做出反应。AppDelegate是UIApplication默认的代理,已经遵守了UIApplicationDelegate协议。
    **/
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

·APP的生命周期:

·didFinishLaunchingWithOptions

在这个函数中可以用如下类似的代码创建一个UIWindow:

// 伪代码:self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]

如果在指定模版时选择了storyboard,那么Info.plist的Main storyboard file name(UIMainStoryboardFile)会指向主storyboard。UIApplicationMain实例化代理后,会向代理询问其window属性的值;如果该值为 nil,则创建UIWindow对象并将其指定给代理的window属性,然后将主storyboard的初始视图控制器实例化并分配给窗口的rootViewController属性,结果是将其作为根视图控制器放置在窗口中,再向窗口发送makeKeyAndVisible消息。

1.2.2 视图控制器

每个视图控制器VC都维护一个视图层次,视图层次的根结点是根视图,可以通过self.view来访问,每个视图都有自己的子视图。

调用时机

方法

作用

默认实现

UIStoryboard获取视图控制器,或者NSBundle加载nib文件时

– (instancetype)initWithCoder:(NSCoder *)coder

nib文件、xib或storyboard中加载视图控制器

在当前所有控件元素都加载好之后

awakeFromNib

该消息发送给所有拥有已加载元素的对象,表明对其他加载对象的所有引用都是有效且可用的。

VC在view属性被请求且当前view为nil时

loadView

VC在初始化时创建一个UIView对象,作为默认视图,可以通过self.view来访问。

从Storyboard或xib中加载VC的视图设为当前VC的视图属性,如果没有关联的文件则创建一个空白视图给VC的视图属性。

视图层次已经放入内存中。在控制器因为某种原因如要显示,需要使用到该视图的时候调用。一个VC生命周期内只会调用一次。

-(void)viewDidLoad

可以在此完成自定义数据和控件;当几何内容被确定之后,viewWillLayoutSubview和viewDidLayoutSubviews被调用

视图的基本的初始化,除了几何图形的初始化

当收到视图在窗口将可见时。可能会调用多次。

-(void)viewWillAppear:(BOOL)animated:

对不可见时可能改变的数据进行同步

不执行任何操作

当收到视图在窗口已可见时,此时视图已在屏幕上渲染完成

-(void)viewDidAppear:(BOOL)animated:

不执行任何操作

视图被驳回时。当收到视图将去除、被覆盖或隐藏于窗口的通知时。

-(void)viewWillDisappear:(BOOL)animated:

不执行任何操作

视图被驳回后。当收到视图已去除、被覆盖或隐藏于窗口的通知时。

-(void)viewDidDisappear:(BOOL)animated

不执行任何操作

返回是否支持不同方向的旋转视图

shouldAutorotateToInterfaceOrientation

支持或禁止自动旋转

支持

进行旋转视图前

willAnimateRotationToInterfaceOrientation

用于调整旋转视图

2 MVC

·在iOS中,多数数据源视图控件(View)都有一个dataSource属性,数据源通过与控制器(Controller)交互,间接地从我们定义的数据模型(Model)中获取数据,视图和控制器之间可以相互访问,模型既不能访问视图也不能访问控制器,这种模式是MVC;·视图是用户在屏幕上看到的,模型提供数据,控制器是它们的中介。

3 UIView

UIView是UIResponder的子类,是许多常用控件的父类,如UIScrollView、UILabel、UIControl和UIPickerView等。屏幕上所有的UI元素都是控件,控件指的就是UIView类或者子类。

·Apple将所有控件的基本属性都封装到UIView中,如frame、bounds、center、backgroundColor、hidden、alpha、opaque和userInteractionEnabled等;

·每个controller都对应一个默认的UIView对象,该控件的大小是整个屏幕,每个UIView对象都是容器,都被该控件所容纳。在一个控制器.m文件中,self.view就是调用控制器的默认UIView。

UIView有三个结构体CGPoint,CGRect,CGSize。CGPoint确定了view的起始坐标

CGSize确定view的长宽;CGRect确定了整个视图位置和大小。

/* 关于UIView隐藏属性、alpha属性和不透明属性之间有什么区别? */
[_uiview setHidden:YES];
[_uiview setAlpha:0.0f];
/* 差异很微妙。根据UIView文档:
opaque 告诉系统视图没有透明度,因此渲染速度更快,因为可以跳过混合计算;
hidden 是布尔属性,仅更改当前视图的可见性并将其从 ui 事件中隐藏;
alpha 是一个动画属性。
设置alpha=0.0f或hidden=YES具有相同的视觉效果。然而,当您有大量嵌套视图时,使用hidden 不仅在图形意义上而且从UI事件中实际隐藏视图可能会导致更有效的响应者链. */

3.1 Frame和Bounds

frame和bounds都是UIView的属性。

·bounds通常用来描述视图的大小,bounds大小改变时,当前视图的中心点不会发生改变,当前视图的大小发生改变。bounds改变位置时,改变的是子视图的位置,自身没有影响,实质上改变了当前视图的坐标系原点;
·frame用来描述视图的位置(origin)和大小(size),当前视图的位置是以父视图坐标系来确定。frame大小改变时,当前视图的坐标系原点不会改变,当前视图的大小发生改变。使用frame的布局是唯一确定的,不会随父视图变化而变化,除非在某个时间点再次设置了frame。

3.2 CALayer

CALayer是屏幕上的一个具有可见内容的矩形区域,每个UIView都有一个根CALayer,其所有的绘制(视觉效果)都是在这个layer上进行的。

通过UIView的layer属性可以访问这个层。

3.3 UITextView

UITextView是UIScrollView的子类,具有UIScrollView的属性和方法,包括frame、bounds和backgroundColor等。

UITextView *textmtview = [[UITextView alloc] initWithFrame:CGRectMake(80, 365, 250, 50)];  // Initialize

富文本字符串:AttributedString可以分为NSAttributedStringNSMutableAttributedString,通过将AttributedString赋值给UILabel、UITextField或UITextView的attributedText属性来添加文字样式。

NSTextAttachment该类使用文本附件对象作为附件属性的值(存储在键下的富文本字符串中)。

NSTextAttachment* attch = [[NSTextAttachment alloc] init];
CGFloat diameter = [self fontLineHeight:[UIFont systemFontOfSize:14]];
attch.image = [UIImage imageNamed:@"waybill_detail_help"];
attch.bounds = CGRectMake(0, -5, diameter, diameter);
NSAttributedString* attStr = [NSAttributedString attributedStringWithAttachment:attch];
[attMutStr appendAttributedString:attStr];

3.4 UITableView

3.4.1 数据源和代理

·视图对象:表视图只管理表中数据的表示,而不管理数据本身。

·数据源:UITableView的数据源用于提供数据行数以及每一行显示的数据等,数据源通常是遵守UITableViewDataSource协议的对象。

UITableViewDataSource协议:

表视图数据源协议,该协议声明了管理表视图数据和cell的方法。数据源对象响应表中与数据相关的请求。它还可以直接管理表的数据,或者与app的其他部分协调一起来管理该数据

@protocol UITableViewDataSource<NSObject>
@required
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;  // 设置表视图每个分组的行数
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;  // 控制每一行返回的内容
@optional
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;  // 设置分区数
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;  // 设置分区头的内容
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;  // 控制Cell是否可以编辑
// Other...
@end

·代理:设置UITableView的代理对象来响应事件,比如选中某一行,代理通常是遵守UITableViewDelegate协议的对象。使用表视图通常需要遵守UITableViewDataSource和UITableViewDelegate这两个协议。

UITableViewDelegate协议:

用于管理选择、配置节页眉和页脚、删除和重新排序单元格以及在表格视图中执行其他操作的方法。使用此协议的方法来管理以下功能:创建和管理自定义页眉和页脚视图;指定行、页眉和页脚的自定义高度;提供高度估计以获得更好的滚动支持;缩进行内容;响应行选择等。

// Cell被点击后需要做什么
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
// 告诉UITableView每一个Cell的高度是多少
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

3.4.2 cell重用

·UITableView有一个重用池机制来管理UITableViewCell,当列表滑动时,一部分cell会移出窗口,UITableView默认只加载在屏幕上显示的cell,窗口外的cell会被放入一个对象池里等待复用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先尝试从对象池获取一个,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象。

·UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中尝试获取对应类型的UITableViewCell对象,如果没有对应类型的对象,就用这个标识来初始化一个UITableViewCell对象。

重用方法:定义cell的标识、从对象池里取cell、判断取出cell是否为空,为空就初始化cell。

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    static NSString *showUserInfoCellIdentifier = @"ShowUserInfoCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:showUserInfoCellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:showUserInfoCellIdentifier];
    }
    return cell;
}

3.5 UILabel

UILabel 是基础的文本组件

// 自适应计算UILabel高度
+ (CGFloat)calculateLabelHeightWithString:(NSString *)string font:(UIFont *)font labelWidth:(CGFloat)labelWidth {
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
    paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
    CGFloat height = [string boundingRectWithSize:CGSizeMake(labelWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:font,NSParagraphStyleAttributeName:paragraphStyle.copy} context:nil].size.height;
    short scale = [UIScreen mainScreen].scale;
    height = scale * height;
    height = ceilf(height);
    
    return height / scale;
}

4 UIViewController

UIViewController是视图控制器,它由控制器+view组成。它能够完成以下功能:

  1. 控制视图的代码逻辑,并拥有自己的视图

  2. 控制器与控制器之间的通信

  3. 视图与Model之间的传值

  4. 添加子控制器

  5. 展示界面信息

  6. 添加子控件

4.1 UITabBarController

UITabBarController就是多个ViewController的容器,他们之间的层级是平行的,它会在底部添加一个TabBar的UIView,通过点击TabBar上的按钮tabBarItem来切换对应的ViewController。

4.2 UINavigationController

UITabBarController一般配合UINavigationController来使用,这样可以实现多Tab,多栈跳转页面视图。

4.3 Segue

类型

作用

Show (Push)

此segue调用showViewController: sender:方法显示新内容。对于大多数视图控制器,show segue 在源视图控制器上以modal方式呈现新内容。但UISplitViewController和UINavigationController类会重写showViewController: sender:方法,以根据自身设计处理呈现方式。如在UINavigationController中,视图控制器会被push到其导航堆栈。

Show Detail (Replace)

此segue调用showDetailViewController: sender:方法显示新内容。Show Detail segue仅与嵌入在UISplitViewController对象内的视图控制器相关,此时分割视图用新内容替换detail controller。在其他视图控制器中,show detail会以modal形式呈现新内容。

Present Modally

调用方法presentViewController: animated: completion:,使用指定presentation style和transition style以modal形式呈现新内容

Present As Popover

在horizontally regular environment,视图控制器显示在弹出窗口中;在horizontally compact environment,视图控制器使用全屏模式显示。

5 UIScreen

// 带有状态栏的 Rect 
CGRect bound = [UIScreen mainScreen].bounds;
// 不带有状态栏的 Rect 
CGRect frame = [UIScreen mainScreen].applicationFrame;
// 设备的自然分辨率
float scale = [UIScreen mainScreen].scale;

PPI(Pixel Per Inch by diagonal):表示沿着对角线,每英寸所拥有的像素(Pixel)数目。PPI数值越高,代表显示屏能够以越高的密度显示图像,即通常所说的分辨率越高、颗粒感越弱。在同样的逻辑坐标系下(320×480),1 point = scale*pixel(在iPhone4~6中,缩放因子scale=2;在iPhone6+中,缩放因子scale=3)。可以理解为:scale=绝对长度比(point/pixel)=单位长度内的数量比(pixel/point)

1 MAMapkit

高德地图 iOS SDK 是一套基于 iOS 8.0 及以上版本的地图应用程序开发接口,供开发者在自己的iOS应用中加入地图相关的功能,包括:地图显示(含室内、室外地图)、与地图交互、在地图上绘制、兴趣点搜索、地理编码、离线地图等功能。

高德地图 iOS SDK 专业版是在 iOS SDK 已有服务的基础上,新增支持了自定义地图在线加载、自定义地图元素纹理等功能,便于开发者完成基于自身场景的更深层、更个性化地图的开发需求。

·注册

使用高德地图,首先需要注册获取应用KEY,申请流程见官网

·导入第三方库

自动配置(使用Cocoapods):

1)编辑Podfile

platform :ios, '15.0' #手机的系统
target 'demogmap' do
	pod 'AMap2DMap' #2D地图SDK(2D和3D不能同时使用)
	pod 'AMapSearch' #搜索功能
end

2)安装依赖库(M1)

sudo arch -x86_64 gem install ffi
arch -x86_64 pod install

手动配置:

1)添加依赖库

2)添加资源文件

需要引入的资源文件包括:AMap.bundle,其中:AMap.bundle 在 MAMapKit.framework 包中,AMap.bundle资源文件中存储了定位、默认大头针标注视图等图片,可利用这些资源图片进行开发。

申请好key值后,需要在application里配置用户的key值

3)修改Info.plist,增加如下键值

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>始终允许访问位置信息</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>使用应用期间允许访问位置信息</string>

4)在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中添加申请的key

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [[AMapServices sharedServices] setEnableHTTPS:YES];
    [AMapServices sharedServices].apiKey = @"4ca2e61dc9bd7504896f99b4558a2e7b";
    return YES;
}

1.1 定位

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    _mapView = [[MAMapView alloc] initWithFrame:self.view.bounds];
    _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [_mapView setShowsUserLocation:YES]; // 显示定位
    [_mapView setUserTrackingMode:MAUserTrackingModeFollow];
    [self.view addSubview:_mapView];
    [_mapView setDelegate:self];  // 设置委托
}

1.2 手势

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    UILongPressGestureRecognizer *lpress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    lpress.delegate = self;
    [_mapView addGestureRecognizer:lpress];
}

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

- (void)longPress:(UIGestureRecognizer*)gestureRecognizer{
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
        return;
    }
    [_mapView removeAnnotation:_pointAnnotation];
    CGPoint touchPoint = [gestureRecognizer locationInView:_mapView]; //获取坐标点
    CLLocationCoordinate2D touchMapCoordinate = [_mapView convertPoint:touchPoint toCoordinateFromView:_mapView];
    
    _pointAnnotation.coordinate = touchMapCoordinate;
    _pointAnnotation.title = @"选定位置";
    
    [_mapView addAnnotation:_pointAnnotation];
    [self.mapView addOverlay:[MACircle circleWithCenterCoordinate:touchMapCoordinate radius:1000]];
    
    [self setLocationWithLatitude:touchMapCoordinate.latitude AndLongitude:touchMapCoordinate.longitude];
}

1.3 标注

- (MAAnnotationView*)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation {
    if ([annotation isKindOfClass:[MAPointAnnotation class]])
    {
        static NSString *pointReuseIndetifier = @"pointReuseIndetifier";
        MAPinAnnotationView *annotationView = (MAPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndetifier];
        if (annotationView == nil)
        {
            annotationView = [[MAPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndetifier];
        }
        
        annotationView.canShowCallout               = YES;
        annotationView.animatesDrop                 = YES;
        annotationView.draggable                    = YES;
        annotationView.rightCalloutAccessoryView    = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        annotationView.pinColor                     = [self.annotations indexOfObject:annotation] % 3;
        
        return annotationView;
    }
    
    return nil;
}

1.4 描绘蒙层

- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id<MAOverlay>)overlay {
    if([overlay isKindOfClass:[MACircle class]]){
        MACircleRenderer *render = [[MACircleRenderer alloc] initWithCircle:overlay];
        render.fillColor = [UIColor colorWithRed:1.0 green:0.8 blue:0.0 alpha:0.8];  //圆圈的填充颜色
        render.strokeColor = UIColor.blueColor;  //圆圈的轮廓颜色
        render.lineWidth = 3.f;  //圆圈轮廓的粗细
        return render;
    }
    return nil;
}

1.5 发起路径规划搜索

- (IBAction)findWayAction:(id)sender {
    // 设置步行路径规划请求参数
    AMapWalkingRouteSearchRequest* walkRequest = [[AMapWalkingRouteSearchRequest alloc] init];
    // 设置起点
    walkRequest.origin = [AMapGeoPoint locationWithLatitude:((MAPointAnnotation*)(_annotations[0])).coordinate.latitude longitude:((MAPointAnnotation*)(_annotations[0])).coordinate.longitude];
    // 点击设置终点
    walkRequest.destination = [AMapGeoPoint locationWithLatitude:((MAPointAnnotation*)(_pointAnnotation)).coordinate.latitude longitude:((MAPointAnnotation*)(_pointAnnotation)).coordinate.longitude];
    // 发起路径搜索,发起后会执行代理方法
    [_search AMapWalkingRouteSearch:walkRequest];
}

//路线解析
- (MAPolyline *)polylinesForPath:(AMapPath *)path{
    if (path == nil || path.steps.count == 0){
        return nil;
    }
    NSMutableString *polylineMutableString = [@"" mutableCopy];
    for (AMapStep *step in path.steps) {
        [polylineMutableString appendFormat:@"%@;",step.polyline];
    }
    
    NSUInteger count = 0;
    CLLocationCoordinate2D *coordinates = [self coordinatesForString:polylineMutableString coordinateCount:&count parseToken:@";"];
    
    MAPolyline *polyline = [MAPolyline polylineWithCoordinates:coordinates count:count];
    
    free(coordinates), coordinates = NULL;
    return polyline;
}

//解析经纬度
- (CLLocationCoordinate2D *)coordinatesForString:(NSString *)string
                                 coordinateCount:(NSUInteger *)coordinateCount
                                      parseToken:(NSString *)token{
    if (string == nil){
        return NULL;
    }
    
    if (token == nil){
        token = @",";
    }
    
    NSString *str = @"";
    if (![token isEqualToString:@","]){
        str = [string stringByReplacingOccurrencesOfString:token withString:@","];
    }else{
        str = [NSString stringWithString:string];
    }
    
    NSArray *components = [str componentsSeparatedByString:@","];
    NSUInteger count = [components count] / 2;
    if (coordinateCount != NULL){
        *coordinateCount = count;
    }
    CLLocationCoordinate2D *coordinates = (CLLocationCoordinate2D*)malloc(count * sizeof(CLLocationCoordinate2D));
    
    for (int i = 0; i < count; i++){
        coordinates[i].longitude = [[components objectAtIndex:2 * i]     doubleValue];
        coordinates[i].latitude  = [[components objectAtIndex:2 * i + 1] doubleValue];
    }
    return coordinates;
}

2 相机和相册

iOS开发中,通常获取照片的方式有三种:1.直接调用摄像头拍照 2.从相册中选择 3.从图库中选择。 通常使用到的类或框架有UIImagePickerController、AssetsLibrary(iOS 4 – iOS 9)、PhotoKit(iOS8)等。

2.1 UIImagePickerController

UIImagePickerController 是系统提供的用来获取图片和视频的接口。用 UIImagePickerController 类来获取图片视频,大体分为以下几个步骤:

  1. 初始化 UIImagePickerController 类;

  2. 设置 UIImagePickerController 实例的数据来源类型;

  3. 设置代理;

  4. 如果需要做图片修改的话设置 allowsEditing = YES。

// 需遵守协议 UIImagePickerControllerDelegate, UINavigationControllerDelegate
_uiipc = [[UIImagePickerController alloc] init];
//获取照片的类型
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
    _uiipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
//设置代理
_uiipc.delegate = self;
//是否允许编辑
_uiipc.allowsEditing = YES;

#pragma mark UIImagePickerControllerDelegate

- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker {
    [picker dismissViewControllerAnimated:YES completion:nil];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    UIImageView* imgview = _uiiv;
    // 获取选择的图片
    UIImage* org = [info objectForKey:UIImagePickerControllerOriginalImage];
    // 修改图片
    UIImage* cir = [self CircleImageWithRef:org AndDiameter:100.0f AndBorderWidth:2.0f AndBorderColor:[UIColor redColor]];
    // 替换原来的头像
    [picker dismissViewControllerAnimated:YES completion:^(void) {
        [UIView animateWithDuration:0.5f animations:^(void) {
            [self.view setAlpha:1.0f];
            [imgview setImage:cir];
        } completion:nil];
        UIAlertController* uiac = [UIAlertController alertControllerWithTitle:@"提示" message:@"更改头像" preferredStyle:UIAlertControllerStyleAlert];
        [uiac addAction:[UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil]];
        [self presentViewController:uiac animated:YES completion:nil];
    }];
}

2.2 AssetsLibrary

AssetsLibrary框架包含ALAssetsLibrary、ALAssetsGroup、ALAsset、ALAssetsFilter、ALAssetRepresentation五个类,提供从相册中读取相片、将相片保存到相册,获取相册信息,增加相册等功能。

·ALAssetsLibrary类用来构建资源库对象,这个对象用来整体操作系统的相册资源,可以实现查看相册列表,增加相册,保存图片到相册等功能,例如enumerateGroupsWithTypes方法列举所有相册;

·ALAssetsGroup相册类,通过valueForProperty方法查看不同属性的值,如:ALAssetsGroupPropertyName相册名。ALAssetsGroup类有几个方法,posterImage方法是相册的封面图片,numberOfAssets方法获取该相册的图片视频数量,通过enumerateAssetsUsingBlock方法列举出所有照片,使用setAssetsFilter:(ALAssetsFilter *)filter过滤照片或者视频等;

·ALAsset类也可以通过valueForProperty方法查看不同属性的值,如:ALAssetPropertyType,asset的类型,有三种ALAssetTypePhoto, ALAssetTypeVideo or ALAssetTypeUnknown。另外还可以通过该方法获取ALAssetPropertyLocation(照片位置),ALAssetPropertyDuration(视频时间),ALAssetPropertyDate(照片拍摄日期)等;

·ALAssetRepresentation类是ALAsset类的defaultRepresentation方法的返回值类型,该类的作用就是获取该资源图片的详细资源信息。

2.3 PhotoKit

在 iOS 和 macOS 中,PhotoKit是App在使用、管理图片和视频的框架,而且还包括了iCloud上面的图片以及实时照片。PhotoKit 提供了支持为照片应用程序构建照片编辑扩展的类。在 iOS、macOS 和 tvOS 中,PhotoKit 还提供对照片应用程序管理的照片和视频资产的直接访问。

3 QuartzCore

QuartzCore(包含CoreAnimation)框架,是iOS系统的基本渲染框架,是一个OC语言框架,是一套基于CoreGraphics的OC语言封装,封装出了基本渲染类CALayer。QuartzCore框架用于提供iOS开发中的视觉反馈,开发的界面都是QuartzCore框架中CALayer图层合成的结果,CALayer提供了接口用于给自己添加Animation。

3.1 CALayer

在iOS中,UIView控件并不是直接显示在屏幕上,而是在创建UIView视图对象的时候内部会自动创建一个图层即CALayer对象,而UIView把要显示的东西绘制在层上,待到需要显示时硬件将所有的层拷贝,然后按Z轴的高低合成最终的显示结果。CALayer本质上是一块包含一幅位图的缓冲区,由视图创建的层为隐式层,而手动创建的层称为显式层。CALayer实现了CAMediaTiming协议,CALayer通过CAMediaTiming协议实现了一个有层级关系的时间系统。除了CALayer,CAAnimation也采纳了此协议,用来实现动画的时间系统。

树状结构

Layer也和View一样存在着一个层级树状结构,称之为图层树(Layer Tree),直接创建的或者通过UIView获得的用于显示的图层树,称之为模型树(Model Tree),模型树中的对象是应用程序与之交互的对象。此树中的对象是存储任何动画的目标值的模型对象。每当更改图层的属性时,都使用其中一个对象。模型树负责存储动画的目标值的模型对象。每当更改图层的属性时,它都会把数据存储下来。

模型树的背后还存在两份图层树的拷贝,一个是呈现树(Presentation Tree),一个是渲染树(Render Tree)。呈现树可以通过普通layer(其实就是模型树)的layer.presentationLayer获得,而模型树则可以通过modelLayer属性获得。呈现树通过图层树中所有图层的呈现图层所形成。注意呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候创建,所以在那之前调用-presentationLayer将会返回nil。呈现树中的对象反映屏幕上显示的当前值。您永远不应该修改此树中的对象。相反,您可以使用这些对象来读取当前动画值,也许是为了从这些值开始创建新动画。self.layer = self.layer.modelLayer 、self.layer != self.layer.presentationLayer。layer本身其实就是一个模型layer,只不过它拥有presentationLayer。

呈现树的属性值和动画运行过程中界面上看到的是一致的,而渲染树是私有的,是对呈现树的数据进行渲染。为了不阻塞主线程,渲染的过程是在单独的进程或线程中进行的。

@property(nonatomic,readonly,retain) CALayer *layer;  // 通过UIView的layer属性可以访问这个层

UIView与CALayer

当UIView需要显示时,会调用drawRect:方法将所有内容绘制在自己的CALayer上,绘图完毕后系统再将图层拷贝到屏幕上,从而完成了UIView的显示。UIView的animation实现的动画本质上也是通过CALayer来实现的,iOS系统中CALayer的很多属性都是隐含有动画效果的,如果不想要隐式动画或者想要显示动画效果,都可以通过CATransaction来设置是否显示动画效果。同时,在CATransaction内可同时修改多个属性,然后再一并同时渲染,另外CATransaction还是可嵌套的。与CALayer相比,UIView还具有事件处理的功能,可以跟用户进行交互。CALayer的性能更高,因为它不具备处理事件和响应链(responder chain)等(CALayer直接继承于NSObject,而UIView继承于UIResponder,更加轻量级。UIView侧重于对显示内容的管理,CALayer侧重于对内容的绘制。对于UIView所管理的内容,显示任何图形都会受到CALayer的影响。UIView依赖于CALayer提供的内容,CALayer依赖于UIView提供的容器来显示绘制的内容。UIView与CALayer都有树状层级结构,CALayer内部有SubLayers,UIView内部也有SubViews。

CALayer重要属性

属性

作用

position

设置CALayer在父层上的位置,以父层的左上角为原点(0,0)

anchorPoint

决定CALayer哪个点会在position属性指定的位置,以自己的左上角为原点(0,0),最大的坐标位置为右下角为(1,1),其默认位置为中心点(0.5,0.5)

bounds

设置CALayer对象的尺寸

隐式动画

每一个UIView内部都默认关联着一个CALayer,我们可称这个Layer为Root Layer(根层)。所有的非Root Layer的显式CALayer对象都存在隐式动画。当对显式CALayer对象的部分属性进行修改时,默认会自动产生一些动画效果, 这些属性称为Animatable Properties。

// 通过动画事务(CATransaction)关闭默认的隐式动画效果
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.myview.layer.position = CGPointMake(10, 10);
[CATransaction commit];

常见Animatable Properties

属性

作用

bounds

用于设置CALayer的宽度和高度;修改这个属性会产生缩放动画

backgroundColor

用于设置CALayer的背景色;修改这个属性会产生背景色的渐变动画

position

用于设置CALayer的位置;修改这个属性会产生平移动画

3.2 Core Animation

IOS 动画主要是指Core Animation框架。官方使用文档地址为:Core Animation Programming Guide
Core Animation是IOS和OS X平台上负责图形渲染与动画的基础框架。Core Animation可以作用与动画视图或者其他可视元素,为你完成了动画所需的大部分绘帧工作。你只需要配置少量的动画参数(如开始点的位置和结束点的位置)即可使用Core Animation的动画效果。Core Animation将大部分实际的绘图任务交给了图形硬件来处理,图形硬件会加速图形渲染的速度。这种自动化的图形加速技术让动画拥有更高的帧率并且显示效果更加平滑,不会加重CPU的负担而影响程序的运行速度。

Core Animation核心动画是一组iOS提供的动画处理API,它是跨Mac OS X和iOS平台的。Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。Core Animation作用于CALayer上而并非UIView,修改的值都是假象,layer的真实位置并没有发生改变。

作用

CABasicAnimation

基本动画。CABasicAnimation有三个比较重要的属性:fromValue、toValue、byValue,这三个属性都是可选的,但不能同时多于两个为非空,最终都是为了确定animation变化的起点和终点。设置了动画的起点和终点之后,中间的值都是通过插值方式计算出来的,插值计算的结果由timingFunction指定,默认timingFunction为nil,会使用liner的,也就是变化是均匀的。

CAKeyframeAnimation

帧动画。任何动画要表现出运动或者变化,至少需要两个不同的关键状态,而中间的状态的变化可以通过插值计算完成,从而形成补间动画,表示关键状态的帧叫做关键帧.CABasicAnimation其实可以看作一种特殊的关键帧动画,只有头尾两个关键帧.CAKeyframeAnimation则可以支持任意多个关键帧,关键帧有两种方式来指定,使用path或者使用values,path是一个CGPathRef的值,且path只能对CALayer的 anchorPoint 和 position 属性起作用,且设置了path之后values就不再起效了.而values则更加灵活. keyTimes这个可选参数可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的. 
还可以通过设置可选参数timingFunctions(CAKeyframeAnimation中timingFunction是无效的)为关键帧之间的过渡设置timingFunction,如果values有n个元素,那么timingFunctions则应该有n-1个.但很多时候并不需要timingFunctions,因为已经设置了够多的关键帧了,比如没1/60秒就设置了一个关键帧,那么帧率将达到60FPS,完全不需要相邻两帧的过渡效果(当然也有可能某两帧 值相距较大,可以使用均匀变化或者增加帧率,比如每0.01秒设置一个关键帧).

在关键帧动画中还有一个非常重要的参数,那便是calculationMode,计算模式.其主要针对的是每一帧的内容为一个座标点的情况,也就是对anchorPoint 和 position 进行的动画.当在平面座标系中有多个离散的点的时候,可以是离散的,也可以直线相连后进行插值计算,也可以使用圆滑的曲线将他们相连后进行插值计算. calculationMode目前提供如下几种模式 kCAAnimationLinear 
kCAAnimationDiscrete 
kCAAnimationPaced 
kCAAnimationCubic 
kCAAnimationCubicPaced

kCAAnimationLinear calculationMode的默认值,表示当关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算; 
kCAAnimationDiscrete 离散的,就是不进行插值计算,所有关键帧直接逐个进行显示; 
kCAAnimationPaced 使得动画均匀进行,而不是按keyTimes设置的或者按关键帧平分时间,此时keyTimes和timingFunctions无效; 
kCAAnimationCubic 对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,这里的数学原理是Kochanek–Bartels spline,这里的主要目的是使得运行的轨迹变得圆滑; 
kCAAnimationCubicPaced 看这个名字就知道和kCAAnimationCubic有一定联系,其实就是在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,此时keyTimes以及timingFunctions也是无效的.

CATransition

转场动画。

CAAnimationGroup

动画组。

CAMediaTimingFunction

提供了两种获得时间函数的方式:使用预定义的五种时间函数或通过给点两个控制点得到一个时间函数。有五种预定义的时间函数:kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionEaseIn
kCAMediaTimingFunctionEaseOut
kCAMediaTimingFunctionEaseInEaseOut
kCAMediaTimingFunctionDefault

CADisplayLink

一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。一旦CADisplayLink以特定的模式注册到runloop之后,每当屏幕需要刷新的时候,runloop就会调用CADisplayLink绑定的target上的selector,这时target可以读到 CADisplayLink 的每次调用的时间戳,用来准备下一帧显示需要的数据。例如一个视频应用使用时间戳来计算下一帧要显示的视频数据。在UI做动画的过程中,需要通过时间戳来计算UI对象在动画的下一帧要更新的大小等等。依托于设备屏幕刷新频率触发事件,所以其触发时间上是最准确的。也是最适合做UI不断刷新的事件,过渡相对流畅,无卡顿感

仿射变换包括如下所有变换,以及这些变换任意次序次数的组合:平移、旋转、放缩、剪切、反射

/**
struct CATransform3D
{
CGFloat    m11(x缩放),     m12(y切变),      m13(旋转),     m14();

CGFloat    m21(x切变),     m22(y缩放),      m23(),        m24();

CGFloat    m31(旋转),      m32(),          m33(),        m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);

CGFloat    m41(x平移),     m42(y平移),      m43(z平移),   m44();
};
**/

总结:CoreAnimation是跨iOS和macOS的,CoreAnimation在后台执行动画而不阻塞主线程,CoreAnimation作用于CALayer而不是UIView。

3.2.1 Core Graphics

Core Graphics是一个基于C的绘图专用的API族,它经常被称为QuartZ或QuartZ 2D,是一个二维绘图引擎,同时支持iOS和macOS。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变。

iOS系统本身提供了两套绘图的框架,即UIBezierPath和Core Graphics。前者所属UIKit,其实是对Core Graphics框架关于path的进一步封装,CoreGraphics是底层绘制框架,我们实际会用到的也就是CG开头的一些底层绘制函数和变量,这是一个纯C语言框架。使用UIBezierPath绘图只能在drawRect里进行,因为底层要用到上下文,图形上下文只能在drawRect里获取,不能在其他方法里面绘图,比如:不能在awakeFromNib里绘图。

CoreGraphics和CoreAnimation:它们都是跨iOS和Mac OS 使用的,这点区别于UIKit,并且CoreAnimation中大量使用到CoreGraphics中的类,因为实现动画要用到图形库中的东西。

CGContextRef

图形上下文是一个CGContextRef类型的数据,图形上下文相当于画板,用于封装绘图信息(画了什么)和绘图状态(线条大小、颜色等),它决定绘制的输出目标(绘制到什么地方去),目标可以是PDF文件、bitmap或者显示器的窗口。在UIView中,系统会默认创建一个Layer Graphics Context,它对应UIView的layer属性,该图形上下文可以在drawRect:方法中获取,开发者只能获取,不能自己重新创建,在该图层上下文中绘制的图形,最终会通过CALayer显示出来。因此,View之所以能显示东西,完全是因为它内部的layer。

当 UIView 需要显示时,它内部的层会准备好一个 CGContextRef(图形上下文),然后调用 delegate(这里就是 UIView)的 drawLayer:inContext: 方法,并且传入已经准备好的 CGContextRef 对象。而 UIView 在 drawLayer:inContext: 方法中又会调用自己的 drawRect: 方法。平时在 drawRect: 中通过 UIGraphicsGetCurrentContext() 获取的就是由层传入的 CGContextRef 对象,在 drawRect: 中完成的所有绘图都会填入层的 CGContextRef 中,然后被拷贝至屏幕。

5 AVPlayer

AVFoundation是一个功能齐全的高级框架,用于在 iOS、macOS、watchOS 和 tvOS 上处理基于时间的视听媒体。通过开发所需的工具提供了强大的功能集,让开发者能够基于苹果平台创建当下最先进的媒体应用程序,其针对64位处理器设计,充分利用了多核硬件优势,会自动提供硬件加速操作,确保大部分设备能以最佳性能运行。

AVFoundation的播放都围绕AVPlayer展开,AVPlayer是一个用来播放基于时间的视听媒体的控制器对象。支持播放从本地、分布下载或通过HTTP Live Streaing协议得到的流媒体,并在多种播放场景中播放这些资源。

今天的文章UIKit框架基础分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/24140.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注