UIView---汇总

视图、绘图、贴图、手势、变形、布局、动画、动力、特效
UIBezierPath、UIGestureRecognizer、CGAffineTransform、frame、bounds、center、transform、UITouch、UIEvent、Layout、Autoresizing、Auto Layout、Animation、UIImage、NSTimer、UIView、Core Animation、CALayer、CAAnimation、CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransform3D、UIDynamicAnimator、UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、 UISnapBehavior、UIPushBehavior、NSNotification
=========================================================================================================
知识点
一、绘图 今天: .绘图 .1基本概念
屏幕:很多个晶体组成的,分辨率1920X1080,一行有1920个晶体,一共有1080行,每一个晶体发三种颜色的光(red,green,blue) 图片:
点阵图:一堆点,每一个点是一个颜色,图片的分辨率指的就是存储的图片的点的个数
图片中的每一个点是用4个整数来存储的,这四个整数分别对象该点的red(-)、green(-),blue(-),alpha。一个点就需要4个字节来存储,依据这个规律就可以计算图片的大小了。但为了压缩图片的大小,将一个点周围的四个点计算一个平均值,用一个点的值来代替这四个点, 矢量图:
存储的是生成图形所需要经过计算的数学公式,所以放大后不会失真 像素 .OC对象与图形的转换
内存中的OC对象,会基于系统提供的默认规则,绘制成平面图后显示到屏幕上。
绘制的过程本质是由底层的Core Graphics 这一组C语言的API实现
系统为编程人员提供了一个可编程的接口,在指定的位置添加绘制的代码以后,就可以让系统在原有的绘制基础上增加自定义的绘制内容 .绘图的实现
[Demo1_Graphics]
实现步骤:
a。重写UIView的drawRect方法。该方法由系统自动调用,不能自己手动调用。因为drawRect方法只是系统一整套绘制流程中的一个环节。
b。获取绘制的上下文对象
c。设置上下文对象的绘制起始点
d。添加路径
e。设置描边或填充的颜色
f。绘制路径 MyView.h 继承自UIView MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{
// 获取系统的上下文对象
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextMoveToPoint(context, , );
CGContextAddLineToPoint(context, , );
CGContextAddLineToPoint(context, , );
CGContextAddLineToPoint(context
, , );
//设置描边的颜色
CGContextSetStrokeColorWithColor(context
, [[UIColor redColor] CGColor]);
CGContextSetFillColorWithColor(context, [[UIColor greenColor]CGColor]);
//按照路径描边
//CGContextStrokePath(context);
//CGContextFillPath(context);
CGContextDrawPath(context, kCGPathFillStroke); } @end
MyViewController.h
MyViewController.m
#import "MyViewController.h"
#import "MyView.h" @interface MyViewController () @end @implementation MyViewController - (void)viewDidLoad
{
[super viewDidLoad];
MyView *view = [[MyView alloc]initWithFrame:self.view.frame]; 将自定义的view加到controller
view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:view];
}

. UIBezierPath贝塞尔曲线
4.1 是OC语言对c语言绘图的一部分API的封装结果 4.2 作用:更方便的绘制直线、曲线、矩形、圆弧、椭圆等 4.3 绘制直线
【Demo2_BezierPath_Line】 path.lineWidth 设置线条宽度
path.lineCapStyle = kCGLineCapButt;设置线头儿的样式
path.lineJoinStyle = kCGLineJoinRound;设置交叉点的样式
[[UIColor redColor] setStroke];设置线条颜色
[[UIColor greenColor] setFill];设置填充色 )在故事板中拉入一个view,并在第三个检查器class与自己创建的类关联起来
)创建贝塞尔路径实例
UIBezierPath *path = [UIBezierPath bezierPath];
)绘制,填充颜色、线条显示出来
[path stroke]; [path fill];
MyView.h
MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{
//创建贝塞尔路径的实例
UIBezierPath *path = [UIBezierPath bezierPath]; [path addArcWithCenter:CGPointMake(, ) radius: startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
[path addLineToPoint:CGPointMake(, )];
//移动到起始点
//[path moveToPoint:CGPointMake(50, 50)];
//[path addLineToPoint:CGPointMake(50, 150)];
//[path addLineToPoint:CGPointMake(200, 50)];
//[path addLineToPoint:CGPointMake(50, 50)]; //设置颜色
[[UIColor redColor] setStroke];
[[UIColor greenColor] setFill]; //设置path的常用属性
path.lineWidth = ;
//设置线头儿的样式
//path.lineCapStyle = kCGLineCapButt;
//设置交叉点的样式
//path.lineJoinStyle = kCGLineJoinRound; //绘制 [path stroke];
//[path fill];
} .4绘制圆弧
【Demo2_BezierPath_Line】
M_PI_2 M_PI
[path addArcWithCenter:CGPointMake(, ) radius: startAngle:M_PI_2 endAngle:M_PI clockwise:YES]; 练习:
定制圆形的下载进度提示条
【Demo3_Custom_DownloadView】
// 重绘视图
[self setNeedsDisplay]; DownloadView.h
DownloadView.m
#import "ViewController.h"
#import "DownloadView.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet DownloadView *downloadView;
@property (weak, nonatomic) IBOutlet UISlider *slider; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
self.slider.value = ;
} - (IBAction)downloadNumber:(UISlider *)sender {
self.downloadView.progressValue = sender.value;
}
DownloadView.h
#import <UIKit/UIKit.h> @interface DownloadView : UIView @property(nonatomic,strong)UIColor *progressColor;
@property(nonatomic)CGFloat progressValue;//0~1之间的浮点数 @end DownloadView.m
#import "DownloadView.h" @implementation DownloadView //重写setter方法
- (void)setProgressValue:(CGFloat)progressValue{
// 保留原有的set方法的操作
_progressValue = progressValue;
// 重绘视图
[self setNeedsDisplay];
} - (void)drawRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath bezierPath];
//圆显示在屏幕中心
CGPoint center = CGPointMake(self.bounds.size.width/, self.bounds.size.height/);
[path addArcWithCenter:center radius :(self.bounds.size.width-)/ startAngle:M_PI_2* endAngle:self.progressValue**M_PI+M_PI_2* clockwise:YES];
if (self.progressColor) {//设置了即为设置后的颜色,如果没设置就显示默认的蓝色
[self.progressColor setStroke];
}else{
[[UIColor blueColor] setStroke];
}
path.lineWidth = ;
[path stroke]; }
@end
 
.5绘制曲线
【Demo4_BezierPath_Curve】
MyView.h
MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(, )];
起始坐标 第一个拉伸的点
[path addCurveToPoint:CGPointMake(, ) controlPoint1:CGPointMake(, ) controlPoint2:CGPointMake(, )];
[path addCurveToPoint:CGPointMake(, ) controlPoint1:CGPointMake(, ) controlPoint2:CGPointMake(, )];
[[UIColor redColor] setStroke];
path.lineWidth = ;
[path stroke];
} @end

.6绘制其他图形 (矩形,圆形,椭圆)
【Demo5_BezierPath_Others】
MyView.h
MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{
//绘制圆角矩形
// UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40, 40, 200, 150) cornerRadius:10];
//圆形
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(, , , )]; [[UIColor redColor] setStroke];
[[UIColor greenColor] setFill];
path.lineWidth = ;
[path fill];
[path stroke]; } @end

.绘制字符串
【Demo6_Draw_String】
使用NSString带有的绘制方法完成:
drawAtPoint:
drawInRect: 根据字符串的内容计算对应的高度:
boundingRectWithSize:
MyView.h
MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{
NSString *str = @"Hello World";
//设置字符串的格式
NSDictionary *strAttributes =
@{
NSFontAttributeName:[UIFont systemFontOfSize:],
NSForegroundColorAttributeName:[UIColor redColor]
};
[str drawAtPoint:CGPointMake(, ) withAttributes:strAttributes]; //长字符串的绘制
NSString *str2 = @"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog String";
//计算出该字符串所占的空间大小 //计算字符串的高
CGRect strRect = [str2 boundingRectWithSize:CGSizeMake(, ) options: attributes:strAttributes context:nil]; [str2 drawInRect:CGRectMake(, ,,strRect.size.height) withAttributes:strAttributes];
} @end

.绘制图片
【Demo7_Draw_Image】
UIImage *image = [UIImage imageNamed:@"icon80.png"];
[image drawAtPoint:CGPointMake(, )];//按图片原始大小显示
[image drawInRect:imageRect];//根据设定的范围对图片进行缩放
MyView.h
MyView.m
#import "MyView.h" @implementation MyView - (void)drawRect:(CGRect)rect
{ CGRect imageRect = CGRectMake(, , , );
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:imageRect];
//按照路径剪切
[path addClip]; UIImage *image = [UIImage imageNamed:@"icon80.png"];
//[image drawAtPoint:CGPointMake(50, 50)];
[image drawInRect:imageRect];
} @end

作业:
.参考资源1,绘制聊天气泡
写一个视图类:MessageView
+message:NSString 要求:
a。只做保持在视图右上角的情况
b。最宽不能超过200,高度根据消息的内容来计算
ViewController.h
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
ViewController.m
#import "ViewController.h"
#import "TRMessageView.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; TRMessageView *view = [[TRMessageView alloc]initWithMessage:@"中文测试一下整体到了屏幕的右侧会是什么样子呢?"];
view.frame = CGRectMake(, , , view.height);
[self.view addSubview:view]; TRMessageView *view2 = [[TRMessageView alloc]initWithMessage:@"你来我往的对话中,为什么会有一条灰色的这么难看的横线显示在哪里呢?"];
view2.frame = CGRectMake(, +view.height, , view2.height);
[self.view addSubview:view2]; }
@end TRMessageView.h
#import <UIKit/UIKit.h> @interface TRMessageView : UIView @property(nonatomic,readonly)CGFloat height;
-(instancetype)initWithMessage:(NSString *)message; @end
TRMessageView.m
#import "TRMessageView.h" @interface TRMessageView()
@property(nonatomic,strong)NSString *message;
@property(nonatomic)CGFloat messageWidth;
@property(nonatomic)CGFloat messageHeight;
@property(nonatomic,readwrite)CGFloat height;
@end @implementation TRMessageView -(instancetype)initWithMessage:(NSString *)message{
self = [super init];
if (self) {
self.message = message;
CGRect msgRect = [self.message boundingRectWithSize:CGSizeMake(, )
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{
NSFontAttributeName:[UIFont systemFontOfSize:]
}
context:nil];
self.messageWidth = msgRect.size.width;
self.messageHeight = msgRect.size.height;
self.height = +self.messageHeight;
self.backgroundColor = [UIColor whiteColor];
}
return self;
} - (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context); //绘制气泡
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(-self.messageWidth, , self.messageWidth+, self.messageHeight+) cornerRadius:];
[path moveToPoint:CGPointMake(, self.messageHeight+)];
[path addLineToPoint:CGPointMake(, +self.messageHeight)];
[path addLineToPoint:CGPointMake(, +self.messageHeight)];
[[UIColor lightGrayColor] setFill];
[path fill]; CGContextRestoreGState(context);
CGContextSaveGState(context); //绘制字符串
[self.message drawInRect:CGRectMake(-self.messageWidth, , self.messageWidth, self.messageHeight) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:],NSForegroundColorAttributeName:[UIColor whiteColor]}];
} @end

.绘制饼图
写一个视图类:
PieChartView
+radius:CGFloat半径
+width:CGFloat 线宽
+data:NSArray数据
+[item]: PieChartViewItem PieChartViewItem
+color:UIColor 颜色
+value:CGFloat ~ 占总体的百分比
TRPieChartViewItem.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> @interface TRPieChartViewItem : NSObject @property(nonatomic,strong)UIColor *color;
@property(nonatomic)CGFloat value; -(instancetype)initWithColor:(UIColor *)color andValue:(CGFloat)value; @end TRPieChartViewItem.m
#import "TRPieChartViewItem.h" @implementation TRPieChartViewItem - (instancetype)initWithColor:(UIColor *)color andValue:(CGFloat)value{
self = [super init];
if (self) {
self.color = color;
self.value = value;
}
return self;
} @end
TRPieChartView.h
#import <UIKit/UIKit.h> @interface TRPieChartView : UIView @property(nonatomic)CGFloat radius;
@property(nonatomic)CGFloat lineWidth;
@property(nonatomic,strong)NSArray *data; @end TRPieChartView.m
#import "TRPieChartView.h"
#import "TRPieChartViewItem.h" @implementation TRPieChartView -(NSArray *)data{
if (!_data) {
TRPieChartViewItem *item1 = [[TRPieChartViewItem alloc]initWithColor:[UIColor redColor] andValue:0.25];
TRPieChartViewItem *item2 = [[TRPieChartViewItem alloc]initWithColor:[UIColor blueColor] andValue:0.5];
TRPieChartViewItem *item3 = [[TRPieChartViewItem alloc]initWithColor:[UIColor blackColor] andValue:0.15];
TRPieChartViewItem *item4 = [[TRPieChartViewItem alloc]initWithColor:[UIColor greenColor] andValue:0.1]; _data = @[item1,item2,item3,item4];
}
return _data;
} -(CGFloat)lineWidth{
if (!_lineWidth) {
_lineWidth = ;
}
return _lineWidth;
} -(CGFloat)radius{
if (!_radius) {
_radius = ;
}
return _radius;
} - (void)drawRect:(CGRect)rect {
CGFloat startAngel = M_PI_2*;
for (TRPieChartViewItem *item in self.data) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
UIBezierPath *path = [UIBezierPath bezierPath];
// 画弧线
[path addArcWithCenter:CGPointMake(self.frame.size.width/, self.frame.size.height/) radius:self.radius startAngle:startAngel endAngle:(item.value**M_PI+startAngel) clockwise:YES];
startAngel +=item.value**M_PI;
//画直线
[path addLineToPoint:CGPointMake(self.frame.size.width/, self.frame.size.height/)];
//线宽
path.lineWidth = self.lineWidth;
path.lineCapStyle = kCGLineCapButt;
//填充色
[item.color setFill];
//线条颜色
//[[UIColor grayColor] setStroke];
//绘制填充边线
[path fill];
//[path stroke];
CGContextRestoreGState(context);
CGContextSaveGState(context);
}
} @end

.自定义Cell,图片是圆角的
新闻客户端中的自定义Cell,里面的图片做成圆角的
===============================================================
知识点
二、贴图、美化 . iOS的设备的种类
.1设备的分辨率和坐标系大小(2倍关系) .2Retina屏设备对图片的处理
a。程序中的图片:在不同尺寸的图片名称上添加@2x 或 @3x这样的标识即可。系统会根据当前设备的不同,来选择加载哪个版本的图片
b。AppIcon应用程序图标:屏幕上有一个尺寸,设置界面上也有图标,搜索结果中也有图标,设定图片资源库中的AppIcon里面不同尺寸的图片后,完成设置 、9切片技术
2.1 为什么需要9切片( Slice技术)
用代码来实现基于一个简单图片,产生不同尺寸的图片的一种手段 .2切片原则
4个角不变,中间部分可以横向或纵向拉伸或复制
两种模式:Tile 切片复制 (默认模式)
Stretch切片拉伸 2.3 实现方法
a。利用xcode工具中的图片资源库中的9切片功能
b。编写代码实现切片 如:
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *btnImageView; 和故事版中的image连线
@end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UIImage *btnImage = [[UIImage imageNamed:@"delete_btn"] resizableImageWithCapInsets:UIEdgeInsetsMake(, , , ) resizingMode:UIImageResizingModeStretch]; 编代码实现九切片功能
self.btnImageView.image = btnImage; } @end . 对控件的贴图美化 3.1 UIButton
四种状态的设置:通过代码或故事板
四种状态的切换:
Normal:正常
Highlighted:高亮,按下后不抬起的状态
Selected:通过修改selected属性设置 .selected=YES(按钮被选中) Disabled:通过修改enabled属性设置 .enabled=NO(按钮不可用状态) - (IBAction)changeState:(UIButton *)sender { 将按钮连线,用代码修改
[self.button setSelected:!sender.selected];//连续点击,持续变化
// [sender setSelected:YES];//是否改变,只变化一次
//sender setEnabled:YES
// 按钮摆在那不动:normal
// 点下,不抬起:highLighted
// 点下,松手:normal
// 进入到seleted状态:用代码修改seleted属性
// enabled状态:用代码改
} 如下:view+image+button+field,通过故事板实现即可,不需要编程

3.2 UISlider
setMaximumTrackImage:设置滑动过的区域的背景图
setMinimumTrackImage:设置未滑动到的区域的背景图
setThumbImage:设置滑块中拖动按钮部分的图片
如:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.slider setMaximumTrackImage:[[UIImage imageNamed:@"playing_volumn_slide_foreground"] resizableImageWithCapInsets:UIEdgeInsetsMake(, , , ) resizingMode:UIImageResizingModeTile]forState:UIControlStateNormal];
[self.slider setMinimumTrackImage:[UIImage imageNamed:@"playing_volumn_slide_bg"] forState:UIControlStateNormal];
[self.slider setThumbImage:[UIImage imageNamed:@"playing_volumn_slide_sound_icon"] forState:UIControlStateNormal];
} . tintColor
统一管理一个视图中所有子视图和子视图的子视图的颜色,批量修改一些视图的颜色
.颜色受控制的因素
拥有 xxxTintColor属性,如UISwitch,可以使用属性修改
没有xxxTintColor,受从UIView中继承来的tintColor影响
.self.window.tintColor影响整个应用的风格,除非某一个视图特别设置了自己的tintColor颜色 . UIAppearance
遵守此协议的对象,可以批量设置某种控件的外观(颜色、贴图等)(只针对某一类控件)
.获取方式
+(instancetype)appearance;
.使用方式
拿到此对象后,通过这个对象设置背景、颜色等来批量设置某一类控件的外观
[ [ UISlider appearance] setTintColor: [ UIColor redColor ] ]
如:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UISlider appearance]setTintColor:[UIColor purpleColor]];
[[UIButton appearance]setBackgroundImage:[UIImage imageNamed:@"delete_btn"] forState:UIControlStateNormal]; return YES;
} . UINavigationBar美化 6.1 设置NavigationBar的颜色 //设置导航栏的背景色
naviBar.barTintColor = [UIColor blackColor];
//设置是否透明
naviBar.translucent = YES; 6.2 给NavigationBar贴图
//设置背景图(图片的高度,竖屏时一般设置为64个点,横屏时高度是52个点)
[naviBar setBackgroundImage:[UIImage imageNamed:@"NavigationBarDefault"] forBarMetrics:UIBarMetricsDefault];竖屏显示
[naviBar setBackgroundImage:[UIImage imageNamed:@"NavigationBarLandscapePhone"] forBarMetrics:UIBarMetricsLandscapePhone];横屏显示 6.3 设置返回按钮的图片
naviBar.backIndicatorImage = [UIImage imageNamed:@"back_btn"];
naviBar.backIndicatorTransitionMaskImage =[UIImage imageNamed:@"back_btn"];
naviBar.tintColor = [UIColor redColor];设置按钮颜色 6.4 设置标题栏的文字字体
naviBar.titleTextAttributes =
@{
NSFontAttributeName:[UIFont boldSystemFontOfSize:],
NSForegroundColorAttributeName:[UIColor redColor]
}; 6.5 设置标题为任意视图
UIStepper *stepper = [[UIStepper alloc]init];
self.navigationItem.titleView = stepper; 6.6 设置状态栏风格 (显示电池栏的风格)
//重写方法,用于设置状态的风格
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
} 6.7 是否显示状态栏
//重写方法,设置状态栏隐藏
- (BOOL)prefersStatusBarHidden
{
return YES;
} 6.8 隐藏NavigationBar
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden animated:YES]; ViewController.h
ViewController.m
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UINavigationBar *naviBar = self.navigationController.navigationBar;
naviBar.barTintColor = [UIColor blackColor];
naviBar.translucent = YES;
//设置背景图
[naviBar setBackgroundImage:[UIImage imageNamed:@"NavigationBarDefault"] forBarMetrics:UIBarMetricsDefault];
[naviBar setBackgroundImage:[UIImage imageNamed:@"NavigationBarLandscapePhone"] forBarMetrics:UIBarMetricsLandscapePhone];
//设置返回按钮的的图片
naviBar.backIndicatorImage = [UIImage imageNamed:@"back_btn"];
naviBar.backIndicatorTransitionMaskImage =[UIImage imageNamed:@"back_btn"];
naviBar.tintColor = [UIColor redColor];
//设置标题栏的文字字体
naviBar.titleTextAttributes =
@{
NSFontAttributeName:[UIFont boldSystemFontOfSize:],
NSForegroundColorAttributeName:[UIColor redColor]
};
//设置标题为其他视图
//UITextField *textField = [[UITextField alloc]init];
UIStepper *stepper = [[UIStepper alloc]init];
self.navigationItem.titleView = stepper;
} - (IBAction)hideNavigationBar:(UIButton *)sender {
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden animated:YES]; } @end
GreenViewController.h
GreenViewController.m
#import "GreenViewController.h" @interface GreenViewController () @end @implementation GreenViewController //重写方法,用于设置状态的风格
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
} //重写方法,设置状态栏隐藏
- (BOOL)prefersStatusBarHidden
{
return YES;
}   
点击item得到的界面 . UITableViewCell的背景贴图 实现步骤:
a。故事板中修改TableView的分割线(Separator)为None
b。故事板中修改TableViewCell的背景色(Background)为clearColor
c。cell.backgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"list"]];
cell.selectedBackgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"listSeleted”]];选中后的背景图设置显示
如:
MyTableViewController.h
MyTableViewController.m
#import "MyTableViewController.h" @interface MyTableViewController () @end @implementation MyTableViewController - (void)viewDidLoad
{
[super viewDidLoad]; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return ;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.textLabel.text = @"Hello World";
cell.backgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"list"]];
cell.selectedBackgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"listSelected"]];
return cell;
}
 格显示的是虚线 作业: 聊天气泡:
用9slicing实现 类:MessageView
+message:NSString
+fromMe:BOOL
+messageLabel:UILabel
+messagePopImageView:UIImageView//图,切片 效果:
a。屏幕最上方添加一个文本框和一个发送按钮
b。屏幕下方已经存在一些聊天的消息,其中自己说的内容在屏幕右侧,对方说的内容在屏幕的左侧
c。在文本框中输入内容后,算作自己说的内容,然后点击发送按钮,在界面中追加一个气泡,在屏幕的右侧,里面显示文本框中输入的内容,并且键盘收起,文本框内容清空 -(void)refresh{
if(fromMe)
显示蓝色的气泡在右上角
大小根据message属性计算
else
显示灰色的气泡在左上角
大小,计算
}
参考:H02资源包 追加:看Apple官方的NavigationBar的demo
=====================================================================
知识点
三、手势(UIGestureRecognizer)、变形
.手势(GestureRecognizer) .1什么是手势
用户在view上的一些触屏操作,诸如 点、滑动、捏合。。。 1.2 手势的分类
a。一次性手势
触屏动作发生以后,方法只会响应一次。如:点击以下屏幕、解锁动作 b。连续性手势
触屏动作发生以后,方法会连续响应多次。如:长按、捏合、移动、旋转 1.3 手势的本质
本质是一个对象,当用户针对视图发生了一定的动作之后,系统会检测到该动作,并根据具体的动作创建不同种类的手势对象,该对象中会存储与动作有关的一些数据,如触屏动作的坐标点、滑动的快慢、移动的距离。
如果发生的是一次性手势动作,那么就调用一次方法,如果发生的是连续性手势动作,那么就多次调用响应方法 1.4 如何使用手势
step1:创建手势对象
step2:设置与该种手势相关的属性
step3:将手势对象与需要检测的视图关联在一起 1.5 具体的手势种类
所有手势的父类:UIGestureRecognizer
6种手势: UIXXXGestureRecognizer
UITapGestureRecognizer 点击一下屏幕
UISwipeGestureRecognizer 轻扫屏幕,如解锁
UIPinchGestureRecognizer 捏合手势
UIPanGestureRecognizer 移动手势
UILongPressGestureRecognizer 长按手势
UIRotationGestureRecognizer 旋转手势 .具体的手势使用 2.1 UITapGestureRecognizer (一次性手势)
【Demo1_TapGestureRecognizer】 常用属性:
.numberOfTapsRequired 设置点击数 手指点几下
.numberOfTouchesRequired 设置触点数 几个手指点击
CGPoint location=[gr locationInView:self.view];获取点击动作时,出点的绝对坐标
ViewController.m
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
//step1 创建手势对象
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
//step2 设置手势的属性
tapGR.numberOfTapsRequired = ;
tapGR.numberOfTouchesRequired = ;
//step3 将手势与视图关联起来
[self.view addGestureRecognizer:tapGR]; } -(void)tap:(UITapGestureRecognizer *)gr
{
CGPoint location = [gr locationInView:self.view];
NSLog(@"(%.2f,%.2f)",location.x,location.y);
} @end 2.2 UISwipeGestureRecognizer(一次性手势)
【Demo2_SwipeGestureRecognizer】
常用属性:
.numberOfTouchesRequired触点的个数
.direction 轻扫的方向
注意:direction属性为枚举值,并且是可以进行组合的枚举值,多个枚举值之间使用 “|”按位“或”进行运算。
补充:<<? 表达式的含义是:对于二进制的数字1进行向左移位,符号右侧是几,就向左移动几位。 ViewController.m
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *swipeGR = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
swipeGR.numberOfTouchesRequired = ;
//设置轻扫动作的方向
swipeGR.direction = UISwipeGestureRecognizerDirectionLeft|UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeGR];
} -(void)swipe:(UISwipeGestureRecognizer *)gr{
NSLog(@"swipe... ...");
}
@end 2.3 UILongPressGestureRecognizer(连续性手势)
【Demo3_LongPressGestureRecognizer】
常用属性:
longGR.minimumPressDuration = ;//设置最少按下的持续时间 ViewController.m
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
//设置最少按下的持续时间
longGR.minimumPressDuration = ;
[self.view addGestureRecognizer:longGR];
} -(void)longPress:(UILongPressGestureRecognizer *)gr{
NSLog(@"long press... ...");
} @end 2.4 UIPinchGestureRecognizer(连续性手势,捏合)
【Demo4_PinchGestureRecognizer】
CGFloat scale = gr.scale; //手势的变化比率,向外扩展时,为大于1的数,向内捏合时,为小与1的数
CGFloat velocity = gr.velocity;//手势的变化速率,向外扩展时,为正数,向内捏合时,为负数 以上两个属性不是用来设置的,而是在手势发生时用来读取的。 练习:
界面上 有一个UITextView,看小说,增加手势识别,如果快速扩,小说出现,快速捏,小说隐藏,速度不快时,代表放大或缩小小说的文字大
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextView *textView;//连线 @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
self.textView.editable = NO;
UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
[self.view addGestureRecognizer:pinchGR];
} -(void)pinch:(UIPinchGestureRecognizer *)gr{
CGFloat scale = gr.scale;比率
CGFloat velocity = gr.velocity; 速率
//NSLog(@"scale=%.2f,velocity=%.2f",scale,velocity);
if (velocity > ) {
self.textView.hidden = NO;
} else if (velocity < -){
self.textView.hidden = YES;
} else{
// 改变字体
self.textView.font = [UIFont systemFontOfSize:*scale];
}
} 2.5 UIRotationGestureRecognizer (连续性手势,旋转)
【Demo5_RotationGestureRecognizer】
常用属性:
CGFloat rotation = gr.rotation;
代表手势旋转的弧度。顺时针旋转时,为正数,逆时针旋转时为负数
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;//image上添加图片 @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
[self.view addGestureRecognizer:rotationGR];
} -(void)rotation:(UIRotationGestureRecognizer *)gr{
//CGFloat rotation = gr.rotation;
//NSLog(@"%.2f",rotation);
//实现图片的选转
self.imageView.transform = CGAffineTransformMakeRotation(gr.rotation);
} @end 2.6 UIPanGestureRecognizer(连续性手势,拖动)
【Demo6_PanGestureRecognizer】
常用属性:
//获取移动到的位置在视图坐标系中的绝对位置
CGPoint location = [gr locationInView:self.view];
//获取移动到的新位置相对于移动动作起始点的坐标的横纵向的偏移
CGPoint translation = [gr translationInView:self.view]; ViewController.m
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:panGR];
} -(void)pan:(UIPanGestureRecognizer *)gr{
//获取移动到的位置在视图坐标系中的绝对位置
CGPoint location = [gr locationInView:self.view];
//获取移动到的新位置相对于移动动作起始点的坐标的横纵向的偏移
CGPoint translation = [gr translationInView:self.view];
self.myImageView.transform=CGAffineTransformMakeTranslation(translation.x, translation.y);//相对
NSLog(@"location:(%.2f,%.2f) translation:(%.2f,%.2f)",location.x,location.y,translation.x,translation.y);
}
@end .变形(Transform)
.1什么是变形
视图发生了位移、缩放、旋转这样的变化叫做变形。 3.2 如何实现变形?
通过修改视图对象的.transform属性完成变化的效果
位移:translation
缩放:scale
旋转:rotation 3.3 transform属性
类型:CGAffineTransform(仿射)类型的结构体
结构体中包含了6个可变的值和3个定值组成的 X 3的矩阵,修改了6个数据中的某一个或某几个就可以实现变形,实际上这6个数很难计算,借助于一些系统的API实现数值的改变。 3.4 修改transform属性的API 位移变换:CGAffineTransformMakeTranslation() 相对最原始变化的
CGAffineTransformTranslate() 相对修改后变化的 缩放变换:CGAffineTransformMakeScale()
CGAffineTransformScale() 旋转变换:CGAffineTransformMakeRotation()
CGAffineTransformRotate() 回到原始状态: CGAffineTransformIdentity 重点注意:变形与自动布局是冲突的,所以在使用变形时,一定要关闭AutoLayout,不关闭的话,产生的效果无法预计。 【Demo7_Transform】 界面创建后,没有做任何变形之前,系统会将每一个视图当前的transform记录到一个常量中CGAffineTransformIdentity,当使用Makexxx()方法进行仿射变换,计算新的矩阵,都是基于这个常量进行变形计算的。当使用没有Make的那组方法时,每次计算新的矩阵都是以传入的transform的值作为基准。 3.5 transform属性的初始常量
CGAffineTransformIdentity 如:通过点击按钮实现图片变化
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad]; }
//位移
- (IBAction)translation:(UIButton *)sender {
//self.imageView.transform = CGAffineTransformMakeTranslation(0, 350);
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, , );
//self.imageView.center = CGPointMake(self.imageView.center.x+2, self.imageView.center.y+2);
} //缩放
- (IBAction)scale:(UIButton *)sender {
//self.imageView.transform = CGAffineTransformMakeScale(1.5, 1.5);
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, 1.2, 1.2);
} //旋转
- (IBAction)rotation:(UIButton *)sender {
//self.imageView.transform = CGAffineTransformMakeRotation(M_PI_4);
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, -M_PI_4);
}
//回到原始位置
- (IBAction)identity:(UIButton *)sender {
self.imageView.transform = CGAffineTransformIdentity;
} @end
 .手势+变形 4.1 使用pan手势实现位移
CGPoint translation = [gr translationInView:self.view];
CGPoint center = CGPointMake(self.imageView.center.x+translation.x, self.imageView.center.y+translation.y);
self.imageView.center = center;
//将这一次移动的偏移量归零
[gr setTranslation:CGPointZero inView:self.view]; 4.2 使用pinch手势实现缩放变形
// 实现图片的缩放
self.imageView.transform = CGAffineTransformScale(self.imageView.transform,gr.scale, gr.scale);
gr.scale = ;// 4.3 使用rotation手势实现旋转变形 // 实现图片的旋转
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, gr.rotation);
gr.rotation = ; 4.4 多手势共存的问题
解决步骤:
a。遵守协议
b。设置各个手势的代理
c。实现一个方法 (遵守协议时点开发放寻找粘贴过来即可)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
} .在storyboard中实现手势
【Demo8_GestureRecognizer_Storyboard】
.从资源库中拖拽某种类型的手势对象到视图中
.如果拖拽时就想与某视图关联起来,那么就把手势对象拖到视图中,如果不想关联,那么就拖拽到场景的资源条上
.选中要关联的视图,按住control,连线到场景的资源条上的手势对象
.
TransformViewController.m
#import "TransformViewController.h" @interface TransformViewController ()<UIGestureRecognizerDelegate> //遵守协议
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation TransformViewController - (void)viewDidLoad
{
[super viewDidLoad];
//pan手势
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:panGR];
//pinch手势
UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
//设置pinch的代理
pinchGR.delegate = self;
[self.view addGestureRecognizer:pinchGR];
//rotation手势
UIRotationGestureRecognizer *rotationGR = [[ UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
//设置rotation的代理
rotationGR.delegate = self;
[self.view addGestureRecognizer:rotationGR];
} -(void)pan:(UIPanGestureRecognizer *)gr{
//实现图片的位移
//CGPoint location = [gr locationInView:self.view];
CGPoint translation = [gr translationInView:self.view];
CGPoint center = CGPointMake(self.imageView.center.x+translation.x, self.imageView.center.y+translation.y);
self.imageView.center = center;
//将这一次移动的偏移量归零
[gr setTranslation:CGPointZero inView:self.view];
} -(void)pinch:(UIPinchGestureRecognizer *)gr{
// 实现图片的缩放
self.imageView.transform = CGAffineTransformScale(self.imageView.transform,gr.scale, gr.scale);
gr.scale = ;
} -(void)rotation:(UIRotationGestureRecognizer *)gr{
// 实现图片的旋转
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, gr.rotation);
gr.rotation = ;
} - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
} @end  【综合练习】:
图片查看器:
)使用代码想view中添加一个UIImageView对象,UIImageView的大小和图片大小一致,找一张大图(用大象) )使用center属性将ImageView移动到屏幕的*
)使用transform属性将imageView缩放到屏幕刚好能显示的下正常图片的内容,且保持宽高比
)对imageView增加rotation手势,支持图片旋转
)对imageView增加pinch手势,支持图片的缩放
)对imageView增加pan手势,支持图片的移动
)对imageView增加tap手势,床架回到到第3步
注意:手势不要添加到self.view,要添加到图片上,记得打开imageView的用户交互(.userInteractionEnable=YES) 作业:
ViewController.m #import "ViewController.h" @interface ViewController ()<UIGestureRecognizerDelegate>//遵守协议 @property(nonatomic,strong)UIImageView *imageView;
@end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
//创建ImageView
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Elephant.jpg"]];
self.imageView = imageView;
//设置初始的状态
[self loadImageView];
//添加
[self.view addSubview:imageView];
//开启交互
imageView.userInteractionEnabled = YES; //设置手势
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
tapGR.numberOfTapsRequired = ;
[self.imageView addGestureRecognizer:tapGR]; UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
pinchGR.delegate = self;
[self.imageView addGestureRecognizer:pinchGR]; UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.imageView addGestureRecognizer:panGR]; UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
rotationGR.delegate =self;
[self.imageView addGestureRecognizer:rotationGR];
} -(void)loadImageView{
//设置中心点
self.imageView.center = self.view.center;
CGFloat scaleX = self.view.bounds.size.width/self.imageView.bounds.size.width;
CGFloat scaleY = self.view.bounds.size.height/self.imageView.bounds.size.width;
self.imageView.transform = CGAffineTransformMakeScale(MIN(scaleX, scaleY), MIN(scaleX, scaleY));
}
//移动
-(void)pan:(UIPanGestureRecognizer *)panGR{
CGPoint translation = [panGR translationInView:self.view];
CGPoint center = CGPointMake(self.imageView.center.x+translation.x, self.imageView.center.y+translation.y);
self.imageView.center = center;
[panGR setTranslation:CGPointZero inView:self.view]; } //缩放
-(void)pinch:(UIPinchGestureRecognizer *)pinchGR{
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, pinchGR.scale, pinchGR.scale);
pinchGR.scale = ; } //旋转
-(void)rotation:(UIRotationGestureRecognizer *)rotationGR{
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, rotationGR.rotation);
rotationGR.rotation = ;
} //回到起始点中心点
-(void)tap:(UITapGestureRecognizer *)gr{
[self loadImageView];
} //实现旋转和缩放同时实现的方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
} @end
 www.raywenderlich.com .根据文档,把一个TableCell实现移动。
长按Cell后,拖到哪里就放在哪里
====================================================================
知识点
五、坐标系、触控 .坐标系(frame、bounds、center、transform)
1.1 frame属性 【Demo1_Frame_Bounds_Center_Transform】 a。什么是frame?
类型:CGRect结构体类型
作用:该视图左顶点在父视图的坐标系中的位置,以及,该视图在父视图中占据的宽和高 b。直接修改了frame属性时,其他属性如何变化?
bounds:会被改变
center:会被改变
transform:不会被改变 c。什么时候使用到frame?
当把一个视图添加到父视图中时,一定要重设定frame属性 1.2 bounds属性 (相对与自己的) a。什么是bounds属性?
类型:CGRect结构体类型
作用:描述的是该视图的坐标系顶点的值,以及该视图自身的大小 b。直接修改了bounds属性时,其他属性如何变化?
frame: 会被改变
center:不会被改变
transform:不会被改变 c。什么时候使用bounds属性?
当需要定位视图时,要读取父视图的大小,那么就是用父视图的bounds
当修改视图内的子视图的位置时,可以修改视图的bounds的坐标起点,从而让子视图的位置发生偏移,实现移动的效果
#import "OtherViewController.h" @interface OtherViewController () @end @implementation OtherViewController - (void)viewDidLoad
{
[super viewDidLoad];
UIView *view1=[[UIView alloc]initWithFrame:CGRectMake(, , , )];
view1.backgroundColor=[UIColor redColor];
[self.view addSubview:view1]; }
- (IBAction)button:(UIButton *)sender {
[self.view setBounds:CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y+, self.view.bounds.size.width, self.view.bounds.size.height)];
} @end
点击按钮,图片垂直向上移动 1.3 center属性 a。什么是center属性?
类型:CGPoint结构体类型
作用:描述的是该视图的中心点,在父视图坐标中的位置 b。直接修改了center属性时,其他属性如何变化?
frame:会被改变
bounds:不会被改变
transform:不会被改变 c。什么时候用center?
需要修改视图的位置,也就是位移时,需要修改center 1.4 transform属性 a。什么是transform属性?
类型:CGAffineTransform结构体类型
作用:描述该视图的变形状态 b。直接修改了transform属性时,其他属性如何变化?
frame:会
bounds:不会
center:不会 c。结论:
变形前,frame和bounds保持变化的一致性,变形后,frame代表的是在视图中表现出来的外观,所以会随着变形而记录不同的外观状态,但bounds不是用来表现的,只是记录大小的,所以不会改变,bounds的坐标系也不会改变
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
[self print];
}
//直接修改frame
- (IBAction)changFrame:(UIButton *)sender {
CGRect frame=self.imageView.frame;
frame.origin.x+=;
frame.origin.y+=;
frame.size.width+=;
frame.size.height+=;
self.imageView.frame=frame;
[self print];
}
//直接修改bounds
- (IBAction)changeBounds:(UIButton *)sender {
CGRect bounds=self.imageView.bounds;
//bounds.origin.x+=4;
// bounds.origin.y+=4;
bounds.size.width+=;
bounds.size.height+=;
self.imageView.bounds=bounds;
[self print]; }
//直接修改center
- (IBAction)changeCenter:(UIButton *)sender {
CGPoint center=self.imageView.center;
center.x+=;
center.y+=;
self.imageView.center=center;
[self print];
}
//直接修改transform
- (IBAction)changeTransform:(UIButton *)sender {
self.imageView.transform=CGAffineTransformTranslate(self.imageView.transform, , );
self.imageView.transform=CGAffineTransformScale(self.imageView.transform, 1.3, 1.3);
self.imageView.transform=CGAffineTransformRotate(self.imageView.transform, M_1_PI);
[self print];
} -(void)print{
NSLog(@"\nframe:%@\nbounds:%@\ncenter:%@\ntransform:%@",
NSStringFromCGRect(self.imageView.frame),
NSStringFromCGRect(self.imageView.bounds),
NSStringFromCGPoint(self.imageView.center),
NSStringFromCGAffineTransform(self.imageView.transform)); }
@end 点击了bounds按钮
原始:
frame:{{, }, {, }}
bounds:{{, }, {, }}
center:{, }
transform:[, , , , , ]
点击后:
frame:{{, }, {, }}
bounds:{{, }, {, }}
center:{, }
transform:[, , , , , ] .触控(UITouch)
2.1 是什么?
是一个UITouch类型的对象,当用户touch视图时,会自动产生UITouch对象 2.2 如何获取Touch
需要自定义视图类,覆盖类中的指定的方法,在方法中才能获取到这个Touch对象 2.3 有什么用?
可以跟踪用户在视图上手指移动的轨迹,判断用户的意图,以此进行绘图、涂鸦、手写等操作 2.4 怎么用?
step1:自定义一个视图类
step2:重写类中的方法即可
touchesBegan:withEvent://手指接触视图时调用
touchesMoved:withEvent://手指在视图上移动时调用
touchesEnded:withEvent://手指离开视图时调用 注意:手势是对触控的一个封装 【Demo3_UITouch】
MyView.h(自己创建的类,关联到VC)
#import "MyView.h"
@implementation MyView //手指接触调用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch*touch=[touches anyObject];
CGPoint location=[touch locationInView:self];
NSLog(@"手指接触屏幕:(%.2f,%.2f)",location.x,location.y); }
//手指在视图移动时
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch*touch=[touches anyObject];
CGPoint location=[touch locationInView:self];
NSLog(@"手指在视图上移动:(%.2f,%.2f)",location.x,location.y);
} //离开时
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(@"手指离开视图");
} @end
手指接触屏幕:(147.00,87.00)
手指在视图上移动:(146.50,89.00)
手指在视图上移动:(148.50,92.00)
手指离开视图 练习:简易画板
【Demo4_Touch_Paint】
PaintView.h
PaintView.m
#import "PaintView.h" @interface PaintView () //记录所有路径的可变数组
@property(nonatomic,strong)NSMutableArray *paths; @end @implementation PaintView - (NSMutableArray *)paths{
if (!_paths) {
_paths = [NSMutableArray array];
}
return _paths;
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//获取UITouch对象
UITouch *touch = [touches anyObject];
CGPoint startPoint = [touch locationInView:self]; // 创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:];
[path moveToPoint:startPoint]; // 将路径对象记录到数组属性中
[self.paths addObject:path];
} -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self];
//将刚刚创建的路径添加一个直线到该点
//从数组中取出最后一个元素,就是刚刚手指落下时
//创建的路径
UIBezierPath *currentPath = [self.paths lastObject];
[currentPath addLineToPoint:currentPoint];
//重绘
[self setNeedsDisplay];
} - (void)drawRect:(CGRect)rect
{
//绘制所有路径
for (UIBezierPath *path in self.paths) {
[path stroke];
}
} @end
 作业: . 做一个界面上画矩形的效果 按下手指后开始画,拖动时大小变化,松手后定在屏幕上,支持绘制多个矩形 . 在作业1的基础上,支持选择绘制的颜色,支持线条的粗细设置 . 在作业1或2的基础上,增加橡皮功能 . 支持各种形状—矩形,圆角矩形,椭圆。。。。
=======================================================================================================
知识点
六、布局
.布局 (Layout)控制器中的view 1.1 什么是布局?
是指在一个视图中如何摆放它的子视图(安排子视图的位置和大小) 1.2 为什么要布局?
屏幕的尺寸会经常发生变化或者随着设备不同,屏幕尺寸也会不同,只要屏幕大小发生了变化,坐标系也会随之变化,于是变化前设置的frame在新的坐标系中定位就会与期待的不符,所以就需要在屏幕发生变化时重新布局指定frame
【Demo5_Layout】 1.3 可能导致屏幕大小发生变化的原因
a。设备不同(.5寸 4存。。。。)
b。屏幕方向不同
c。状态栏
隐藏
特殊的状态栏:来电时,绿色状态栏
录音时,红色状态栏
开启个人热点,蓝色状态栏
d。各种Bar
NavigationBar: / 如果设置了prompt,bar会更高
TabBar:49个点高
ToolBar:/ 个点
iOS7中,导航栏挤占了状态栏,高度是64/ e。键盘
弹出时挤占屏幕,高度不确定,因为中英文键盘的高度不同 1.4 如何布局 方法一:纯代码布局,古老的方法
理念:当屏幕发生变化时,自动执行一段我们写好的代码,代码中重新计算了视图的frame,从而达到在新坐标系下重新定位的目的
特点:功能强大,非常繁琐 方法二:Auto Resizing 以前的一种自动布局技巧
理念:记录视图与父视图的边缘为可调整或固定值,然后屏幕发生变化时,依据这段相对的距离,重新布局视图
特点:操作简单,功能有限 方法三:Auto Layout 最新的自动布局方法
理念:将视图与视图之间的位置以及视图自身的大小,用多个约束来记录,当屏幕发生变化时,系统根据定好的约束,自动计算满足该约束情况下的新的坐标值,然后调整位置
特点:简单易用 注意:以上的布局方式,选择其一使用。 . 纯代码布局 4.1 理念:在屏幕大小发生变化时,通过代码的方式改变视图的frame 4.2 重写控制器中方法:viewDidLayoutSubviews:即可
该方法,在屏幕发生变化时,由系统自动调用,所以,改变frame的代码写在这个方法中就会被自动执行了。 注意:使用纯代码布局时,一定要关闭AutoLayout,否则代码可能会无效 【Demo5_Layout】 【练习】
.两个等宽的按钮,高40,有背景色
———————————————
| |
|--[button1]--[button2]--|
.在1的基础上,加一个大小会变化的ImagView(内有图片)
imageView离屏幕上、下、左、右保持70,,,
.在2的基础上,增加三个按钮,大小是20X20,永远排列在屏幕的右下角
【b1】--【b2】--【b3】--|
|
———————————————|
b1,,3离下边缘都是20个点
时间:半个小时 补充: 关掉AutoLayout以后,drawRect方法中针对视图绘制的图形在屏幕旋转时会被拉伸,解决该问题的话,需要设置视图的“contentMode”设置为“redraw”即可 解决方法:设置视图的的contentMode为redraw即可 ViewController.h
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *button1;
@property (weak, nonatomic) IBOutlet UIButton *button2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *b1;
@property (weak, nonatomic) IBOutlet UIButton *b2;
@property (weak, nonatomic) IBOutlet UIButton *b3; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
} -(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews]; CGFloat buttonWidth = (self.view.bounds.size.width - - - )/; CGRect frame = CGRectMake(, , buttonWidth, );
self.button1.frame = frame; frame.origin.x += buttonWidth + ;
self.button2.frame = frame; frame.size = CGSizeMake(self.view.bounds.size.width--, self.view.bounds.size.height--);
frame.origin = CGPointMake(, );
self.imageView.frame = frame; frame.size = CGSizeMake(, );
frame.origin = CGPointMake(self.view.bounds.size.width--, self.view.bounds.size.height--);
self.b3.frame =frame; frame.origin.x -= (+);
self.b2.frame = frame; frame.origin.x -= (+);
self.b1.frame = frame; } @end
故事板中的界面随意布局就好,做一个控件的创建就好了 .UIView对内部的子视图的布局(典型应用:TableViewCell对内部子视图的布局)
1.1 如何实现?
step1:自定义视图,继承自UIView
step2:重写自定义视图的方法
a。 viewWillLayoutSubViews
b。 layoutSubViews
c。 viewDidLayoutSubView
方法执行的顺序:a->b->c
一般重写 layoutSubViews方法即可。 练习:音乐列表
【Demo1_TableViewCell_Layout】
Mondel
TRMusic.h
#import <Foundation/Foundation.h> @interface TRMusic : NSObject @property (nonatomic, copy) NSString * name;//歌曲名
@property (nonatomic, copy) NSString * album;//专辑
@property (nonatomic, copy) NSString * artist;//艺术家 @property (nonatomic) NSTimeInterval duration;//时长 @property (nonatomic) BOOL highQuality;//高品质
@property (nonatomic) BOOL downloaded;//下载 @end
TRMusic.m
#import "TRMusic.h" @implementation TRMusic @end TRMusicGroup.h
#import <Foundation/Foundation.h>
#import "TRMusic.h" typedef NS_ENUM(NSInteger, TRMusicGroupState) {
TRMusicGroupStateNormal, //音乐组的状态
TRMusicGroupStateDownloading,
TRMusicGroupStateDownloaded
}; @interface TRMusicGroup : NSObject @property (nonatomic, copy) NSString * name;//组名字 @property (nonatomic, strong) NSArray * musics;//多个音乐 @property (nonatomic) TRMusicGroupState state;//状态 + (NSArray *) fakeData; @end TRMusicGroup.m
#import "TRMusicGroup.h" @implementation TRMusicGroup + (NSArray *) fakeData
{
NSMutableArray * musics = nil;
TRMusic * music = nil; musics = [NSMutableArray array]; music = [[TRMusic alloc] init];
music.name = @"Burn";
music.album = @"Burn - Single";
music.artist = @"Ellie Goulding";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = YES;
music.highQuality = NO;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"Summertime Sadness (Cedric Gervais Remix)";
music.album = @"Summertime Sadness (Cedric Gervais Remix) - Single";
music.artist = @"Lana Del Rey";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = YES;
music.highQuality = YES;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"Spectrum";
music.album = @"Clarity";
music.artist = @"Zedd";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = YES;
music.highQuality = YES;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"It's Time";
music.album = @"It’s Time";
music.artist = @"Imagine Dragons";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = NO;
music.highQuality = YES;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"Dancing in The Moonlight";
music.album = @"Dancing In The Moonlight: The Best Of Toploader";
music.artist = @"Toploader";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = YES;
music.highQuality = YES;
[musics addObject:music]; TRMusicGroup * g1 = [[TRMusicGroup alloc] init];
g1.name = @"国外单曲";
g1.musics = [musics copy];
g1.state = TRMusicGroupStateDownloaded; musics = [NSMutableArray array]; music = [[TRMusic alloc] init];
music.name = @"你有本事抢男人";
music.album = @"好大的胆子";
music.artist = @"雪姨";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = NO;
music.highQuality = NO;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"喂鸡";
music.album = @"六十年代生人";
music.artist = @"刘欢";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = NO;
music.highQuality = YES;
[musics addObject:music]; music = [[TRMusic alloc] init];
music.name = @"忐忑";
music.album = @"*鸟";
music.artist = @"龚琳娜";
music.duration = [self durationWithMinutes: andSeconds:];
music.downloaded = NO;
music.highQuality = YES;
[musics addObject:music]; TRMusicGroup * g2 = [[TRMusicGroup alloc] init];
g2.name = @"国内神曲";
g2.musics = [musics copy];
g2.state = TRMusicGroupStateNormal; TRMusicGroup * g3 = [[TRMusicGroup alloc] init];
g3.name = @"Calvin Harris 专辑";
g3.musics = @[];
g3.state = TRMusicGroupStateNormal; TRMusicGroup * g4 = [[TRMusicGroup alloc] init];
g4.name = @"Ellie Gounding 专辑";
g4.musics = @[];
g4.state = TRMusicGroupStateNormal; return @[g1, g2, g3, g4];
} + (NSTimeInterval) durationWithMinutes:(int)minutes andSeconds:(int)seconds
{
return minutes * + seconds;
} @end MusilcTableViewController.h
#import <UIKit/UIKit.h>
#import "TRMusicGroup.h"
@interface MusilcTableViewController : UITableViewController
@property(nonatomic,strong)TRMusicGroup*musicGroup;
@end MusilcTableViewController.m
#import "MusilcTableViewController.h"
#import "MusicTableViewCell.h"
#import "TRMusic.h"
@interface MusilcTableViewController () @end @implementation MusilcTableViewController - (void)viewDidLoad
{
[super viewDidLoad]; }
//获取列表内容
-(TRMusicGroup *)musicGroup{
if (!_musicGroup) {
_musicGroup=[TRMusicGroup fakeData][]; }
return _musicGroup;
} #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ return self.musicGroup.musics.count;
} /**/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MusicTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MusicCell" forIndexPath:indexPath];
//找出音乐,获取数据
TRMusic* music=self.musicGroup.musics[indexPath.row];
//将音乐赋给cell
cell.music=music; return cell;
} @end
MusicTableViewCell.h
#import <UIKit/UIKit.h>
#import "TRMusic.h" @interface MusicTableViewCell : UITableViewCell
@property(nonatomic,strong)TRMusic*music;
@end
MusicTableViewCell.m
#import "MusicTableViewCell.h"
@interface MusicTableViewCell () //扩展属性
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *durationLabel;
@property (weak, nonatomic) IBOutlet UILabel *artistLabel;
@property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;
@property (weak, nonatomic) IBOutlet UIImageView *highQualityImageView; @end @implementation MusicTableViewCell //重写set方法
-(void)setMusic:(TRMusic *)music{
_music = music;
//将music的各个属性放到对应的控件上
self.nameLabel.text = self.music.name;
self.durationLabel.text=[NSString stringWithFormat:@"%d:%02d",(int)self.music.duration/,(int)self.music.duration%];
self.artistLabel.text=[[self.music.artist stringByAppendingString:@"-"]stringByAppendingString:self.music.album]; self.downloadedImageView.hidden = !self.music.downloaded;
self.highQualityImageView.hidden = !self.music.highQuality; }
//重写 cell对自己内部子视图的布局
-(void)layoutSubviews{
[super layoutSubviews];
CGFloat x=self.downloadedImageView.frame.origin.x;
if (self.music.downloaded) {
x+=;
}
if (self.music.highQuality) {
CGRect rect=self.highQualityImageView.frame;
rect.origin.x=x;
self.highQualityImageView.frame=rect;
x+=;
}
CGRect fram=self.artistLabel.frame;
fram.origin.x=x;
self.artistLabel.frame=fram;
}
@end

1.2. 布局对状态栏和各种Bar的处理 使用属性:topLayoutGuide.length//屏幕上方当前被占据的区域的长度
bottomLayoutGuide.length//屏幕下方当前被占据的长度 【Demo2_Layout_Bar】
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (weak, nonatomic) IBOutlet UILabel *label; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad]; } - (void)viewDidLayoutSubviews{
CGRect frame = self.button.frame;
frame.origin.x = self.view.bounds.size.width--frame.size.width;
frame.origin.y = self.topLayoutGuide.length+;
self.button.frame = frame; frame = self.label.frame;
frame.origin.x = self.view.bounds.size.width - - frame.size.width;
frame.origin.y = self.view.bounds.size.height - self.bottomLayoutGuide.length - frame.size.height;
self.label.frame = frame; }
//点击屏幕隐藏工具栏
- (IBAction)changeBar:(UITapGestureRecognizer *)sender {
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden animated:YES];
[self.navigationController setToolbarHidden:!self.navigationController.toolbarHidden animated:YES];
} - (BOOL)prefersStatusBarHidden{
return YES;
} @end 
. 手动设置Autoresizing 布局 3.1 是什么?
是旧版本(iOS5之前)的自动布局奇数。操作简单,API简单,功能也简单,有局限性,很久以前叫 struts/spring(架构/弹性)技术 3.2 核心理念
当界面大小发生变化时,根据变化的比例,对子视图进行同比例的变化 3.3 怎么样
step1:关闭AutoLayout
step2:选中需要布局的子视图
step3:打开检查器5
step4:点亮需要的红线
外框(4个)红线负责子视图到父视图的边缘
内框(2个)红线负责子视图内部是否可以拉伸 【Demo3_Autoresizing】
.编写代码实现Autoresizing 布局
Autoresizing和代码布局可以同时使用,用代码补齐Autoresizing的不足
button.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin;
代码设置时,规则描述与在检查器中描述相反,只需要设置可变的边距
如:
- (void)viewDidLoad
{
[super viewDidLoad]; /**/
UIButton* button=[UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"button1" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor redColor]];
button.frame=CGRectMake(self.view.bounds.size.width-, , , );
[self.view addSubview:button];
//点亮红线
button.autoresizingMask=UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin; } .Autolayout(自动布局)
5.1 是什么?
是从iOS6开始的一个新的布局技术,功能强大,操作复杂。从xcode5开始,慢慢好用了。在xcode6中,功能更强大了。
核心理念:使用约束(constraint)来描述控件在视图中的位置,当屏幕大小发生变化时,系统会根据你设定的约束自动计算出frame的值,然后将该值赋给控件,实现控件的排布 5.2 使用故事板为控件添加约束
【Demo4_AutoLayout】 5.3 操作要点
a。选中控件,分析6点,上下左右及控件的宽高需要哪些约束才能确定
b。约束的添加可以通过屏幕下方的选项,或者是,选中控件后,按住control,连线到屏幕边缘或其他视图
c。可以添加的约束有:对齐方式(与中心点对齐或与其他控件对齐)、与边缘或其他视图的间距(前导间距和尾部边距)、视图的宽高是给定值还是以其他视图做参照标准
d。添加约束后,正确的结果出现时,屏幕中只有蓝色的线,存在红色虚线框框时,代表视图占据的区域,有橘色线条时,代表当前摆放位置与定义的位置有距离,可以通过底部的第三个选项菜单,选择更新某个视图的frame或更新所有视图的frame
e。选中一个视图,查看第5个检查器可以看到该视图已经添加了的约束,可以选中约束修改约束的内容
f。选中一个视图,通过查看场景的文档结构图,观察该场景下的约束是否有错误或警告,如果有,可以点击该场景的右上角的红色点点,进入说明界面,红色的提示为一场,必须修改为正确,方可代表系统认可约束,可以进行布局,橘色的提示,一般是实际位置与约束位置有偏差,只要更新frame,就可以让橘色的警告消失 .Auto Layout 代码创建约束 1.1 什么时候用?
当子视图对象时代码创建时,并且需要给子视图布局时,只能用代码来进行自动布局 1.2 如何添加约束?
step1:创建约束对象
NSLayoutConstraint (API)
step2:将约束添加到父视图 1.3 创建约束对象
方法一:使用万能公式
+ (instancetype)constraintWithItem:(id)view1
                 attribute:(NSLayoutAttribute)attr1
                 relatedBy:(NSLayoutRelation)relation
                 toItem:(id)view2
                 attribute:(NSLayoutAttribute)attr2
               multiplier:(CGFloat)multiplier
                  constant:(CGFloat)c
公式:view1.attr1<relation>view2.attr2*multiplier +contant
如:
button.left = self.view.left* +
button.right = self.view.width* + (-)
注意:translateAutoresizingToConstraints这个属性默认是YES,这个属性代表:将视图默认自带的Auto resizing特性是否自动转换为对应的约束。既然使用代码来创建约束,那么就不要让系统自带的转换过来的约束影响添加的自定义约束,所以该属性要设置为NO,为了保证效果,可以将视图以及视图的父视图的该属性都设置为NO
//关闭视图自身的翻译
button1.translatesAutoresizingMaskIntoConstraints = NO;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
【Demo1_Autolayou_Code】 如:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem];
[button1 setTitle:@"button1" forState:UIControlStateNormal];
button1.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:button1];
//关闭视图自身的翻译
button1.translatesAutoresizingMaskIntoConstraints = NO;
self.view.translatesAutoresizingMaskIntoConstraints = NO; //创建约束
NSLayoutConstraint *c1 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier: constant:];
[self.view addConstraint:c1]; NSLayoutConstraint *c2 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier: constant:];
[self.view addConstraint:c2];
//高
NSLayoutConstraint *c3 = [NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute: multiplier: constant:];
[self.view addConstraint:c3];
} 方法二:一次创建多个约束,使用VFL(Visual Format Language) + (NSArray *)constraintsWithVisualFormat:(NSString *)format
            options:(NSLayoutFormatOptions)opts 参数2:对齐方式
            metrics:(NSDictionary *)metrics
            views:(NSDictionary *)views 参数4:创建添加的对象 特点:功能强大
【Demo2_Autolayout_Code_VFL】 1.4 VFL(Visual Format Language)
a。是什么?
一个字符串,具有一定的格式,代表一些约束的含义 b。如何写VFL字符串?
| 代表父视图的边
V:| 代表垂直方向的父视图的上边
[ ] 代表一个子视图(或控件)
( ) 代表一个条件(== 、 >=、<=)==可以省略
- 代表间距,默认的8个点
-xxx- 代表间距是多少
[button2(button1)] :button1与button2的高度相同 如: |--[button1]--[button2(button1)]--[button3(button1)]--|
V:|--[button1] c。metrics参数 (用于替换参数3)
用于指定VFL字符串中可以替换的值,是一个字典类型
如:NSDictionary *metrics = @{@"left":@,@"space":@,@"right":@}; d。NSDictionaryOfVariableBinding()函数 (用于替换参数4)
NSDictionary *dictionary = NSDictionaryOfVariableBindings(b1,b2,b3);
生成的字典如下:
@{@"b1":b1,@"b2":b2,@"b3":b3}
如:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
//需求:屏幕上方 添加三个等宽的按钮
UIButton *b1 = [UIButton buttonWithType:UIButtonTypeSystem];
[b1 setTitle:@"button1" forState:UIControlStateNormal];
b1.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:b1]; UIButton *b2 = [UIButton buttonWithType:UIButtonTypeSystem];
[b2 setTitle:@"button2" forState:UIControlStateNormal];
b2.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:b2]; UIButton *b3 = [UIButton buttonWithType:UIButtonTypeSystem];
[b3 setTitle:@"button3" forState:UIControlStateNormal];
b3.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:b3]; //1.添加约束的第一步:关闭自动翻译
b1.translatesAutoresizingMaskIntoConstraints = NO;
b2.translatesAutoresizingMaskIntoConstraints = NO;
b3.translatesAutoresizingMaskIntoConstraints = NO;
self.view.translatesAutoresizingMaskIntoConstraints = NO; //下面的函数会将传入的多个引用构建成如下的字典形式:
//@{@"b1":b1,@"b2":b2,@"b3":b3}
NSDictionary *dictionary = NSDictionaryOfVariableBindings(b1,b2,b3); NSDictionary *metrics = @{@"left":@,@"space":@,@"right":@}; //2.第二步:创建约束
//2.1准备一个VFL
//NSString*hVFL=@"|-20-[button1]-10-[button2(==button1)]-10-[button3(==button1)]-20-|";
NSString *hVFL = @"|-left-[b1]-space-[b2(b1)]-space-[b3(b1)]-right-|";
//2.2创建约束
NSArray *cs = [NSLayoutConstraint constraintsWithVisualFormat:hVFL options:NSLayoutFormatAlignAllCenterY metrics:metrics views:dictionary];
//3.将约束添加到父视图中
[self.view addConstraints:cs]; NSString *vVFL = @"V:|-left-[button1]";
cs = [NSLayoutConstraint constraintsWithVisualFormat:vVFL options: metrics:metrics views:@{@"button1":b1}];
[self.view addConstraints:cs];
} @end

=======================================================================
知识点
七、动画
.动画 (Animation) 1.1 是什么? “帧动画”:一帧 是一张静态的图片,一般情况下,1秒钟达到24,5帧的时候,人眼就分辨不出图片的切换过程,就有连续的效果产生 帧率(FPS)Frame Per Second 1.2 iOS中的动画 UIImage 类,自带一些方法,可以做简单动画
NSTimer 类,间隔指定时间,产生切换效果
UIView类 本来提供了动画的功能
底层的Core Animation 提供了动画的支持 在iOS7中增加一些动画功能:
UIKit Dynamic 动力
Motion Effects 特效
Sprite Kit (2D引擎) . UIImage动画 【Demo3_UIImage_Animation】
//duration:播放一组图片用的时间
UIImage *image = [UIImage animatedImageNamed:@"ship-anim" duration:*/]; 1秒播放30帧,播放一张图片需要1/,5张照片
self.imageView.image = image; . NSTimer 动画 4.1 是什么?
一个计时器类,用于定时向指定对象发消息 4.2 如何使用?
【Demo4_NSTimer】 .//创建计时器
//使用schedule。。开头的方法时,定时器创建完毕就会启动
self.timer = [NSTimer scheduledTimerWithTimeInterval: target:self selector:@selector(doSomething:) userInfo:nil repeats:YES];
//使用timerWith。。。开头的方法创建timer
//定时器不会在创建完毕后启动
//需要使用代码将定时器添加到时间循环中才能启动
self.timer = [NSTimer timerWithTimeInterval: target:self selector:@selector(doSomething:) userInfo:nil repeats:YES];
.//启动定时器
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
.//关闭定时器
[self.timer invalidate]; 练习:【Demo5_NSTimer_Alpha】
使用NSTimer做一个图片淡入的效果(通过修改alpha透明度值来完成)
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)NSTimer *timer;
@property(nonatomic)NSInteger count;
@end @implementation ViewController #define FPS 30.0
#define DURATION 5.0 - (void)viewDidLoad
{
[super viewDidLoad];
self.imageView.alpha = ;
self.timer = [NSTimer scheduledTimerWithTimeInterval:/FPS target:self selector:@selector(changeAlpha:) userInfo:nil repeats:YES];
}
//匀速动画的公式
//当前值 = 开始值+当前帧数*(结束值-开始值)/(帧率*动画时长)
-(void)changeAlpha:(NSTimer *)timer{
//count代表当前帧数
self.count++; self.imageView.alpha = + self.count*(-)/(FPS*DURATION); if (self.count>=FPS*DURATION) {
[timer invalidate];
} } @end 4.3 匀速动画 公式:当前值= 开始值+当前帧数*(结束值-开始值)/(帧率*动画时长) 这个值可以是:center transform frame alpha 【Demo6_NSTimer_Animation】 4.4 变速动画 由快到慢、由慢到快、由慢到快再到慢 公式: 当前值 = 上一次的值+(目标值-上一次值)*渐进因子
渐进因子根据情况调节
【Demo7_NSTimer_Animation】
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *airCraft; @end @implementation ViewController
#define FPS 30.0 - (void)viewDidLoad
{
[super viewDidLoad]; } - (IBAction)start:(id)sender {
[NSTimer scheduledTimerWithTimeInterval:/FPS target:self selector:@selector(move:) userInfo:nil repeats:YES];
} //当前值=上一次的值+(目标值-上一次的值)*渐进因子
/*
400 + (100 - 400)*0.1 = 370 30p
370 + (100 - 370)*0.1 = 343 27p
343 + (100 - 343)*0.1 = 319.7 24.3
*/
-(void)move:(NSTimer *)timer{
CGPoint center = self.airCraft.center;
center.y = center.y+(-center.y)*0.05;
self.airCraft.center = center;
if (center.y<=51.0) {
[timer invalidate];//结束
}
} @end . UIView 动画:真正的动画,有专门的API 5.1 是什么 有UIKit专门制作动画的API,这些API的底层对Core Animation的封装,可以轻松实现动画,不用再计算每一帧的值来实现动画的效果 5.2 制作动画的步骤 step1:设置需要动画的视图的初始值
step2:给UIView类发消息,告诉UIView类需要什么样的动画
step3:将动画结束的状态(属性值),即变化的结果,写入到一个Bloack中
[UIView animateWithDuration:2.0 delay: options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
//设置动画结束时,被动画的那个视图的结束状态
self.airCraft.center = endCenter;
self.airCraft.transform = transform;
} completion:nil]; 【Demo8_UIView_Animation】 5.3 动画的高级选项 先慢后快再慢
UIViewAnimationOptionCurveEaseInOut
越来越快
UIViewAnimationOptionCurveEaseIn
越来越慢
UIViewAnimationOptionCurveEaseOut
匀速
UIViewAnimationOptionCurveLinear
动画重复
UIViewAnimationOptionRepeat
反着执行动画,要配合Repeat选项
UIViewAnimationOptionAutoreverse 要求:启动后,从屏幕左边弹出label,从屏幕下边弹出image,然后点击按钮,飞机实现旋转飞行,并重复飞行
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *airCraft;
@property (weak, nonatomic) IBOutlet UILabel *welcomeLabel; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
CGRect endFrame = self.welcomeLabel.frame;
CGRect startFrame = endFrame;
startFrame.origin.x = -startFrame.size.width;
//1.设置需要动画的视图的初始属性
self.welcomeLabel.frame = startFrame;
self.welcomeLabel.alpha = 0.1;
//2.给UIView发消息
[UIView animateWithDuration:2.0 animations:^{
self.welcomeLabel.frame = endFrame;
self.welcomeLabel.alpha = 1.0;
}]; endFrame = self.airCraft.frame;
startFrame = endFrame;
startFrame.origin.y = self.view.bounds.size.height;
self.airCraft.frame = startFrame;
[UIView animateWithDuration:2.0 animations:^{
self.airCraft.frame = endFrame;
}]; } - (IBAction)start:(id)sender {
//这是当前中心点
CGPoint endCenter = self.airCraft.center;
//计算结束位置的y值
endCenter.y -=;
//旋转
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI); [UIView animateWithDuration:2.0 delay: options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
//设置动画结束时,被动画的那个视图的结束状态
self.airCraft.center = endCenter;
self.airCraft.transform = transform;
} completion:nil]; } @end

作业 .飞机放到屏幕上以后,点哪,飞哪儿 .做一个购物车的动画
在屏幕的左上角有一个UIImageView,图片是一个商品。
当用户点击此商品时,商品会从上面掉下来,落入到下面的购物车中, 注意:商品掉下来时,屏幕左上角的图片不会消失
例:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController
#define FPS 30.0
#define MAX_SIZE 10
#define MAX_DURATION 10 - (void)viewDidLoad
{
[super viewDidLoad];
//启动定时器,创建雪花
[NSTimer scheduledTimerWithTimeInterval:/FPS target:self selector:@selector(animate:) userInfo:nil repeats:YES];
} -(void)animate:(NSTimer *)timer{
//1.创建一个雪花
UIImageView *snow = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"snow.png"]];
int viewWidth = self.view.bounds.size.width;
int viewHeight = self.view.bounds.size.height;
CGFloat size = MAX_SIZE+arc4random()%MAX_SIZE;
snow.frame = CGRectMake(arc4random()%viewWidth, -, size, size);
[self.view addSubview:snow];
//2.创建动画
[UIView animateWithDuration:arc4random()%MAX_DURATION+ delay: options:UIViewAnimationOptionCurveEaseIn animations:^{
//3. 设置动画结束时雪花的位置信息
int offset = arc4random()% - ;
snow.center = CGPointMake(snow.center.x+offset, viewHeight-);
}completion:^(BOOL finished) {
//4.落地动画结束时,开始融雪
[UIView animateWithDuration:arc4random()%MAX_DURATION delay: options:UIViewAnimationOptionCurveEaseIn animations:^{
snow.alpha = ;
} completion:^(BOOL finished) {
//5.融雪动画结束时,将图片移出父视图
[snow removeFromSuperview];
}];
}];
} ======================================================================
知识点
七、Core Animation .Core Animation
.1是什么
是一个图形渲染和动画的底层框架,用于iOS和Mac OS X 1.2 能干什么
)可以提供更多更强大的图形渲染(显示)效果
)可以提供专业级的动画效果
)是高层图形技术的基础 1.3 如何使用Core Animation
(内容较多,很庞大,只讲常用的)
通过CALayer类,直接对一个视图(UIView及子类)的 Core Animation层进行一些设置,达到需要的效果 1.4 如何获得CALayer这一层呢?
任何UIView及其子类都有一个属性叫layer
UIView CALayer
.layer
.frame .frame
.transform .transform3D
.autoresizing .autoresizing
.addSubView: .addSubLayer:
【Demo1_CoreAnimation】
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
CALayer *layer = self.view.layer;
layer.backgroundColor = [[UIColor orangeColor] CGColor];
layer.cornerRadius = 30.0; self.imageView.layer.cornerRadius = 10.0;
//打开遮罩
self.imageView.layer.masksToBounds = YES; // 加子层
CALayer *subLayer = [CALayer layer];
subLayer.backgroundColor = [[UIColor purpleColor]CGColor];
subLayer.frame = CGRectMake(, , , );
subLayer.shadowColor = [[UIColor greenColor]CGColor];
subLayer.shadowOffset = CGSizeMake(, );
subLayer.shadowRadius = 5.0;
subLayer.shadowOpacity = 0.8;阴影透明度,设置它阴影才会显示
subLayer.cornerRadius = 10.0; [layer addSublayer:subLayer]; //有内容的子层
CALayer *imageLayer = [CALayer new];
imageLayer.frame = CGRectMake(, , , );
imageLayer.contents = (id)[UIImage imageNamed:@"d.jpg"].CGImage;//添加图片
imageLayer.cornerRadius = ;
imageLayer.masksToBounds = YES; [layer addSublayer:imageLayer];
} @end
 1.5 CAAnimation 一个抽象的动画类型,很多时候不关心这个父类,而是使用它的子类来实现动画 ) CAKeyframeAnimation 关键帧动画
可以根据指定的路径进行动画
实现步骤:
step1:创建关键帧动画
使用animationWithKeyPath:方法创建,同时,最后一个字符串参数必须是以下几种选择:
position transform opacity
step2:设置动画属性
step3:将动画添加到视图的layer层上 )CABasicAnimation可以实现缩放,旋转,透明度等动画
特点:设置动画属性主要为两个:fromValue 和 toValue 例:点击按钮,图片按曲线移动,并且移动的时候会缩小,并且会消失
#import "AnimationViewController.h" @interface AnimationViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation AnimationViewController - (void)viewDidLoad
{
[super viewDidLoad]; } - (IBAction)start:(id)sender {
UIBezierPath *path = [UIBezierPath bezierPath];
[self createPath:path]; // 创建关键帧动画
CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
// 设置相关的属性
moveAnimation.path = path.CGPath;
//moveAnimation.duration = 2.0;
//[self.imageView.layer addAnimation:moveAnimation forKey:nil]; //创建缩放动画
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 0.1)];
//scaleAnimation.duration = 2.0;
//[self.imageView.layer addAnimation:scaleAnimation forKey:nil]; //透明度动画
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnimation.fromValue = @1.0;
alphaAnimation.toValue = @0.0;
//alphaAnimation.duration = 2.0;
//[self.imageView.layer addAnimation:alphaAnimation forKey:nil]; //创建动画组,将所有动画对象添加到组中
//针对组设置的动画属性,会被应用到组中的每一个动画上面
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = @[moveAnimation,scaleAnimation,alphaAnimation];
group.duration = 2.0;
group.delegate = self;
[self.imageView.layer addAnimation:group forKey:nil];
} //图片消失
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self.imageView removeFromSuperview];
} //生成路线
-(void)createPath:(UIBezierPath *)path{
[path moveToPoint:self.imageView.center];
CGPoint tartgetPoint = CGPointMake(self.view.bounds.size.width-self.imageView.frame.size.width-, self.view.bounds.size.height-self.imageView.frame.size.height-);
CGPoint control1 = CGPointMake(self.view.bounds.size.width-self.imageView.frame.size.width-, self.imageView.frame.origin.y);
CGPoint control2 = CGPointMake(self.imageView.frame.origin.x, self.view.bounds.size.height--self.imageView.frame.size.height);
[path addCurveToPoint:tartgetPoint controlPoint1:control1 controlPoint2:control2]; } @end

补充:动画停止
//停止
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ self.imageView.layer.transform = CATransform3DRotate(self.imageView.layer.transform,M_PI, 1.0, 1.0, 1.0);
} .6CATransform3D
)是什么
是一个4X4的矩阵,一个结构体。描述了一个3D图片变形的数据
)创建
CATransform3DMakeRotation/scale/Translation//这组方法在定值常量的基础上变形
CATransform3DScale/Rotate/Translate //这组方法是在传入值的基础上进行变形
=====================================================================
知识点
八、 UIKit Dynamic 动力特效 . UIKit Dynamic 动力特效 .1是什么
中文翻译:UIKit 动力、动力模型。。。。iOS7开始的技术。 提供了一个模拟真实世界中力学相关的动画和交互系统,比如,重力、碰撞、吸附等。UIKit Dynamic可以组合 可重用 1.2 UIKit Dynamic架构
a。核心部分:UIDynamicAnimator —>视图的坐标系
b。UIDynamic xx Behavior (行为)
重力 UIGravityBehavior (.magnitude 重力的强度,即加速度)
碰撞 UICollisionBehavior
//将场景视图的四周翻译成可碰撞的四个边
collision.translatesReferenceBoundsIntoBoundary = YES;
吸附 UIAttachmentBehavior
self.attachment.anchorPoint //吸附点 (一个坐标)
self.attachment.frequency=;//设置吸附行为的频率(左右晃动的大小)
self.attachment.damping=0.1;//设置吸附行为的阻尼(上下弹跳的范围)
闪烁
推力
综合力 【Demo2_Dynamic】 *****给view添加背景色,显示小方格(方法就是重绘一下图形,利用重写初始化方法:然后设置self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"BackgroundTile"]]; BackgroundView.h
#import <UIKit/UIKit.h> @interface BackgroundView : UIView @property(nonatomic,strong)UIBezierPath *path; @end
BackgroundView.m
#import "BackgroundView.h" @implementation BackgroundView //当故事板创建此视图对象时调用此方法,初始化方法
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"BackgroundTile"]];
}
return self;
}
//绘制传入的障碍物图形
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor]setFill];
[[UIColor greenColor]setStroke];
[self.path stroke];
[self.path fill]; }
@end
=======================================================================================================
重力 (点击下落就向下,点击停止即停止)
ViewController.h
ViewController.m
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIDynamicAnimator *animator;
@property(nonatomic,strong)UIGravityBehavior *gravityBehavior;
@end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
// 1.构建场景
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
self.animator = animator;
// 2.创建重力行为对象
UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[self.imageView]];
self.gravityBehavior = gravity; }
- (IBAction)begin:(id)sender {
// 3.将重力行为添加到场景中
[self.animator addBehavior:self.gravityBehavior];
} - (IBAction)stop:(id)sender {
[self.animator removeBehavior:self.gravityBehavior];//将重力行为从场景中移除
} @end 碰撞(依靠重力下落,然后碰到矩形框,改变成红色然后停止)
CollisionViewController.h
CollisionViewController.m #import "CollisionViewController.h"
#import "BackgroundView.h" @interface CollisionViewController ()<UICollisionBehaviorDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIDynamicAnimator *animator;
@end @implementation CollisionViewController
- (void)viewDidLoad{
[super viewDidLoad];
self.imageView.transform = CGAffineTransformMakeRotation(M_PI_4);
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
self.animator = animator;
//重力行为
UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[self.imageView]];
//设置重力行为的强度
gravity.magnitude = ;
[animator addBehavior:gravity];
//碰撞行为
UICollisionBehavior *collision = [[UICollisionBehavior alloc]initWithItems:@[self.imageView]];
//将场景视图的四周翻译成可碰撞的四个边
collision.translatesReferenceBoundsIntoBoundary = YES; //添加一条矩形的障碍物
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(, , , ) cornerRadius:10.0];
BackgroundView *myView = (BackgroundView *)self.view;
myView.path = path;
[myView setNeedsDisplay];
[collision addBoundaryWithIdentifier:@"MyPath1" forPath:path];
collision.collisionDelegate = self;
[animator addBehavior:collision]; }
//添加代理实现方法 碰撞时方框实现变色
-(void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p{
//NSLog(@"...");
UIImageView *box = (UIImageView *)item;
box.tintColor = [UIColor redColor];
box.image = [box.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
} @end 吸附(设置移动,并在移动到的点与图片的中心点绘制直线,然后移动到哪,图片就吸附到哪,然后有重力,图片还会摆动)
AttachmentViewController.h
AttachmentViewController.m
#import "AttachmentViewController.h"
#import "BackgroundView.h" @interface AttachmentViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIDynamicAnimator *animator;
@property(nonatomic,strong)UIGravityBehavior *gravity;
@property(nonatomic,strong)UIAttachmentBehavior *attachment; @end @implementation AttachmentViewController - (UIDynamicAnimator *)animator{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
}
return _animator;
} - (UIGravityBehavior *)gravity{
if (!_gravity) {
_gravity = [[UIGravityBehavior alloc]initWithItems:@[self.imageView]];
}
return _gravity;
} - (UIAttachmentBehavior *)attachment{
CGPoint attachmentAnchor = CGPointMake(self.imageView.center.x, self.imageView.center.y - );
if (!_attachment) {
_attachment = [[UIAttachmentBehavior alloc]initWithItem:self.imageView attachedToAnchor:attachmentAnchor];
}
return _attachment;
} - (IBAction)tap:(UIPanGestureRecognizer *)sender {
CGPoint location = [sender locationInView:self.view];
//将手势滑动到的点作为吸附行为的锚点
self.attachment.anchorPoint = location;
} - (void)viewDidLoad
{
[super viewDidLoad]; } - (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.animator addBehavior:self.gravity]; self.attachment.action = ^{
//绘制锚点到中心点的悬挂的线
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.attachment.anchorPoint];
[path addLineToPoint:self.imageView.center];
BackgroundView *bgView = (BackgroundView *)self.view;
bgView.path = path;
path.lineWidth = ;
[bgView setNeedsDisplay];
};
[self.animator addBehavior:self.attachment];
} @end
    改:吸附类
移动的时候会出现绘图绿线和重力作用,当手势停止,一切动作都停止
AttachmentViewController.h
AttachmentViewController.m #import "AttachmentViewController.h"
#import "BackgroundView.h" @interface AttachmentViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIDynamicAnimator *animator;
@property(nonatomic,strong)UIGravityBehavior *gravity;
@property(nonatomic,strong)UIAttachmentBehavior *attachment; @end @implementation AttachmentViewController - (UIDynamicAnimator *)animator{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
}
return _animator;
} - (UIGravityBehavior *)gravity{
if (!_gravity) {
_gravity = [[UIGravityBehavior alloc]initWithItems:@[self.imageView]];
}
return _gravity;
} - (UIAttachmentBehavior *)attachment{
//设置锚点
CGPoint attachmentAnchor = CGPointMake(self.imageView.center.x, self.imageView.center.y - );
if (!_attachment) {
_attachment = [[UIAttachmentBehavior alloc]initWithItem:self.imageView attachedToAnchor:attachmentAnchor];
}
return _attachment;
} - (IBAction)tap:(UIPanGestureRecognizer *)sender { if (sender.state==UIGestureRecognizerStateBegan) {
[self.animator addBehavior:self.gravity]; self.attachment.frequency=;//设置吸附行为的频率(左右晃动的大小)
self.attachment.damping=0.1;//设置吸附行为的阻尼(上下弹跳的范围)
[self drawLine];
[self.animator addBehavior:self.attachment]; }else if(sender.state==UIGestureRecognizerStateChanged){
CGPoint location = [sender locationInView:self.view];
//将手势滑动到的点作为吸附行为的锚点
self.attachment.anchorPoint = location;
[self drawLine];
}else if(sender.state==UIGestureRecognizerStateEnded){
[self.animator removeBehavior:self.gravity];
[self.animator removeBehavior:self.attachment];
BackgroundView*view=(BackgroundView*)self.view;
view.path=nil;
[view setNeedsDisplay];
} }
//执行绘图功能
-(void)drawLine{
self.attachment.action = ^{
//绘制锚点到中心点的悬挂的线
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.attachment.anchorPoint];
[path addLineToPoint:self.imageView.center];
BackgroundView *bgView = (BackgroundView *)self.view;
bgView.path = path;
path.lineWidth = ;
[bgView setNeedsDisplay];
}; } @end ****补充: 在block块中,只要用到self引用必须要用弱引用
-(void)drawLine{ //弱引用
__weak UIAttachmentBehavior* weakAttachment=self.attachment;
__weak UIImageView*weakImageView=self.imageView;
__weak BackgroundView*weakBgView=(BackgroundView*)self.view; self.attachment.action = ^{
//绘制锚点到中心点的悬挂的线
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:weakAttachment.anchorPoint];
[path addLineToPoint:weakImageView.center];
BackgroundView *bgView = weakBgView;
bgView.path = path;
path.lineWidth = ;
[bgView setNeedsDisplay];
}; } . 闪烁行为 UISnapBehavior
/*
闪烁其实就是快速移动到某一点,使用变形动画也可以做到这个效果,
但是,使用snapBehavior,物体在移动到某点以后
会晃动以下
注意:在animator中不能添加两种同样的特效,所以需要先移除以前的行为,再添加新的行为 */ SnapViewController.h
SnapViewController.m
#import "SnapViewController.h" @interface SnapViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)UIDynamicAnimator*dynamicAnimator;
@property(nonatomic,strong)UISnapBehavior*snapBehavior;
@end @implementation SnapViewController -(UIDynamicAnimator *)dynamicAnimator{
if (!_dynamicAnimator) {
_dynamicAnimator=[[UIDynamicAnimator alloc]initWithReferenceView:self.view];
}
return _dynamicAnimator;
} - (IBAction)pan:(UIPanGestureRecognizer *)sender { CGPoint point=[sender locationInView:self.view];
//先移除原有的闪烁行为 不然只能移一次
[self.dynamicAnimator removeBehavior:self.snapBehavior]; self.snapBehavior=[[UISnapBehavior alloc]initWithItem:self.imageView snapToPoint:point];
[self.dynamicAnimator addBehavior:self.snapBehavior];
} @end . 推力行为 UIPushBehavior
//推力角度
self.pushBehavior.angle=M_PI_4;//右下角
//推力大小
self.pushBehavior.magnitude=;
//激活力 push手势的特别之处:需要激活,否则不起作用
self.pushBehavior.active=YES; 如:借助移动手势来实现力的方向
#import "PushViewController.h" @interface PushViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @property(nonatomic,strong)UIDynamicAnimator *animator;
@property(nonatomic,strong)UIPushBehavior *pushBehavior; @end /*
push手势的特别之处:需要激活,否则不起作用
设置 .active = YES
*/
@implementation PushViewController - (UIDynamicAnimator *)animator{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
}
return _animator;
} - (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated]; // 添加碰撞行为,遇到边界则停止
UICollisionBehavior *collision = [[UICollisionBehavior alloc]initWithItems:@[self.imageView]];
[collision setTranslatesReferenceBoundsIntoBoundaryWithInsets:UIEdgeInsetsMake(self.topLayoutGuide.length, ,self.bottomLayoutGuide.length, )
];
[self.animator addBehavior:collision]; //添加推力行为
self.pushBehavior = [[UIPushBehavior alloc]initWithItems:@[self.imageView] mode:UIPushBehaviorModeContinuous];
[self.animator addBehavior:self.pushBehavior];
}
- (IBAction)tap:(UITapGestureRecognizer *)sender {
CGPoint point = [sender locationInView: self.view];
CGPoint center = self.imageView.center;
CGFloat angle = atan2(point.y-center.y, point.x-center.x)+M_PI;
//powf()函数:求某数的某几次幂
//sqrt()函数:开平方
CGFloat distance = sqrt(powf((point.x-center.x), )+powf((point.y-center.y), )); self.pushBehavior.angle = angle;
self.pushBehavior.magnitude = distance/;
self.pushBehavior.active = YES;
} @end
   点击哪就从这个方向施力 练习:SpringMessage 弹簧效果短消息:
.思路:
利用手指移动的距离,以及item离手指点的位置的远近,产生一个变化的值,用这个值来修改所有collectionView中的item的锚点 .步骤
step1.首先计算scroll的距离scrollDelta
step2.为了得到每个item与触摸点的之间的距离,还需要知道触摸点的坐标touchLocation。
step3.可以根据距离对每个锚点进行设置了:简单地计算了原来锚点与触摸点之间的距离distanceFromTouch,并由此计算一个系数。
step4.接下来,对于当前的item,我们获取其当前锚点位置,然后将其根据scrollDelta的数值和刚才计算的系数,重新设定锚点的位置。
step5.最后需要告诉UIDynamicAnimator已经完成了对锚点的更新,现在可以开始更新物理计算,并随时准备collectionView来取LayoutAttributes的数据了。
SpringLayout.h
#import <UIKit/UIKit.h> @interface SpringLayout : UICollectionViewFlowLayout
@property(nonatomic,strong)UIDynamicAnimator *animator; @end
SpringLayout.m
#import "SpringLayout.h" /*
1. 为每一个布局属性对象添加行为,让布局属性对象成为behavior的主体
2. 控制器再来询问布局属性对象(layoutAttribute)
时,返回具有行为特征的item
3. 每当屏幕滚动一点,就根据触点和各个item的位置的远近,计算出一个有一定变化规律的因子,根据这个因子,有规律的调整每一个布局属性对象的attachment Behavior的锚点,就可以实现弹簧效果
*/ @implementation SpringLayout //1. 在vc即将跟布局对象要各个布局属性对象之前
//就将每一个布局属性对象都添加上 吸附 行为
-(void)prepareLayout{
if (!self.animator) {
self.animator = [[UIDynamicAnimator alloc]initWithCollectionViewLayout:self];
//得到整个collectionView的尺寸
CGSize contentSize = [self collectionViewContentSize];
NSArray *layoutAttributes = [super layoutAttributesForElementsInRect:CGRectMake(, , contentSize.width, contentSize.height)];
//得到整个collectionView中的所有向的
//布局属性对象,从而添加 吸附 行为
for (UICollectionViewLayoutAttributes *attribute in layoutAttributes) {
UIAttachmentBehavior *spring = [[UIAttachmentBehavior alloc]initWithItem:attribute attachedToAnchor:attribute.center];
spring.damping = 0.6;
spring.frequency = 0.8;
[self.animator addBehavior:spring];
}
}
} //2.当vc再来询问每一个布局属性对象时,返回
//修改过的 布局属性对象,或者说返回已经成为
//吸附行为主体 的 那个布局属性对象
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ //体系维护对象 按照指定的矩形区域
//返回该范围内的行为主体(布局属性对象)
return [self.animator itemsInRect:rect]
;
} //3.根据vc传入的单元格的坐标,返回该单元格
//对应的那个用来布局这个单元格的部署属性对象
//-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
// return [self.animator layoutAttributesForCellAtIndexPath:indexPath];
//} //4.当边界bounds发生变化时,是否更新边界
//思路:1.不更新边界
// 2.边界发生变化的原因,是因为有滚动事件发生了
// 虽然不更新边界,但要针对这个用户的操作
// 修改一下锚点,以此出现弹簧效果
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
UIScrollView *scrollView = self.collectionView;
//获取滚动的距离,其实就是坐标系变换的大小
CGFloat scrollDistance = newBounds.origin.y - scrollView.bounds.origin.y;
//手指所在的位置
CGPoint touchLocation = [scrollView.panGestureRecognizer locationInView:scrollView];
//计算和修改每一个行为的锚点
for (UIAttachmentBehavior *spring in self.animator.behaviors) {
//因为attribute是 behavior 行为的主体
//所以反过来,从行为上获取 与它绑定的主体
//就需要访问items属性,由于,该行为只关联
//了一个主体,所以取firstObject
UICollectionViewLayoutAttributes *attributes = [spring.items firstObject];
CGPoint center = attributes.center;
CGPoint anchorPoint = spring.anchorPoint;
//手指的触点和项的中心点的远近
//根据 项 离 手指触点的远近,产生一组
//不同大小的 新的anchor值
CGFloat distance = fabsf(touchLocation.y - anchorPoint.y);
// distance是变化的,所以除以最大距离
//也就是一屏幕的高度,取个整,算600
//到底一个小于1的可变化的值
//用这个值作为变化因子,从而使得滚动距离
//与项离触点远近的变化规律相同了
center.y += scrollDistance*(distance/);
attributes.center = center;
// 更新体系内的每一个行为的状态
[self.animator updateItemUsingCurrentState:attributes];
} return NO;
} @end
ViewController.h
ViewController.m
#import "ViewController.h"
#import "SpringLayout.h" @interface ViewController ()<UICollectionViewDataSource> @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
SpringLayout *layout = [[SpringLayout alloc]init];
layout.itemSize = CGSizeMake(,);
layout.sectionInset = UIEdgeInsetsMake(, , , );
UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
collectionView.showsHorizontalScrollIndicator = NO;
collectionView.showsVerticalScrollIndicator = NO;
[collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"MyCell"];
collectionView.dataSource = self;
[self.view addSubview:collectionView];
} -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor lightGrayColor];
return cell;
} @end
 点击屏幕发生晃动
===================================================================
知识点
九、通知( NSNotification) . 通知(Notification) 1.1 是什么? 是一种观察者模式的具体体现 观察者模式:一个对象(A)想知道另一个对象(B)的状态是否发生了改变。思路就是,在对象B上注册一个观察者,当对象B的状态发生改变时,通知对象A,对象A收到通知后进行相关的处理的一种模式 其中观察者模式的一种解决方案叫做—广播
系统中的通知 就是广播的体现 1.2 好处
一个对象不需要知道消息的接收者是谁,就可以将一些消息发送给需要的接收者 有时,发送消息的对象无法知道有哪些对象,有多少对象接收消息,也不知道对象是否存在
有时,消息的接受者和发送者太远(远 指的不是距离,是关系,如 控制器和视图就很近,但Model离控制器就很远) 1.3 具体用法
)收听者:找到通知中心
NSNotificationCenter,注册要收听的一个具体频道 addObserver
)发送者:找到通知中心,创建通知对象(NSNotification),使用通知中心来发送这个消息(postNotification)
)收听者 收到消息 处理消息(掉个方法)
)停止收听,不需要收听时,找到通知中心,注销 removeObserver .4关键的类
NSNotificationCenter,是一个单例类,使用defaultCenter方法永远返回同一个对象,以此保证中心对象只有一个
NSNotification 通知类(封装通知的内容等信息) 【Demo2_Notification】创建控制台程序
Company.h
#import <Foundation/Foundation.h> @interface Company : NSObject
-(void)broadcast;
@end
Company.m
#import "Company.h" @implementation Company
-(void)broadcast{
NSNotificationCenter*center=[NSNotificationCenter defaultCenter];
//发消息
[center postNotificationName:@"videoUpdate" object:self userInfo:@{@"title": @"锦绣园",@"episode": @"第15集"}]; }
@end
Vap.h
Vap.m
#import "Vap.h" @implementation Vap -(id)init{
if ([super init]) {
NSNotificationCenter*center=[NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(updated:) name:@"videoUpdate" object:nil];
}
return self;
} -(void)updated:(NSNotification*)notification{
NSDictionary*message=notification.userInfo;
NSLog(@"%@已经更新到%@",message[@"title"],message[@"episode"]);
} -(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self];
NSLog(@"dealloc执行了");
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Company.h"
#import "Vap.h"
int main(int argc, const char * argv[])
{ @autoreleasepool {
Company*company=[[Company alloc]init];
Vap*vip=[[Vap alloc]init];
[company broadcast]; NSLog(@"Hello, World!"); }
return ;
} .键盘通知 键盘弹起的通知名称:
UIKeyboardWillShowNotification
键盘收起的通知名称
UIKeyboardWillHideNotification 【Demo3_Keyboard_Notification】
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
} -(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// 注册监听
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyboardOpen:) name:UIKeyboardWillShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardClosed:) name:UIKeyboardWillHideNotification object:nil];
} -(void)keyboardOpen:(NSNotification *)notification{
NSDictionary *message = notification.userInfo;
//获取键盘的起始点(0,264)
NSValue *value = message[UIKeyboardFrameEndUserInfoKey];
CGRect rect = [value CGRectValue];
NSLog(@"%f,%f",rect.origin.x,rect.origin.y);
//NSLog(@"%@",message);
} -(void)keyboardClosed:(NSNotification *)notification{
NSLog(@"close.....");
} //点击右下角关闭键盘
- (IBAction)inputFinished:(UITextField *)sender {
[sender resignFirstResponder];
} - (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
} @end
qq聊天程序
TRMessage.h
#import <Foundation/Foundation.h> @interface TRMessage : NSObject @property(nonatomic)BOOL fromMe;
@property(nonatomic,strong)NSString *content; +(NSMutableArray *) demoData; @end TRMessage.m
#import "TRMessage.h" @implementation TRMessage + (NSMutableArray *)demoData{
TRMessage *message = nil; NSMutableArray *array = [NSMutableArray array]; message = [[TRMessage alloc]init];
message.fromMe = YES;
message.content = @"Hello 你好Hello 你好Hello 你好Hello 你好";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = NO;
message.content = @"干嘛呢";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = YES;
message.content = @"没干嘛你呢";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = NO;
message.content = @"呵呵 呵呵呵";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = YES;
message.content = @"今天的大新闻就是苹果手表终于发布了,好喜欢红色的,但是买不起";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = NO;
message.content = @"就知道你买不起,那就别买了";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = YES;
message.content = @"还是你送我吧";
[array addObject:message]; message = [[TRMessage alloc]init];
message.fromMe = NO;
message.content = @"做梦";
[array addObject:message]; return array; }
@end
TRMessageCell.h
#import <UIKit/UIKit.h>
#import "TRMessage.h" @interface TRMessageCell : UITableViewCell
@property(nonatomic,strong)TRMessage *message;
@end TRMessageCell.m
#import "TRMessageCell.h" @interface TRMessageCell ()
@property (weak, nonatomic) IBOutlet UIImageView *popImageView;
@property (weak, nonatomic) IBOutlet UILabel *label; @end @implementation TRMessageCell #define CELL_MARGIN_TB 4.0 //气泡上下外边距
#define CELL_MARGIN_LR 10.0 //气泡左右外边距 #define CELL_CORNOR 18.0 //气泡圆角半径
#define CELL_TAIL_WIDTH 16.0 //气泡尾巴 #define MAX_WIDTH_OF_TEXT 200.0 //文字宽度限制
#define CELL_PADDING 8.0 //气泡内边距 - (void)setMessage:(TRMessage *)message{ _message = message;
self.label.text = self.message.content; //根据消息的来源,对label和image进行定位
if (self.message.fromMe) {//蓝色气泡 //设置标签内容和图片视图中的气泡图片
self.label.textColor = [UIColor whiteColor]; UIImage *image = [UIImage imageNamed:@"message_i.png"];
image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(CELL_CORNOR, CELL_CORNOR, CELL_CORNOR, CELL_CORNOR+CELL_TAIL_WIDTH)];
self.popImageView.image = image; //1。定位Label
// 先确定文本的高度
CGRect rectOfText = CGRectMake(, , MAX_WIDTH_OF_TEXT, );
rectOfText = [self.label textRectForBounds:rectOfText limitedToNumberOfLines:];
CGRect frameOfLabel = CGRectZero;
frameOfLabel.size = rectOfText.size;
frameOfLabel.origin.y = CELL_MARGIN_TB + CELL_PADDING;
frameOfLabel.origin.x = self.bounds.size.width - CELL_MARGIN_LR - CELL_TAIL_WIDTH - CELL_PADDING - rectOfText.size.width;
self.label.frame = frameOfLabel; //2。定位popImageView的坐标
CGRect frameOfPop = frameOfLabel;
frameOfPop.origin.x -=CELL_PADDING;
frameOfPop.origin.y -=CELL_PADDING;
frameOfPop.size.width += * CELL_PADDING + CELL_TAIL_WIDTH
;
frameOfPop.size.height += * CELL_PADDING;
self.popImageView.frame = frameOfPop; //3.设定单元格的bounds
CGRect bounds = self.bounds;
bounds.size.height = frameOfPop.size.height + CELL_MARGIN_TB * ;
self.bounds = bounds;
}else{//灰色气泡
self.label.textColor = [UIColor darkGrayColor]; UIImage *image = [UIImage imageNamed:@"message_other.png"];
image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(CELL_CORNOR, CELL_CORNOR+CELL_TAIL_WIDTH, CELL_CORNOR, CELL_CORNOR)];
self.popImageView.image = image; CGRect rectOfText = CGRectMake(, , MAX_WIDTH_OF_TEXT, );
rectOfText = [self.label textRectForBounds:rectOfText limitedToNumberOfLines:]; CGRect frameOfLabel = CGRectZero;
frameOfLabel.size = rectOfText.size;
frameOfLabel.origin.x = CELL_PADDING+CELL_MARGIN_LR+CELL_TAIL_WIDTH;
frameOfLabel.origin.y = CELL_MARGIN_TB + CELL_PADDING;
self.label.frame = frameOfLabel; CGRect frameOfPop = frameOfLabel;
frameOfPop.origin.x -=(CELL_PADDING + CELL_TAIL_WIDTH);
frameOfPop.origin.y -=CELL_PADDING;
frameOfPop.size.width += CELL_PADDING*+CELL_TAIL_WIDTH;
frameOfPop.size.height += CELL_PADDING*;
self.popImageView.frame = frameOfPop; CGRect bounds = self.bounds;
bounds.size.height = frameOfPop.size.height + CELL_MARGIN_TB *;
self.bounds = bounds;
}
}
@end
TRMessageViewController.h
#import <UIKit/UIKit.h> @interface TRMessageViewController : UIViewController @property(nonatomic,strong)NSMutableArray *messages; @end
TRMessageViewController.m
#import "TRMessageViewController.h"
#import "TRMessage.h"
#import "TRMessageCell.h" @interface TRMessageViewController ()<UITableViewDataSource,UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UIView *inputTextView;
@property (weak, nonatomic) IBOutlet UITextField *textField; @end @implementation TRMessageViewController - (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Message";
self.messages = [TRMessage demoData];
[self.tableView registerNib:[UINib nibWithNibName:@"TRMessageCell" bundle:nil] forCellReuseIdentifier:@"Cell"];
// 设置view的背景图
self.inputTextView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ToolViewBkg_Black.png"]]; } -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return ;
} -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.messages.count;
} -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TRMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
cell.message = self.messages[indexPath.row];
return cell;
} -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//在生成cell的时候,在cell的内部曾经
//根据图片的大小,修改过cell的bounds属性
//随着修改bounds属性,cell的frame就自动被
//修改了,变成我们根据图片计算出来的高度
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
} //注册监听系统键盘的弹起
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(keyboardAppear:) name:UIKeyboardWillShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardDisappear:) name:UIKeyboardWillHideNotification object:nil];
} //键盘弹起时
-(void)keyboardAppear:(NSNotification *)notification{ //1.获取键盘的坐标体系
CGRect frameOfKeyboard = [notification.userInfo[UIKeyboardFrameEndUserInfoKey]CGRectValue]; //2.计算输入框的结束的坐标信息
CGRect frameOfInputView = self.inputTextView.frame; frameOfInputView.origin.y = frameOfKeyboard.origin.y - frameOfInputView.size.height; //3.计算表格需要滚动的距离
CGPoint offset = self.tableView.contentOffset;
offset.y += frameOfKeyboard.size.height; //4.为inputTextView添加动画
NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions options = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
[UIView animateWithDuration:duration delay:0.0 options:options animations:^{
//设置动画结束时 输入视图的新的位置
self.inputTextView.frame = frameOfInputView;
//动画结束时,修改表格的内容的位置
self.tableView.contentOffset = offset; } completion:nil];
} //键盘收起时
-(void)keyboardDisappear:(NSNotification *)notification{ CGRect frameOfInputView = self.inputTextView.frame;
frameOfInputView.origin.y = self.view.bounds.size.height - frameOfInputView.size.height; CGPoint newOffSet = CGPointMake(,self.tableView.contentSize.height - self.tableView.frame.size.height); NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions options = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
[UIView animateWithDuration:duration delay:0.0 options:options animations:^{
self.inputTextView.frame = frameOfInputView;
self.tableView.contentOffset = newOffSet;
} completion:nil];
} -(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self forKeyPath:UIKeyboardWillShowNotification];
[center removeObserver:self forKeyPath:UIKeyboardWillHideNotification];
} // 点击键盘右下角的return按键
- (IBAction)send:(UITextField *)sender {
// NSLog(@"%f",self.tableView.contentSize.height);
if (![self.textField.text isEqualToString:@""]) {
TRMessage *message = [[TRMessage alloc]init];
message.fromMe = YES;
message.content = self.textField.text;
//清空文本框
self.textField.text = @"";
//message对象添加到数据源
[self.messages addObject:message];
//更新表视图显示新增加的消息
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messages.count - inSection:];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
// NSLog(@"%f",self.tableView.contentSize.height);
} //[self.textField resignFirstResponder];
}
@end
 
故事板TRMessageViewController TRMessageCell   注意:第一个检查器中关闭自动布局,xib中image的第五个检查器中,点亮的红线只有保持上左即可,其他的关闭就可以了。tableView中第四个检查器中,Separator改为None,去掉表格线。Selection 改为No Selection,实现运行的时候,去掉点击时显示的灰色区域
========================================================================
知识点
十、Search Bar 搜索栏 .Search Bar(旧版本)
故事板中的添加了Search Bar 然后到第四个检查器 点击勾上Shows Scope Bar
单元格要记得在第四个检查器中注册identifier为cell
Product.h
#import <Foundation/Foundation.h> typedef NS_ENUM(NSInteger, ProductType){
ProductTypeDevice,
ProductTypeSoftware,
ProductTypeOther
}; /*定义商品类,包含名称和类别*/
@interface Product : NSObject @property(nonatomic,strong)NSString *name;
@property(nonatomic)ProductType type; +(NSArray *)demoData; @end Product.m
#import "Product.h" @implementation Product + (NSArray *)demoData{
Product *p1 = [[Product alloc]init];
p1.name = @"iPhone4s";
p1.type = ProductTypeDevice; Product *p2 = [[Product alloc]init];
p2.name = @"iPhone5s";
p2.type = ProductTypeDevice; Product *p3 = [[Product alloc]init];
p3.name = @"iPhone6";
p3.type = ProductTypeDevice; Product *p4 = [[Product alloc]init];
p4.name = @"iPhone6 Plus";
p4.type = ProductTypeDevice; Product *p5 = [[Product alloc]init];
p5.name = @"OS X Yosemite";
p5.type = ProductTypeSoftware; Product *p6 = [[Product alloc]init];
p6.name = @"Airport Time Capsule";
p6.type = ProductTypeOther; return @[p1,p2,p3,p4,p5,p6];
} @end
ProductViewController.h
#import <UIKit/UIKit.h> @interface ProductViewController : UITableViewController @property(nonatomic,strong)NSArray *products; @end ProductViewController.m
#import "ProductViewController.h"
#import "Product.h" @interface ProductViewController ()<UISearchDisplayDelegate>
//声明一个数组,用于存储搜索到的结果内容
@property(nonatomic,strong)NSMutableArray *searchResult;
@end @implementation ProductViewController - (void)viewDidLoad
{
[super viewDidLoad]; self.products = [Product demoData]; //创建 searchResult 的实例
self.searchResult = [NSMutableArray array];
//为显示数据的表格注册单元格
[self.searchDisplayController.searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } /*
当前控制器已经是两个标题视图的 代理对象了。
一个表视图指的是TVC自带的表视图
另一个表视图指的是用于显示搜索结果数据展示的表视图
*/ #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//需要区分参数tableView到底是self.tableView
//还是searchBar的resultTableView
//return self.products.count;
if (tableView == self.searchDisplayController.searchResultsTableView) {
return self.searchResult.count;
}else{
return self.products.count;
}
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
Product *product = nil;
if (tableView == self.view) {
product = self.products[indexPath.row];
}else{
product = self.searchResult[indexPath.row];
}
cell.textLabel.text = product.name;
return cell;
} //只要在搜索框中修改了搜索的内容,立即执行此方法
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
//根据搜索框内写的字符串进行比对
//生成搜索结果
//搜索需要两个数据:文本框中输入的+分段控件中选择的搜索类别
NSInteger type = self.searchDisplayController.searchBar.selectedScopeButtonIndex;
[self updateContentForProductName:searchString andType:type];
return YES;
} //只要选择了搜索框下面的分段控件,该方法就执行
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption{
NSString *inputStr = self.searchDisplayController.searchBar.text;
[self updateContentForProductName:inputStr andType:searchOption];
return YES;
} // 根据输入的文本和选择的类别进行匹配
-(void)updateContentForProductName:(NSString *)searchString andType:(NSInteger )type{
NSMutableArray *array = [NSMutableArray array];
for (Product *p in self.products) {
//查看字符串B在A中的位置及占用的长度
//ABCDE -> BCD location = 1 lenght = 3
NSRange range = [p.name rangeOfString:searchString];
if (range.length > && p.type==type) {
[array addObject:p];
}
}
self.searchResult = array;
} @end
  
.iOS8 Search Bar (Xcode6)
参考【Demo3_SearchBar_iOS8】
思想:创建类:用于展示搜索结果的控制器的显示模型,在主控制器创建执行搜索动作的控制器并与 自己创建的类相联系,并设置搜索下边的三项分类,并且将searchResultsUpdater和searchBar分别设置为代理,遵守协议<UISearchResultsUpdating,UISearchBarDelegate>
Product.h
Product.m
同上
MainTableViewController.h
#import <UIKit/UIKit.h> @interface MainTableViewController : UITableViewController @property(nonatomic,strong)NSArray *products; @end
MainTableViewController.m
#import "MainTableViewController.h"
#import "SearchTableViewController.h"
#import "Product.h" @interface MainTableViewController ()<UISearchResultsUpdating,UISearchBarDelegate>
//增加属性:用于控制搜索结果显示的控制器
@property(nonatomic,strong)SearchTableViewController *searchResultViewController;
@property(nonatomic,strong)UISearchController *searchController; @end @implementation MainTableViewController - (void)viewDidLoad {
[super viewDidLoad];
self.products = [Product demoData];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; //创建用于展示搜索结果的控制器实例
self.searchResultViewController = [[SearchTableViewController alloc]init];
//创建执行搜索动作的控制器,并制定哪个控制器帮助它显示结果
self.searchController = [[UISearchController alloc]initWithSearchResultsController:self.searchResultViewController]; //设置搜索控制器的结果更新代理对象
self.searchController.searchResultsUpdater = self;
//设置显示的bar的大小和样式
[self.searchController.searchBar sizeToFit];
self.searchController.searchBar.scopeButtonTitles = @[@"设备",@"软件",@"其它"]; 显示几个按钮是根据给几个名字
//将搜索bar添加到表头视图
self.tableView.tableHeaderView = self.searchController.searchBar; self.definesPresentationContext = YES; self.searchController.searchBar.delegate = self;
} - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope{
[self updateSearchResultsForSearchController:self.searchController];
}
#pragma mark - UISearchResultUpdating
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController{
//用户输入的要搜索的文本信息
NSString *searchText = searchController.searchBar.text;
//获取选择的scope按钮是哪个
NSInteger selectedScopeButton = searchController.searchBar.selectedScopeButtonIndex;
NSLog(@"%ld",selectedScopeButton);
NSMutableArray *searchResult = [NSMutableArray array];
for (Product *p in self.products) {
NSRange range = [p.name rangeOfString:searchText];
if (range.length > && p.type==selectedScopeButton) {
[searchResult addObject:p];
}
}
self.searchResultViewController.resultArray = searchResult;
[self.searchResultViewController.tableView reloadData];
} #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.products.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
Product *p = self.products[indexPath.row];
cell.textLabel.text = p.name;
return cell;
} @end
SearchTableViewController.h
#import <UIKit/UIKit.h> @interface SearchTableViewController : UITableViewController
@property(nonatomic,strong)NSArray *resultArray; @end
SearchTableViewController.m
#import "SearchTableViewController.h"
#import "Product.h" @interface SearchTableViewController () @end @implementation SearchTableViewController - (void)viewDidLoad {
[super viewDidLoad];
// 注册cell
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell2"];
} #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ;
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.resultArray.count;
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell2" forIndexPath:indexPath];
Product *p = self.resultArray[indexPath.row];
cell.textLabel.text = p.name;
return cell;
} @end .Size Classes 随着苹果设备的增多,屏幕尺寸越来越多样化,为了解决适配不同设备屏幕的问题,从iOS8开始,推出了一项配合Auto layout一起用的Size Classes技术 核心理念:抛弃屏幕尺寸的概念,将不同种类的设备划分到不同的组合内,制作界面时,关注这一个组别,就等同于对这一个组别下的所有设备进行设计界面。运行时,系统会根据当前设备,判断属于哪个组别,然后找到对应组别下的AutoLayout原则,依据此原则计算坐标 有哪些组别?
划分组别的标准: 紧凑型 any 标准型
根据右下角确定,当选到any,即可以包括, .应用程序间的通信 4.1 什么是应用程序间的通讯?
一个应用给另一个应用发点信息过去,但很少,如果说,打开图片库、点击分享、打印、共享 4.2 使用场景
将一个字符串或图片发到微博、微信等应用上 4.3 如何做?
使用一个叫做UIActivityViewController控制器完成任务 4.4 Activity
把共享时要操作的项目叫做Activity,比如说,拷贝、打印——是 Activity中的操作,微信、微博这种Activity叫做分享 【Demo4_UIActivityViewController】
ViewController.h
ViewController.m
#import "ViewController.h"
#import "StringReverseActivity.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *textField; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad]; }
- (IBAction)shared:(UIButton *)sender { NSString *text = self.textField.text; //第一步 创建Activity控制器
//activityItems:要传递的信息
//applicationActivities:写nil,系统会
//列出支持的所有activity
UIActivityViewController *avc = [[UIActivityViewController alloc]initWithActivityItems:@[text] applicationActivities:nil]; //第二步:设置排除的activity
avc.excludedActivityTypes = @[UIActivityTypeMail];
//第三步:显示出VC
[self presentViewController:avc animated:YES completion:nil]; } - (IBAction)customActivity:(UIButton *)sender {
NSArray *itemToShare = @[@"Hello",@"World",@]; //第一步创建自定义的Activity的对象
StringReverseActivity *srActivity = [[StringReverseActivity alloc]init]; //第二步 添加activity到Activity控制器上
UIActivityViewController *avc = [[UIActivityViewController alloc]initWithActivityItems:itemToShare applicationActivities:@[srActivity]]; //第三步 推出avc
[self presentViewController:avc animated:YES completion:nil]; } @end .5自定义的Activity
step1:自己写一个类,继承UIActivity
step2:实现类内的方法
类内的6个方法必须实现
【Demo4_UIActivityViewController】 接上面的编写
StringReverseActivity.h
StringReverseActivity.m
#import "StringReverseActivity.h" @interface StringReverseActivity ()<UIAlertViewDelegate> @property(nonatomic,strong)NSMutableArray *activityItems; @end @implementation StringReverseActivity //第一个:返回自己的Activity的类型,只要唯一即可
-(NSString *)activityType{
//获得应用程序所在的沙箱的完整路径
//NSStringFromClass是根据类,获取类的名称
return [[NSBundle mainBundle].bundleIdentifier stringByAppendingFormat:@".%@",NSStringFromClass([self class])];
} //第二个:不要求唯一,但希望短一点
//返回activity的名称
-(NSString *)activityTitle{
return @"反转";
} //第三个:显示的图片 Retina: 86 X 86 ipad: 110 X 110
-(UIImage *)activityImage{
return [UIImage imageNamed:@"icon80"]; 图片尺寸不行,所以不显示
} //第四个:将共享的item传过的数据进行检验,看是否可以反转
-(BOOL)canPerformWithActivityItems:(NSArray *)activityItems{
for (id object in activityItems) {
if ([object isKindOfClass:[NSString class]]) {
//只要有一个string就调用反转方法
return YES;
}
}
return NO;
} //第5个:查找activity的条目,只要有一个item能用就会到达这个方法
- (void)prepareWithActivityItems:(NSArray *)activityItems{
//把所有能反转的item挑出来,放到一个数组中
NSMutableArray *stringObjects = [NSMutableArray array];
for (id object in activityItems) {
if ([object isKindOfClass:[NSString class]]) {
[stringObjects addObject:object];
}
}
self.activityItems = stringObjects;
} //第6个:执行activity,编写逻辑---反转
-(void)performActivity{
// 为了保存每一个反转后的string
NSMutableString *reverseString = [[NSMutableString alloc]init];
//反转
for (NSString
*string in self.activityItems) {
[reverseString appendString:[self myReverseString:string]];
[reverseString appendString:@"\n"];
} //显示反转的结果
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"字符串反转" message:reverseString delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
} //第7个:点击alert中的确定按钮后,通知activity动作结束
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[self activityDidFinish:YES];
} //自定义方法:执行字符串的反转
-(NSString *)myReverseString:(NSString *)paramString
{
NSMutableString *reversed = [[NSMutableString alloc]init];
for (NSInteger index = paramString.length-; index>=; index--) {
[reversed appendFormat:@"%c",[paramString characterAtIndex:index]];
}
return [reversed copy];
} @end
系统默认,去掉了邮件 自定义,多增加了反转 点击反转,只要是字符串就倒过来显示
   
.横竖屏的判断
【Demo5_All_Orientation】
.设置屏幕支持的方向
.获取即将要旋转到的某个朝向
#import "ViewController.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *greenVIew; @property (weak, nonatomic) IBOutlet UIButton *button1;
@property (weak, nonatomic) IBOutlet UIButton *button2;
@property (weak, nonatomic) IBOutlet UIButton *button3; @property (weak, nonatomic) IBOutlet UIButton *button4; @end @implementation ViewController - (void)viewDidLoad
{
[super viewDidLoad];
} //1.设置屏幕支持的方向
- (NSUInteger)supportedInterfaceOrientations{
//设置支持的设备方向
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape;
} //2.获取即将要旋转到的某个朝向
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
//判断即将到达的朝向,决定选中何种布局
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
//竖屏
[self layoutPortrait];
}else{
//横屏
[self layoutLandscape];
}
} //自定义的方法,用于竖屏时布局
-(void)layoutPortrait{
self.greenVIew.frame = CGRectMake(, , self.view.bounds.size.width--, self.view.bounds.size.height-*-*); CGRect frame = CGRectMake(, self.view.bounds.size.height-*-*, , );
self.button1.frame = frame; frame.origin.x +=+;
self.button2.frame = frame; frame.origin.y += (+);
self.button4.frame = frame; frame.origin.x -= (+);
self.button3.frame = frame; } //自定义的方法,用于横屏时布局
-(void)layoutLandscape{ } //界面显示前判断好方向,界面显示的时候布置
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated]; //判断出来的那一刻的方向
UIApplication *app = [UIApplication sharedApplication];
UIInterfaceOrientation orientation = app.statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation)) {
[self layoutPortrait];
}else{
[self layoutLandscape];
} } @end
上一篇:modelsim编译altera的库


下一篇:ss命令