NSFileManager、NSURL、NSFileHandle、NSData、NSXMLParser、NSUserDefaults、NSKeyedArchiver、NSKeyedUnarchiver、CoreData、NSFetchRequest、NSEntityDescription、NSPredicate、 NSSortDescriptor、UIImagePickerController、AVAudioPlayer、MPMoviePlayerController、NSThread、NSBlockOperation、NSInvocationOperation、NSOperationQueue、NSLock、NSCondicion、AsyncSocket(FTP)、AsyncUdpSocket(UTP)、NSJSONSerialization、NSMutableURLRequest、NSURLConnection、UIWebView、AFNetworking(HTTP)、MKAnnotationView 、MKMapView、UILocalNotification、CMMotionManager =========================================================================== 横屏: 1.故事版中,右侧检查器Oriemtation—>Landscape 2.主项目中,portrait的勾去掉 全屏:(去掉屏幕上边的下拉框) 1.Supporting Files—>BaylAnimation—>info.plist—>右击点击空白处—>AddRow—>选倒数第一个 2.项目中勾上Hide duing application launch 例: 1.要求: 移动图片,放到下边的view视图中,上边的图片保持不消失 2. 思想: 0.通过touch来实现 1.遍历出数组中的图片,判断点击的是哪一张图片,然后将点击的图片保存在添加属性的UIImageView中。 2.根据保存在UIImageView中的点击的图片,将图片添加到下边的view,意思是只有点击了此图片才进行判断添加到哪个视图,首先先判断添加到下边的哪个view,判断根据点击的点是在哪个视图的范围内,在变换点击图片的父视图,变换成下面的view,然后将图片添加进去,注意,如果点击的区域不在两个view上,就删除此图片。 3.根据下边view的子视图确定添加到视图的图片数量,赋给label显示 3.新知识点: 1.查看子视图个数 self.leftCountLabel.text = [NSString stringWithFormat:@"%d",self.leftView.subviews.count]; 2.判断点击到的是哪一张图片 根据点击的点是否是矩形中包含的点确定 CGRectContainsPoint(self.leftView.frame, p) 3.当一个控件的父视图发生改变的时候 并且两个父视图不重叠 需要转变相对坐标 CGPoint oldCenter = self.dragIV.center; CGPoint newCenter = [self.view convertPoint:oldCenter toView:self.rightView]; self.dragIV.center=newCenter; 4.从主视图移除此图片 [self.dragIV removeFromSuperview]; 完整程序: #import "ViewController.h" @interface ViewController () @property (nonatomic, strong)UIImageView *dragIV; @property (weak, nonatomic) IBOutlet UILabel *rightCountLabel; @property (weak, nonatomic) IBOutlet UILabel *leftCountLabel; @property (weak, nonatomic) IBOutlet UIView *rightView; @property (weak, nonatomic) IBOutlet UIView *leftView; @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *imageViews;//三张图片 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; for (UIImageView *iv in self.imageViews) { //判断点击到的是哪一张图片 矩形中包含的点 if (CGRectContainsPoint(iv.frame, p)) { self.dragIV = [[UIImageView alloc]initWithFrame:iv.frame]; self.dragIV.image = iv.image; [self.view addSubview:self.dragIV]; } } } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; self.dragIV.center = p; } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; if (self.dragIV) { if (CGRectContainsPoint(self.leftView.frame, p)) { CGPoint oldCenter = self.dragIV.center; CGPoint newCenter = [self.view convertPoint:oldCenter toView:self.leftView]; [self.leftView addSubview:self.dragIV]; self.dragIV.center = newCenter; //根据leftView的子视图来确定个数 self.leftCountLabel.text = [NSString stringWithFormat:@"%d",self.leftView.subviews.count]; }else if (CGRectContainsPoint(self.rightView.frame, p)){ // 当一个控件的父视图发生改变的时候 并且两个父视图不重叠 需要转变相对坐标 CGPoint oldCenter = self.dragIV.center; CGPoint newCenter = [self.view convertPoint:oldCenter toView:self.rightView]; [self.rightView addSubview:self.dragIV]; self.dragIV.center = newCenter; self.rightCountLabel.text = [NSString stringWithFormat:@"%d",self.rightView.subviews.count]; }else{ [self.dragIV removeFromSuperview]; } self.dragIV = nil;//当放在下边的视图中就不能在实行移动 } } //意外中断操作时调用 -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ } @end ? 2.植物大战僵尸 僵尸类: 1.要求:从屏幕右侧的边界随机的走出僵尸 2.思想: a.不要都堆到控制器,要创建新的单独的类,封装性,如果要实现不同的图片移动,要用继承,另外如果有重复出现的程序,还要用多态。总之这三点多用 b.创建UIImageView类,从总的图中裁剪出单个图片,记录到公开属性UIImageView中,利用NSTimer控制创建多次裁剪的图片,即可以实现动画效果,改变其中心点即可以实现移动。 c.将保存在属性中的图片在赋给当前类,当前类在控制器中只需要实例化即可创建多个僵尸对象,要想实现重复创建,使用timer即可 d.实现从屏幕右侧不同高度走出,只需随机创建实例的frame的y点的值 e.若继续创建多个类,继承自创建的僵尸类,只要在子类修改其图片以及移动的中心点就可以实现不同形状的僵尸对象 f.注意判断,当僵尸走出屏幕时,需要将僵尸对象销毁,即让timer停止,并从当前视图移除 3.新知识点 1).从总的图中分别裁剪出子图(注意类型的转换) UIImage *zombImage = [UIImage imageNamed:@"zomb_0.png"]; float w = zombImage.size.width/8; CGImageRef subImage = CGImageCreateWithImageInRect(zombImage.CGImage, CGRectMake(self.count++%8*w, 0, w, zombImage.size.height)); self.image = [UIImage imageWithCGImage:subImage]; // 把CGImageRef释放掉 CGImageRelease(subImage); 2)把timer停止 才能让对象从内存中释放掉 if (self.frame.origin.x<=-self.bounds.size.width) { //把timer停止 才能让对象从内存中释放掉 [timer invalidate]; [self removeFromSuperview];//从屏幕视图中移除 } 完整程序: Zomb.h #import <UIKit/UIKit.h> @interface Zomb : UIImageView @property (nonatomic)int count; @property (nonatomic, strong)UIImage *zombImage; @property (nonatomic)float speed; @end Zomb.m #import "Zomb.h" @implementation Zomb - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 当使用timer的时候 timer会给target的值内存计数+1 当timer停止的时候 [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(changeImage:) userInfo:nil repeats:YES]; } return self; } -(void)changeImage:(NSTimer *)timer{ float w = self.zombImage.size.width/8; CGImageRef subImage = CGImageCreateWithImageInRect(self.zombImage.CGImage, CGRectMake(self.count++%8*w, 0, w, self.zombImage.size.height)); self.image = [UIImage imageWithCGImage:subImage]; // 把CGImageRef释放掉 CGImageRelease(subImage); self.center = CGPointMake(self.center.x-self.speed, self.center.y); if (self.frame.origin.x<=-self.bounds.size.width) { //把timer停止 才能让对象从内存中释放掉 [timer invalidate]; [self removeFromSuperview];//从屏幕视图中移除 } } - (void)dealloc { NSLog(@"僵尸死掉了!"); } @end Zomb.h #import "Zomb.h" @interface ZombA : Zomb @end Zomb.m #import "ZombA.h" @implementation ZombA - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.zombImage = [UIImage imageNamed:@"zomb_0.png"]; self.speed = 1; } return self; } ViewController.h ViewController.m #import "ViewController.h" #import "ZombA.h" #import "ZombB.h" #import "ZombC.h" #import "ZombD.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:@selector(addZomb) userInfo:nil repeats:YES]; } -(void)addZomb{ int type = arc4random()%4; Zomb *z = nil; switch (type) { case 0: z = [[ZombA alloc]initWithFrame:CGRectMake(self.view.bounds.size.width, arc4random()%(int)(self.view.bounds.size.height-60), 40, 60)]; break; case 1: z = [[ZombB alloc]initWithFrame:CGRectMake(self.view.bounds.size.width, arc4random()%(int)(self.view.bounds.size.height-60), 40, 60)]; break; case 2: z = [[ZombC alloc]initWithFrame:CGRectMake(self.view.bounds.size.width, arc4random()%(int)(self.view.bounds.size.height-60), 40, 60)]; break; case 3: z = [[ZombD alloc]initWithFrame:CGRectMake(self.view.bounds.size.width, arc4random()%(int)(self.view.bounds.size.height-60), 40, 60)]; break; } [self.view addSubview:z]; } @end 2.植物类 1.要求:实现将图片添加到view中,即种植植物到地里 2.思想: a.创建故事板,进行布局,四个imageView进行存放植物,多个view进行存放移动的imageView,分别将其放到数组中 b.将四个imageView从数组遍历出来,将总的图剪裁出的子图赋到遍历出来的imageView中,将遍历出来的图在保存到新创建的UIImageView中,剪裁只要根据x的左边即可确定是哪张图。 c.想实现每张图片的动画效果,继续利用僵尸类的效果即可实现,创建植物类,然后四张图片的效果分别继承植物类,通过timer裁剪总的图片实现移动效果。 d.实现上边的四张图片移动到下边的view中,利用touch,通过点击的时候遍历判断点击的是哪张图,点击的图片声明成属性,遍历出来再将点击的图片添加到view父视图 e.touch结束的时候,遍历数组view即坑,判断有没有在坑里松手 并且坑里什么都没有才可以放,并且判断如果拖拽植物的父视图还是self.view的话 说明没有扔到任何坑里面 就应该删除掉 if ([self.dragPlant.superview isEqual:self.view]) { [self.dragPlant removeFromSuperview]; } f.移到坑里不再移动,需设置self.dragPlant = nil; ViewController.h ViewController.m #import "ViewController.h" #import "SunFlower.h" #import "Pea.h" #import "IcePea.h" #import "Nut.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *sunCountLabel; @property (strong, nonatomic) IBOutletCollection(UIView) NSArray *boxes; @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *plantIVs; @property (nonatomic, strong)Plant *dragPlant; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self initUI]; } -(void)initUI{ UIImage *plantsImage = [UIImage imageNamed:@"seedpackets.png"]; float w = plantsImage.size.width/18; for (int i=0; i<self.plantIVs.count; i++) { UIImageView *plantIV = self.plantIVs[i]; float x = 0; switch (i) { case 1: x = 2*w; break; case 2: x = 3*w; break; case 3: x = 5*w; break; } CGImageRef subImage = CGImageCreateWithImageInRect(plantsImage.CGImage, CGRectMake(x, 0, w, plantsImage.size.height)); plantIV.image = [UIImage imageWithCGImage:subImage]; CGImageRelease(subImage); } } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; for (int i=0;i<self.plantIVs.count;i++) { UIImageView *plantIV = self.plantIVs[i]; if (CGRectContainsPoint(plantIV.frame, p)) { switch (i) { case 0: self.dragPlant = [[SunFlower alloc]initWithFrame:plantIV.frame]; break; case 1: self.dragPlant = [[Pea alloc]initWithFrame:plantIV.frame]; break; case 2: self.dragPlant = [[IcePea alloc]initWithFrame:plantIV.frame]; break; case 3: self.dragPlant = [[Nut alloc]initWithFrame:plantIV.frame]; break; } [self.view addSubview:self.dragPlant]; } } } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; self.dragPlant.center = p; } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; if (self.dragPlant) { for (UIView *box in self.boxes) { //判断有没有在坑里松手 并且坑里什么都没有 if (CGRectContainsPoint(box.frame, p)&&box.subviews.count==0) { [box addSubview:self.dragPlant]; self.dragPlant.center = CGPointMake(box.bounds.size.width/2, box.bounds.size.height/2); } } //判断如果拖拽植物的父视图还是self.view的话 说明没有扔到任何坑里面 就应该删除掉 if ([self.dragPlant.superview isEqual:self.view]) { [self.dragPlant removeFromSuperview]; } self.dragPlant = nil; } } -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ [self.dragPlant removeFromSuperview]; } ? 3. 要求:实现点击太阳花种植,太阳花增加钱数,当种植植物的时候,消耗太阳光,每种植物消耗的钱数都不同。当钱数不够的时候就不能点击。并且刚点击过的植物不能短时间呈透明状态,不能在被点击 知识点: 两个类之间的传值 方法一:代理 1.A类显示,需要公开一个属性或方法接收 2.将需要点击的图片设置为代理 3.B类中,将其A类名称在.h文件中公开创建属性.delegate @property (nonatomic, weak)ViewController *delegate; 4.在B类中将值传给.delegate属性或方法即可 方法二:通知 1.在A类创建监听通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(sunDismissAction:) name:@"sunDismissAction" object:nil]; 2.实现监听通知的方法,即建立接收消息的通知,记录key值,与B类中的key值保持一致 -(void)sunDismissAction:(NSNotification *)noti{ int count = [[noti.userInfo objectForKey:@"sunCount"] intValue]; [self addSunCount:count]; } 3.B类中发送需要传值的消息,值为key值对应的value值 [[NSNotificationCenter defaultCenter]postNotificationName:@"sunDismissAction" object:nil userInfo:@{@"sunCount":@(25)}]; Plant.h Plant.m #import "Plant.h" @implementation Plant - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(changeImage:) userInfo:nil repeats:YES]; } return self; } -(void)beginFier{ } -(void)changeImage:(NSTimer*)timer{ self.fps++; if (self.fps%10==0) { float w = self.plantImage.size.width/8; CGImageRef subImage = CGImageCreateWithImageInRect(self.plantImage.CGImage, CGRectMake(self.count++%8*w, 0, w, self.plantImage.size.height)); self.image = [UIImage imageWithCGImage:subImage]; CGImageRelease(subImage); } } SunFlower.h SunFlower.m #import "SunFlower.h" @implementation SunFlower - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.plantImage = [UIImage imageNamed:@"plant_0.png"]; self.userInteractionEnabled = YES; self.costSunCount = 50; } return self; } -(void)beginFier{ [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(addSun) userInfo:nil repeats:YES]; } -(void)addSun{ UIButton *sunBtn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 25, 25)]; [sunBtn setImage:[UIImage imageNamed:@"sun.png"] forState:UIControlStateNormal]; [sunBtn addTarget:self action:@selector(clickedSunAction:) forControlEvents:UIControlEventTouchUpInside]; [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(dismissSunAction:) userInfo:sunBtn repeats:NO]; [self addSubview:sunBtn]; } -(void)dismissSunAction:(NSTimer *)timer{ UIButton *sunBtn = timer.userInfo; [sunBtn removeFromSuperview]; } -(void)clickedSunAction:(UIButton *)sunBtn{ [sunBtn removeFromSuperview]; // 让界面中的Label数量增加 // [self.delegate addSunCount:25]; [[NSNotificationCenter defaultCenter]postNotificationName:@"sunDismissAction" object:nil userInfo:@{@"sunCount":@(25)}]; } @end Pea.h #import "Plant.h" @interface Pea : Plant @property (nonatomic, strong)NSMutableArray *bullets; @property (nonatomic, strong)UIImage *bulletImage; @end Pea.m #import "Pea.h" @implementation Pea - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.bullets = [NSMutableArray array]; self.plantImage = [UIImage imageNamed:@"plant_2.png"]; self.bulletImage = [UIImage imageNamed:@"bullet_0.png"]; self.costSunCount = 100; } return self; } -(void)beginFier{ [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addBullet) userInfo:nil repeats:YES]; } -(void)addBullet{ UIImageView *bulletIV = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 15, 15)]; bulletIV.center = CGPointMake(self.superview.center.x+20, self.superview.center.y-10); bulletIV.image = self.bulletImage; [self.delegate.view addSubview:bulletIV]; [self.bullets addObject:bulletIV]; } -(void)changeImage:(NSTimer *)timer{ [super changeImage:timer]; //移动子弹 for (UIImageView *bulletIV in self.bullets) { bulletIV.center = CGPointMake(bulletIV.center.x+2, bulletIV.center.y); if (bulletIV.frame.origin.x>=self.delegate.view.bounds.size.width) { [bulletIV removeFromSuperview]; [self.bullets removeObject:bulletIV]; break; } } } IcePea.h #import "Pea.h" @interface IcePea : Pea @end IcePea.m #import "IcePea.h" @implementation IcePea - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.plantImage = [UIImage imageNamed:@"plant_3.png"]; self.bulletImage = [UIImage imageNamed:@"bullet_1.png"]; self.costSunCount = 175; } return self; } Nut.h Nut.m #import "Nut.h" @implementation Nut - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.plantImage = [UIImage imageNamed:@"plant_5.png"]; self.costSunCount = 125; } return self; } ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UIViewController -(void)addSunCount:(int)count; @end ViewController.m #import "ViewController.h" #import "SunFlower.h" #import "Pea.h" #import "IcePea.h" #import "Nut.h" #import "ZombA.h" #import "ZombB.h" #import "ZombC.h" #import "ZombD.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *sunCountLabel; @property (strong, nonatomic) IBOutletCollection(UIView) NSArray *boxes; @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *plantIVs; @property (nonatomic, strong)Plant *dragPlant; @property (nonatomic)int zombCount; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self initUI]; //监听阳光被点击的通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(sunDismissAction:) name:@"sunDismissAction" object:nil]; //添加僵尸 [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(addZomb) userInfo:nil repeats:YES]; } -(void)addZomb{ self.zombCount++; // 0-29 0A // 30-59 1B // 60-89 2C // 90 3D int type = self.zombCount/30; Zomb *zomb = nil; switch (type) { case 0: zomb = [[ZombA alloc]init]; break; case 1: zomb = [[ZombB alloc]init]; break; case 2: zomb = [[ZombC alloc]init]; break; default: zomb = [[ZombD alloc]init]; break; } int line = arc4random()%5+1; zomb.frame = CGRectMake(self.view.bounds.size.width, line * 50, 30, 50); [self.view addSubview:zomb]; } -(void)sunDismissAction:(NSNotification *)noti{ int count = [[noti.userInfo objectForKey:@"sunCount"] intValue]; [self addSunCount:count]; } -(void)initUI{ UIImage *plantsImage = [UIImage imageNamed:@"seedpackets.png"]; float w = plantsImage.size.width/18; for (int i=0; i<self.plantIVs.count; i++) { UIImageView *plantIV = self.plantIVs[i]; float x = 0; switch (i) { case 1: x = 2*w; break; case 2: x = 3*w; break; case 3: x = 5*w; break; } CGImageRef subImage = CGImageCreateWithImageInRect(plantsImage.CGImage, CGRectMake(x, 0, w, plantsImage.size.height)); plantIV.image = [UIImage imageWithCGImage:subImage]; CGImageRelease(subImage); } } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; int currentSunCount = self.sunCountLabel.text.intValue; for (int i=0;i<self.plantIVs.count;i++) { UIImageView *plantIV = self.plantIVs[i]; if (plantIV.alpha!=1) { return; } if (CGRectContainsPoint(plantIV.frame, p)) { switch (i) { case 0: if (currentSunCount<50) { return; } self.dragPlant = [[SunFlower alloc]initWithFrame:plantIV.frame]; break; case 1: if (currentSunCount<100) { return; } self.dragPlant = [[Pea alloc]initWithFrame:plantIV.frame]; break; case 2: if (currentSunCount<175) { return; } self.dragPlant = [[IcePea alloc]initWithFrame:plantIV.frame]; break; case 3: if (currentSunCount<125) { return; } self.dragPlant = [[Nut alloc]initWithFrame:plantIV.frame]; break; } self.dragPlant.tag = i; self.dragPlant.delegate = self; [self.view addSubview:self.dragPlant]; } } } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; self.dragPlant.center = p; } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject]; CGPoint p = [t locationInView:self.view]; if (self.dragPlant) { for (UIView *box in self.boxes) { //判断有没有在坑里松手 并且坑里什么都没有 if (CGRectContainsPoint(box.frame, p)&&box.subviews.count==0) { [box addSubview:self.dragPlant]; self.dragPlant.center = CGPointMake(box.bounds.size.width/2, box.bounds.size.height/2); //让植物开火儿 [self.dragPlant beginFier]; //花钱 [self addSunCount:-self.dragPlant.costSunCount]; //处理CD UIImageView *plantIV = self.plantIVs[self.dragPlant.tag]; plantIV.alpha = .3; [UIView animateWithDuration:3 animations:^{ plantIV.alpha = .8; } completion:^(BOOL finished) { plantIV.alpha = 1; }]; } } //判断如果拖拽植物的父视图还是self.view的话 说明没有扔到任何坑里面 就应该删除掉 if ([self.dragPlant.superview isEqual:self.view]) { [self.dragPlant removeFromSuperview]; } self.dragPlant = nil; } } -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ [self.dragPlant removeFromSuperview]; } -(void)addSunCount:(int)count{ int currentCount = self.sunCountLabel.text.intValue; self.sunCountLabel.text = [NSString stringWithFormat:@"%d",currentCount+count]; } ============================================================================================================== 知识点 一、NSFileManager对整个文件进行操作 NSFileHandle 对文件的局部进行操作 1.NSFileManager简单的操作 1.删除文件 删除文件的时候如果想提示出错的地方error参数设置为: NSFileManager *manager = [NSFileManager defaultManager]; NSError *err = nil; //删除文件 [manager removeItemAtPath:@"/Users/tarena/Desktop/dic.xml" error:&err]; if (err) { NSLog(@"出错:%@",[err localizedDescription]); } 2.复制文件 (注意toURL的时候起名字和后缀) //复制文件 [manager copyItemAtURL:[NSURL fileURLWithPath:@"/Users/tarena/Desktop/郑彤.zip"] toURL:[NSURL fileURLWithPath:@"/Users/tarena/Desktop/未命名文件夹/zhengtong.zip"] error:nil]; 3.移动文件 (注意toPath的路径要加上移动文件的名称) [manager moveItemAtPath:@"/Users/tarena6/Desktop/nihao.rtf" toPath:@"/Users/tarena6/Desktop/未命名文件夹/nihao" error:nil]; 4.创建文件 (创建的时候起好名字和后缀) //创建文件 NSString *name = @"张三"; //字符串转换为数据源 NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding]; [manager createFileAtPath:@"/Users/tarena/Desktop/未命名文件夹/a.txt" contents:data attributes:nil]; 5.创建文件夹 (第二个参数用来控制路径中如果有 不存在的文件夹的时候是否 创建成功) [manager createDirectoryAtPath:@"/Users/tarena/Desktop/未命名文件夹/a" withIntermediateDirectories:NO attributes:nil error:nil]; a.判断是否文件存在 //判断是否文件存在 if ([manager fileExistsAtPath:@"/Users/tarena/Desktop/郑彤1.zip"]) { NSLog(@"存在"); } else NSLog(@"不存在"); b.判断是否是文件夹 // 判断是否是文件夹 BOOL isDir = NO; if ([manager fileExistsAtPath:@"/Users/tarena/Desktop/test2.rtf" isDirectory:&isDir]&&isDir) { NSLog(@"文件存在 并且是文件夹"); }else{ NSLog(@"文件不存在 或不是文件夹"); } ***6.获取文件夹下面所有的内容 NSString *direcotry = @"/Users/tarena6/Desktop/未命名文件夹"; NSArray *fileNames = [manager contentsOfDirectoryAtPath:direcotry error:nil]; for (NSString *fileName in fileNames) { NSLog(@"%@",fileName); //得到完整路径,文件名和路径关联到一起 //使用下面的方法拼接字符串 会自动添加一个/ 才可以执行下面的全部删除 NSString *filePath = [direcotry stringByAppendingPathComponent:fileName]; [manager removeItemAtPath:filePath error:nil];//删除 } } 2.NSFileManager 根据指定的路径查找路径下文件夹的内容 主要知识点: 1.hasSuffix:路径最后的即后缀 如:if ([fileName hasSuffix:@"jpg"]) - (BOOL)hasPrefix:(NSString *)aString;前缀 - (BOOL)hasSuffix:(NSString *)aString; 后缀 #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UITextView *myTV; @property (weak, nonatomic) IBOutlet UITextField *myTF; @property (nonatomic)int count; - (IBAction)clicked:(id)sender; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } -(void)findFileInDirecotryPath:(NSString *)path{ NSFileManager *manager = [NSFileManager defaultManager]; NSArray *fileNames = [manager contentsOfDirectoryAtPath:path error:nil]; for (NSString *fileName in fileNames) { NSString *filePath = [path stringByAppendingPathComponent:fileName]; if ([fileName hasSuffix:@"jpg"]) { self.myTV.text = [self.myTV.text stringByAppendingFormat:@"\n%@",filePath]; NSLog(@"%d",self.count++); //[manager removeItemAtPath:filePath error:nil]; } //判断是否是文件夹 如果是继续查找 BOOL isDir; if ([manager fileExistsAtPath:filePath isDirectory:&isDir]&&isDir) { [self findFileInDirecotryPath:filePath]; } } } - (IBAction)clicked:(id)sender { [self findFileInDirecotryPath:@"/Users/tarena6/Desktop/付"];//确定路径 } @end ? 例: 要求:1.以表格的形式根据路径显示文件夹的名称, 2.如果是文件夹,右侧显示小三角的标志,点击小三角继续显示文件夹下的内容 3.右上角的编辑状态,如果不是文件夹可以进入编辑状态,并且可以删除 4.是文件夹的前面就显示文件夹的图标,不是文件夹的如果是图片,前面就显示其包含的图片 5.包含图片的文件,点击弹出新的界面,并平铺到界面上 6.指定文本的,点击进去弹出文本的内容 7.其他的点击弹出警告框 知识点: 1. - (NSString *)lastPathComponent; 路径最后的一个文件夹名称,包括后缀 2.iv.image = [UIImage imageWithContentsOfFile:filePath]; 根据路径添加照片 3.显示文本的视图,属性为.text UITextView *tv = [[UITextView alloc]initWithFrame:vc.view.bounds]; 4.获取路径中的内容 1)tv.text=[[NSString alloc]initWithData:[NSData dataWithContentsOfFile:filePath] encoding:NSUTF8StringEncoding]; 2)tv.text = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; 5. 通过故事板创建 FileListTableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"FileListTableViewController"]; FileListTableViewController.h #import <UIKit/UIKit.h> @interface FileListTableViewController : UITableViewController @property (nonatomic,copy)NSString *direcotryPath; @end FileListTableViewController.m #import "FileListTableViewController.h" @interface FileListTableViewController () @property (nonatomic, strong)NSMutableArray *filePaths; @end @implementation FileListTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.filePaths = [NSMutableArray array]; self.navigationItem.rightBarButtonItem = self.editButtonItem; NSFileManager *manager = [NSFileManager defaultManager]; //判断出 如果是第一次 就显示users下面的内容 if (!self.direcotryPath) { self.direcotryPath =@"/Users/"; } self.title = [self.direcotryPath lastPathComponent]; NSArray *fileNames = [manager contentsOfDirectoryAtPath:self.direcotryPath error:nil]; for (NSString *fileName in fileNames) { //把隐藏文件去掉 if ([fileName hasPrefix:@"."]) { continue; } NSString *filePath = [self.direcotryPath stringByAppendingPathComponent:fileName]; [self.filePaths addObject:filePath]; } } #pragma mark - Table view data source //几行 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.filePaths.count; } //每行的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; NSString *filePath = self.filePaths[indexPath.row]; cell.textLabel.text = [filePath lastPathComponent]; //判断是否是文件夹 if ([self isDirecotryWithPath:filePath]) { cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; //给文件夹添加图标 cell.imageView.image = [UIImage imageNamed:@"direcotry.png"]; }else { cell.accessoryType = UITableViewCellAccessoryNone; //是照片的就显示照片图标 if ([filePath hasSuffix:@"jpg"]||[filePath hasSuffix:@"png"]) { cell.imageView.image = [UIImage imageWithContentsOfFile:filePath]; } } return cell; } //点击每一行显示的效果 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSString *filePath = self.filePaths[indexPath.row]; if ([self isDirecotryWithPath:filePath]) { //如果是文件夹要跳转页面 FileListTableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"FileListTableViewController"]; vc.direcotryPath = filePath; [self.navigationController pushViewController:vc animated:YES]; //查看照片,铺满全屏 }else if ([filePath hasSuffix:@"jpg"]||[filePath hasSuffix:@"png"]){ UIViewController *vc = [[UIViewController alloc]init]; vc.view.backgroundColor = [UIColor whiteColor]; UIImageView *iv = [[UIImageView alloc]initWithFrame:vc.view.bounds]; iv.image = [UIImage imageWithContentsOfFile:filePath]; [iv setContentMode:UIViewContentModeScaleAspectFit]; [vc.view addSubview:iv]; [self.navigationController pushViewController:vc animated:YES]; //查看文件内容 }else if ([filePath hasSuffix:@".h"]||[filePath hasSuffix:@".m"]||[filePath hasSuffix:@".txt"]||[filePath hasSuffix:@".rtf"]){ UIViewController *vc = [[UIViewController alloc]init]; UITextView *tv = [[UITextView alloc]initWithFrame:vc.view.bounds]; tv.text = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [vc.view addSubview:tv]; [self.navigationController pushViewController:vc animated:YES]; //点击其他的就弹出警告框 }else{ UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"此文件格式暂不支持,请期待下一版本!" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alertView show]; } } //判断是否是文件夹 -(BOOL)isDirecotryWithPath:(NSString *)path{ NSFileManager *manager = [NSFileManager defaultManager]; BOOL isDir; if ([manager fileExistsAtPath:path isDirectory:&isDir]&&isDir) { return YES; } return NO; } //表格的编辑模式 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { NSString *filePath = self.filePaths[indexPath.row]; if ([self isDirecotryWithPath:filePath]) { return NO; } return YES; } // 删除模式 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { NSString *filePath = self.filePaths[indexPath.row]; [[NSFileManager defaultManager]removeItemAtPath:filePath error:nil]; [self.filePaths removeObject:filePath]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { } } ? ? ? ? 2.NSFileHandle 对局部文件进行操作 1.创建路径 NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:@"/Library/Screen Savers/Default Collections/2-Aerial/Aerial06.jpg"]; 2.得到文件的长度 方法一:Data获取 此时会把文件加载到内存中 NSData *data = [NSData dataWithContentsOfFile:@"/Users/tarena/Documents/UIKit Core/Day1DragImage/ Day1DragImage/CatInBin.jpg"]; int length = data.length; 方法二:通过fileHandel获取长度 不会把文件加载到内存 int fileLength = (int)[fh seekToEndOfFile]; //把游标重置到开始的位置 [fh seekToFileOffset:fileLength/2]; 3.文件的读写 读:1.)readDataToEndOfFile 读到最后 2.)[fh seekToFileOffset:fileLength/2];中间的部分 写:1)NSFileHandle *writeFH = [NSFileHandle fileHandleForWritingAtPath:@"/Users/tarena/Desktop/未命名文件夹/a.jpg”];写到此路径中 2)writeData:写 将读到的写到路径中 [writeFH writeData:subData]; 如果使用fileHandle写数据的时候必须保证目标文件存在 要求:首先取出前半部分,读到界面上,并将前半部分写到新创建的文件中,然后修改长度到后半部分,再将后半部分写到文件中(注意:有时候读取一半的时候界面显示的是整张图片,那说明是像素点降低了) #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:@"/Library/Screen Savers/Default Collections/2-Aerial/Aerial06.jpg"]; //得到文件的长度 //1.Data获取 此时会把文件加载到内存中 // NSData *data = [NSData dataWithContentsOfFile:@"/Users/tarena/ Documents/UIKit Core/Day1DragImage/Day1DragImage/CatInBin.jpg"]; // int length = data.length; //2.通过fileHandel获取长度 不会把文件加载到内存 int fileLength = (int)[fh seekToEndOfFile]; //把游标重置到开始的位置 [fh seekToFileOffset:fileLength/2]; NSData *subData = [fh readDataToEndOfFile];//读数据 UIImageView *iv = [[UIImageView alloc]initWithFrame:self.view.bounds]; iv.image = [UIImage imageWithData:subData]; [self.view addSubview:iv]; //创建文件 //[[NSFileManager defaultManager]createFileAtPath:@"/Users/tarena/Desktop/未命名文件夹/a.jpg" contents:nil attributes:nil]; //如果使用fileHandle写数据的时候必须保证目标文件存在 NSFileHandle *writeFH = [NSFileHandle fileHandleForWritingAtPath:@"/Users/tarena/Desktop/未命名文件夹/a.jpg"];//写数据 [writeFH seekToFileOffset:fileLength/2]; [writeFH writeData:subData]; } @end =========================================================================== 知识点 二、XML可扩展标记性语言 1.XML可扩展标记性语言 (主要用来封装) 1.Other—>Empty—>创建xml(文件名.xml) 创建Students.xml 2.两种解析方式: SAX解析:逐行解析,从根元素开始,可以解析超大数据不占内存,比较适合解析大文件 DOM解析:把所有xml数据一次性加载到内存中,在内存中构建树状结构进行解析,解析流程简单 方便实用,比较适合解析小文件 3.两种解析手段 1.苹果原生 NSXMLParser:SAX解析 2.第三方框架 libxml2: 同时支持SAX解析和DOM解析 GDataXML: DOM解析 4.解析的时候从开始—文本—结束,注意的是解析过程中空格也是文本 2.SAX解析: Students.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?> <students> <student sid="1"> <name>小明</name> <chinese>38</chinese> <math>30</math> <english>18</english> </student> <student sid="2"> <name>小丽</name> <chinese>78</chinese> <math>90</math> <english>78</english> </student> <student sid="3"> <name>小王</name> <chinese>28</chinese> <math>10</math> <english>48</english> </student> </students> Student.h #import <Foundation/Foundation.h> @interface Student : NSObject @property (nonatomic, copy)NSString *name; @property (nonatomic, copy)NSString *sid; @property (nonatomic)int chinese; @property (nonatomic)int math; @property (nonatomic)int english; @end Student.m MyXMLParser.h #import <Foundation/Foundation.h> #import "Student.h" @interface MyXMLParser : NSObject<NSXMLParserDelegate> @property (nonatomic, strong)NSMutableArray *students; @property (nonatomic, strong)Student *s; @property (nonatomic, copy)NSString *currentString; -(NSMutableArray *)parseStudentsByPath:(NSString *)path; @end MyXMLParser.m #import "MyXMLParser.h" @implementation MyXMLParser -(NSMutableArray *)parseStudentsByPath:(NSString *)path{ self.students = [NSMutableArray array]; NSData *data = [NSData dataWithContentsOfFile:path]; NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data]; parser.delegate = self; //开始解析 [parser parse]; return self.students; } //开始标签的时候会执行这个方法 -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ NSLog(@"开始:%@",elementName); if ([elementName isEqualToString:@"student"]) { self.s = [[Student alloc]init]; self.s.sid = [attributeDict objectForKey:@"sid"]; [self.students addObject:self.s]; } } //标签之间的文本 -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ self.currentString = string; NSLog(@"文本:%@",string); } //结束标签 -(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ if ([elementName isEqualToString:@"name"]) { self.s.name = self.currentString; }else if ([elementName isEqualToString:@"chinese"]) { self.s.chinese = self.currentString.intValue; }else if ([elementName isEqualToString:@"math"]) { self.s.math = self.currentString.intValue; }else if ([elementName isEqualToString:@"english"]) { self.s.english = self.currentString.intValue; } NSLog(@"结束:%@",elementName); } @end ViewController.h ViewController.m #import "ViewController.h" #import "MyXMLParser.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; MyXMLParser *parser = [[MyXMLParser alloc]init]; NSArray *students = [parser parseStudentsByPath:@"/Users/tarena6/Desktop/day03/Day3XML/Day3XML/students.xml"]; NSLog(@"%@",students); } @end 3.DOM解析 Students.xml 同上 Student.h Student.m 同上 ViewController.h ViewController.m NSMutableArray *students = [MyXMLParser parseStudentsByPath:@"/Users/tarena/Documents/UIKit Core/Day3DomXml/Day3DomXml/students.xml"]; MyXMLParser.h #import <Foundation/Foundation.h> #import "Student.h" @interface MyXMLParser : NSObject + (NSMutableArray *)parseStudentsByPath:(NSString *)path;//静态方法 @end MyXMLParser.m #import "MyXMLParser.h" #import "TBXML.h" @implementation MyXMLParser + (NSMutableArray *)parseStudentsByPath:(NSString *)path{ NSMutableArray *students = [NSMutableArray array]; NSData *data = [NSData dataWithContentsOfFile:path]; TBXML *tbXML = [[TBXML alloc]initWithXMLData:data error:nil]; //获得root节点 TBXMLElement *studentsEle = tbXML.rootXMLElement; TBXMLElement *stuEle = [TBXML childElementNamed:@"student" parentElement:studentsEle]; //遍历每一个学生元素 while (stuEle) { Student *s = [[Student alloc]init]; s.sid = [TBXML valueOfAttributeNamed:@"sid" forElement:stuEle]; TBXMLElement *nameEle = [TBXML childElementNamed:@"name" parentElement:stuEle]; TBXMLElement *chineseEle = [TBXML childElementNamed:@"chinese" parentElement:stuEle]; TBXMLElement *mathEle = [TBXML childElementNamed:@"math" parentElement:stuEle]; TBXMLElement *englishEle = [TBXML childElementNamed:@"english" parentElement:stuEle]; //获取元素中的文本 s.name = [TBXML textForElement:nameEle]; s.chinese = [[TBXML textForElement:chineseEle]intValue]; s.math = [[TBXML textForElement:mathEle]intValue]; s.english = [[TBXML textForElement:englishEle]intValue]; [students addObject:s]; stuEle = [TBXML nextSiblingNamed:@"student" searchFromElement:stuEle]; } return students; } @end TBXML.h TBXML.m 引入第三方框架文件 ======================================================================== 知识点 三、Plist文件和数组或字典 1.把内存中的字典或数组保存成plist文件 // 把内存中的字典或数组保存成plist文件 NSArray *names = @[@"老杨",@"小泽",@"我",@"老张"]; [names writeToFile:@"/Users/tarena/Desktop/names.plist" atomically:YES]; 2.把plist文件加载到内存中 成字典或数组 // 把plist文件加载到内存中 成字典或数组 NSArray *names = [NSArray arrayWithContentsOfFile:@"/Users/tarena/Desktop/names.plist"]; NSLog(@"%@",names); ? 例:1.故事板中,scollView,TextView来布局 2.将plist以数组的形式添加到内存中 3.遍历出plist中每个项目,用字典的方式取出plist每个项目中照片key值对应的value值 4.根据要求创建button,其背景图为value值 5.设置scollView的大小,实现滚动 6.为按钮添加点击事件,点击图片即可以将其对应的文本显示在创建的文本框中 7.主要用tag来记录button的位置,用于字典获取plist中图片的文本,赋给文本框即可 #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UITextField *myTF; @property (weak, nonatomic) IBOutlet UIScrollView *mySV; @property (nonatomic, strong)NSArray *faces; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //项目中拖进的plist表格路径 NSString *path = @"/Users/tarena6/Desktop/day04/Day4Faces/Day4Faces/face/emoticons.plist"; self.faces = [NSArray arrayWithContentsOfFile:path]; //遍历出plist for (int i=0; i<self.faces.count; i++) { //根据字典取出照片 NSDictionary *faceDic = self.faces[i]; NSString *imageName = [faceDic objectForKey:@"png"]; UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(i%8*40, i/8*40, 40, 40)]; button.tag = i; // NSLog(@"%ld",(long)button.tag);记录按钮相对应的位置是第几个 [button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; [button addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside]; [self.mySV addSubview:button]; } int line = self.faces.count%8==0?self.faces.count/8 : self.faces.count/8+1; [self.mySV setContentSize:CGSizeMake(0, line*40)]; } //点击图片按钮,对应的文本框显示出其对应的文本 -(void)clicked:(UIButton *)btn{ NSDictionary *dic = self.faces[btn.tag]; NSString *text = [dic objectForKey:@"chs"]; self.myTF.text = [self.myTF.text stringByAppendingString:text]; } @end ? ================================================================================================================= 知识点 四、沙箱/沙盒 1.沙箱/沙盒 1.工程名称.app:保存着工程中用到的所有的素材 此包是只读的 2.Documents:使用最多、用来保存有用的数据 iTunes会备份和恢复 3.Library/Caches:缓存文件夹 缓存数据 4.Library/Prefernces:偏好设置用来保存有用的小量数据 iTunes会备份和恢复 5.tmp:临时文件夹 此文件夹下的数据 不定时的可能会被销毁掉 查看电脑储存的路径方式: 1.打开Finder,点击上方第三个按钮,点击前往或者使用快捷键:(Shift+Command+g) 2.将输出的路径复制到文本框中,就可以到达想要到的路径 1.得到沙箱的根目录 NSString *home = NSHomeDirectory(); NSLog(@"%@",home); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5 2.得到工程中根路径下面的素材完整路径 NSString *path = [[NSBundle mainBundle]pathForResource:@"direcotry" ofType:@"png"]; 照片名称 NSLog(@"%@",path); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5/Day4Sandbox.app/direcotry.png 3.得到app包的路径 NSString *appPath = [[NSBundle mainBundle]resourcePath]; NSLog(@"%@",appPath); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5/Day4Sandbox.app 4.获取Documents路径 NSString *documentsPath = [home stringByAppendingPathComponent:@"Documents"]; documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSLog(@"documentsPath:%@",documentsPath); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5/Documents 5.获得临时文件夹路径 NSString *tmpPath = NSTemporaryDirectory(); NSLog(@"%@",tmpPath); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5/tmp/ 6.获取缓存路径 NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSLog(@"%@",cachesPath); /Users/tarena6/Library/Application Support/iPhone Simulator/7.1/Applications/77AF5459-865F-4792-8588-54B1378F07A5/Library/Caches 2.练习,将一个文件的路径复制到Documents路径下 #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //得到沙箱的根目录 NSString *home = NSHomeDirectory(); NSLog(@"%@",home); //文件的路径 NSString *filePath = [[NSBundle mainBundle]pathForResource:@"direcotry" ofType:@"png"]; //documents的路径 NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; //将文件的路径复制到documents的路径下 NSString *newFilePath = [documentsPath stringByAppendingPathComponent:[filePath lastPathComponent]]; [[NSFileManager defaultManager]copyItemAtPath:filePath toPath:newFilePath error:nil]; } @end ? ======================================================================== 知识点 五、NSUserDefaults 将数据保存在系统的偏好设置中plist 1.NSUserDefaults 偏好设置 第二次运行程序的时候,都保留了之前的数据 步骤: 1.创建NSUserDefaults实例 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults] 2.读取之前保存的数据 int runCount = [ud integerForKey:@"runCount"]; 3.将数据保存到内存中 [ud setInteger:++runCount forKey:@"runCount"]; 4.数据同步,把内存中的数据同步到文件中 [ud synchronize]; 例:保存两个控件的滑动状态 @interface ViewController () - (IBAction)VolumeChange:(UISlider *)sender; - (IBAction)playModeChange:(UISegmentedControl *)sender; @property (weak, nonatomic) IBOutlet UISegmentedControl *playModeSC; @property (weak, nonatomic) IBOutlet UISlider *mySlider; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@",NSHomeDirectory()); NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; //把之前保存的数据取出来 int runCount = [ud integerForKey:@"runCount"]; //保存到内存中 [ud setInteger:++runCount forKey:@"runCount"]; //数据同步 把内存中的数据同步到文件中 [ud synchronize]; NSLog(@"程序运行了%d次",runCount); self.mySlider.value = [ud floatForKey:@"volume"]; self.playModeSC.selectedSegmentIndex = [ud integerForKey:@"playMode"]; } - (IBAction)VolumeChange:(UISlider *)sender { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; [ud setFloat:sender.value forKey:@"volume"]; [ud synchronize]; } - (IBAction)playModeChange:(UISegmentedControl *)sender { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; [ud setInteger:sender.selectedSegmentIndex forKey:@"playMode"]; selectedSegmentIndex选择了第几个按钮 [ud synchronize]; } @end ? ? 例2:每次运行粉界面和黄界面交替出现 AppDelegate.h AppDelegate.m #import "AppDelegate.h" #import "SecondViewController.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSUserDefaults*user=[NSUserDefaults standardUserDefaults]; int runCount=[user integerForKey:@"runCount"]; [user setInteger:++runCount forKey:@"runCount"]; [user synchronize]; NSLog(@"%d",runCount); if (runCount%2==0) { //偶数时,显示粉色界面 SecondViewController自己创建的控制器 SecondViewController*sec=[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"SecondView"]; SecondViewController控制器的ID self.window.rootViewController=sec; } return YES; } ? ? ============================================================================================ 知识点 六、 NSKeyedArchiver 归档 、 NSKeyedUnarchiver 反归档 1. NSKeyedArchiver 归档 把对象和data之间互相转换 (只有实现了NSCoding协议才能将对象转换成data) 2.归档,将对象转为data 1).归档方法一:可以将多个对象添加到data中 1.准备一个可变的Data 2.创建归档对象 3.编码 4.完成编码 - (void)viewDidLoad { [super viewDidLoad]; NSArray *names = @[@"张三",@"李四",@"王五",@"赵六",@"田七"]; //1.准备一个可变的Data NSMutableData *data = [NSMutableData data]; //2.创建归档对象 NSKeyedArchiver *arch = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data]; //3.编码 [arch encodeObject:names forKey:@"names"]; //可以往一个data中添加很多对象 //[arch encodeObject:names2 forKey:@"names2"]; //4.完成编码 [arch finishEncoding]; [data writeToFile:@"/Users/tarena/Desktop/names.arch" atomically:YES]; 2).归档方法二:只能将一个对象添加到data中 - (void)viewDidLoad { [super viewDidLoad]; //NSLog(@"%@",self.view.subviews); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.view.subviews]; [data writeToFile:@"/Users/tarena/Desktop/subviews_1" atomically:YES]; } ? 3.反归档 把data转回对象 1)反归档方法一: NSData *data = [NSData dataWithContentsOfFile:@"/Users/tarena/Desktop/names.arch"]; //1.创建反归档对象 NSKeyedUnarchiver *unArch = [[NSKeyedUnarchiver alloc]initForReadingWithData:data]; //2.解码 NSArray *names = [unArch decodeObjectForKey:@"names"]; NSLog(@"%@",names); 2)反归档方法二: NSData *data = [NSData dataWithContentsOfFile:@"/Users/tarena6/Desktop/subviews_1"]; NSArray *subViews = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 例:1.每两秒变换一次界面图片, 2.利用归档,事先从一个工程中创建好view界面,然后把五个界面分别转换成data,名字要有guilv 3.利用反归档将data转换为对象,再另一个工程中显示 #import "ViewController.h" @interface ViewController () @property (nonatomic)int count; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *path = [[NSBundle mainBundle]pathForResource:@"subviews_0" ofType:@""]; NSData *data = [NSData dataWithContentsOfFile:path]; NSArray *subViews = [NSKeyedUnarchiver unarchiveObjectWithData:data]; for (int i=0;i<subViews.count;i++) { UIView *view = subViews[i]; UIImageView *imageView = [[UIImageView alloc]initWithFrame:view.frame]; imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%d.tif",i]]; imageView.layer.borderWidth = 1; [self.view addSubview:imageView]; } [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(changeLayout) userInfo:nil repeats:YES]; } -(void)changeLayout{ self.count++; NSString *path = [[NSBundle mainBundle]pathForResource:[NSString stringWithFormat:@"subviews_%d",self.count%5] ofType:@""]; NSData *data = [NSData dataWithContentsOfFile:path]; NSArray *newSubviews = [NSKeyedUnarchiver unarchiveObjectWithData:data]; for (int i=0; i<newSubviews.count; i++) { UIImageView *iv = self.view.subviews[i]; UIView *view = newSubviews[i]; [UIView animateWithDuration:1 animations:^{ iv.frame = view.frame; }]; } } @end ? ? ? 4.自定义归档 1.创建一个类继承NSObject,遵守协议<NSCoding>,创建两个属性 2.然后实现两个方法,并保存和读取其数据 - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInt:self.age forKey:@"age"]; } - (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if (self) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntForKey:@"age"]; } return self; } 例:1.自定义一个类,创建两个属性name,length。遵守协议并实现协议 2.A界面获取一个路径,然后将其遍历,获取其长度和名字赋给自定义的类 3.然后将其保存到数组中,再进行归档,保存到桌面 4.点击A界面的按钮进入到B界面,B界面是个表示图控制器,将保存在桌面的文件进行反归档,获取到data,以表格的形式将名称和长度显示出来 File.h #import <Foundation/Foundation.h> @interface File : NSObject<NSCoding> @property (nonatomic, copy)NSString *name; @property (nonatomic)int length; @property (nonatomic)BOOL isDir; @end File.m #import "File.h" @implementation File - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInt:self.length forKey:@"length"]; [aCoder encodeBool:self.isDir forKey:@"isDir"]; } - (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if (self) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.length = [aDecoder decodeIntForKey:@"length"]; self.isDir = [aDecoder decodeBoolForKey:@"isDir"]; } return self; } @end ViewController.h ViewController.m #import "ViewController.h" #import "File.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *path = @"/Users/tarena/Desktop"; NSFileManager *manager = [NSFileManager defaultManager]; NSArray *fileNames = [manager contentsOfDirectoryAtPath:path error:nil]; NSMutableArray *files = [NSMutableArray array]; for (NSString *fileName in fileNames) { File *file = [[File alloc]init]; file.name = fileName; NSString *filePath = [path stringByAppendingPathComponent:fileName]; NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:filePath]; file.length = (int)[fh seekToEndOfFile]; [files addObject:file]; BOOL isDir; if ([manager fileExistsAtPath:filePath isDirectory:&isDir]&&isDir) { file.isDir = YES; } } NSData *data = [NSKeyedArchiver archivedDataWithRootObject:files]; [data writeToFile:@"/Users/tarena/Desktop/files" atomically:YES]; } @end FileListTableViewController.h FileListTableViewController.m #import "FileListTableViewController.h" #import "File.h" @interface FileListTableViewController () @property (nonatomic, strong)NSArray *files; @end @implementation FileListTableViewController - (void)viewDidLoad { [super viewDidLoad]; NSData *data = [NSData dataWithContentsOfFile:@"/Users/tarena/Desktop/files"]; self.files = [NSKeyedUnarchiver unarchiveObjectWithData:data]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.files.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; File *file = self.files[indexPath.row]; cell.textLabel.text = file.name; NSString *size = nil; if (file.length>1024*1024) { size = [NSString stringWithFormat:@"%dM",file.length/1024/1024]; }else{ size = [NSString stringWithFormat:@"%dKB",file.length/1024]; } if (file.isDir) { size = @"文件夹"; } cell.detailTextLabel.text = size; return cell; } @end ? ? ==================================================================== 知识点 七、CoreData 封装了sqlite数据库的框架 1.基本创建 1. 创建项目时直接选Application—>Empty Application 2.勾上Use Core Data 3.开始创建Storyboard.storyboard (User Interface 里创建) 4.在主项目中Main Interface中下拉框选择Storyboard 5.开始配置CoreData.xcdatamodeld,点击下边Add Entety进行添加对象,在右侧Attributes中添加属性 6.实现添加对象的类,点击CoreData,选择第三个下一步创建即可,然后项目中就多了一个类 2.基本功能 0.获取AppDelegate的上下文 AppDelegate *app = [UIApplication sharedApplication].delegate; 1.增加 Person *p = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:app.managedObjectContext]; p.name = @"张三"; p.age = @(20); p.mobileCod = @"13838383388"; [app saveContext]; 2.删除 NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Person"]; NSArray *persons = [app.managedObjectContext executeFetchRequest:request error:nil]; for (Person *p in persons) { if ([p.name isEqualToString:@"张三"]) { [app.managedObjectContext deleteObject:p]; [app saveContext]; 3.修改 NSFetchRequest *reqeust = [[NSFetchRequest alloc]initWithEntityName:@"Person"]; NSArray *persons = [app.managedObjectContext executeFetchRequest:reqeust error:nil]; for (Person *p in persons) { if ([p.name isEqualToString:@"李四"]) { p.name = @"王五"; p.age = @(88); } } [app saveContext]; } 4.查看 NSFetchRequest *reqeust = [[NSFetchRequest alloc]initWithEntityName:@"Person"]; NSArray *persons = [app.managedObjectContext executeFetchRequest:reqeust error:nil]; for (Person *p in persons) { NSLog(@"%@ %@ %@",p.name,p.age,p.mobileCod); } 1)让查询结果进行排序 NSSortDescriptor [request setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]]]; //根据年龄升序排序 2)设置查询条件 NSPredicate NSPredicate *pre = nil; //1.比较运算符> < >= <= == != pre = [NSPredicate predicateWithFormat:@"age>40"]; //2.范围运算符 IN、BETWEEN pre = [NSPredicate predicateWithFormat:@"age BETWEEN{30,40}"]; pre = [NSPredicate predicateWithFormat:@"name IN{‘aaaaa‘,‘bcd‘}"]; //3.字符串处理 BEGINSWITH ENDSWITH CONTAINS c不区分大小写 d不区分发音 pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] ‘a‘"]; pre = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] ‘a‘"]; //4.通配符:LIKE ?代表单个字符 *多个位置字符 pre = [NSPredicate predicateWithFormat:@"name LIKE[cd] ‘*m‘"]; [request setPredicate:pre]; 如: //几行 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Team"]; NSPredicate*pre=nil; pre=[NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] ‘a‘"]; [request setPredicate:pre ]; self.teams = [self.app.managedObjectContext executeFetchRequest:request error:nil]; return self.teams.count; } 3.例:一个数据库 1.创建一个Core Data数据库,保存一个类,属性为name,age 2.两个界面,A界面是tableViewControlle,B界面是普通控制器,B中有两个文本框 3.点击A界面左上角的加号,推出B界面,然后在B界面的文本框中输入内容,保存在创建的Core Data数据库中,点击确定按钮,传回到A界面显示在表格中,利用数据库增加的功能 4.在A 界面利用数据库查看的功能,接收B界面传回保存在数据库的内容 5.删除功能,点击右上角的编辑按钮,进行删除每行的内容 6.点击每行,可以跳转到B 界面,把每行的内容显示到B界面的文本框中,利用segue方法实现传值 知识点:Segue 1.通过设置Segue的identifier来确定是跳转到哪 - (IBAction)gobackText:(UIBarButtonItem *)sender { [self performSegueWithIdentifier:@"identifi" sender:nil]; //跳转到另一个界面 } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ Person*p=self.persons[indexPath.row]; [self performSegueWithIdentifier:@"identifi" sender:p];//跳转到另一个界面,可以传参 } 2.如果有参数要传到另一个界面的时候,用系统提供的方法 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if (sender) { TextViewController*textVC=segue.destinationViewController; textVC.editPerson=sender; } } 创建的数据库 ? Person.h #import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface Person : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSNumber * age; @end Person.m #import "Person.h" @implementation Person @dynamic name; @dynamic age; @end PersonsTableViewController.h PersonsTableViewController.m #import "Person.h" #import "PersonsTableViewController.h" #import "AppDelegate.h" #import "PersonInfoViewController.h" @interface PersonsTableViewController () @property (nonatomic, strong)NSArray *persons; @property (nonatomic, weak)AppDelegate *app; @end @implementation PersonsTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.app = [UIApplication sharedApplication].delegate; self.title = @"通讯录"; self.navigationItem.rightBarButtonItem = self.editButtonItem; UIBarButtonItem *addItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addPerson)]; self.navigationItem.leftBarButtonItem = addItem; } //右上角+按钮 -(void)addPerson{ [self performSegueWithIdentifier:@"personinfovc" sender:nil]; } #pragma mark - Table view data source -(void)viewWillAppear:(BOOL)animated{ [self.tableView reloadData];//更新界面 } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Person"]; self.persons = [self.app.managedObjectContext executeFetchRequest:request error:nil]; return self.persons.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; Person *p = self.persons[indexPath.row]; cell.textLabel.text = p.name; cell.detailTextLabel.text = [NSString stringWithFormat:@"%@",p.age]; return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ Person *p = self.persons[indexPath.row]; [self performSegueWithIdentifier:@"personinfovc" sender:p]; } // 删除功能 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { Person *person = self.persons[indexPath.row]; [self.app.managedObjectContext deleteObject:person]; [self.app saveContext]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } //此方法中写传递参数的代码 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { //判断是否是编辑 if (sender) { PersonInfoViewController *vc = segue.destinationViewController; vc.editPerson = sender; } } @end PersonInfoViewController.h #import <UIKit/UIKit.h> #import "Person.h" @interface PersonInfoViewController : UIViewController @property (nonatomic, strong)Person *editPerson; @end PersonInfoViewController.m #import "PersonInfoViewController.h" #import "AppDelegate.h" #import "Person.h" @interface PersonInfoViewController () - (IBAction)clicked:(id)sender; @property (weak, nonatomic) IBOutlet UITextField *ageTF; @property (weak, nonatomic) IBOutlet UITextField *nameTF; @end @implementation PersonInfoViewController - (void)viewDidLoad { [super viewDidLoad]; //判断是否是编辑 if (self.editPerson) { self.nameTF.text = self.editPerson.name; self.ageTF.text = [NSString stringWithFormat:@"%@",self.editPerson.age]; } } - (IBAction)clicked:(id)sender { AppDelegate *app = [UIApplication sharedApplication].delegate; //如果是编辑 就修改 if (self.editPerson) { self.editPerson.name = self.nameTF.text; self.editPerson.age = @(self.ageTF.text.intValue); }else{//如果editPerson没有值说明是新建person Person *p = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:app.managedObjectContext]; p.name = self.nameTF.text; p.age = @(self.ageTF.text.intValue); } [app saveContext]; [self.navigationController popViewControllerAnimated:YES]; } @end ? ? ? 4.例:两个有联系的数据库 1.通过.xcdatamodeld添加两个类,设置好各自的属性 2.然后点击右下角进行切换模式,进行连线。 3.双击最后一行修改属性,并通过右侧的窗口设置Type,To One 或To Many 4.对两个类进行实例化 ? To Many To One 例:要求: 1.创建两个数据库球队和球员,并建立关系实例化 2.创建两个table控制器,A控制器显示球队,点击左上角加号弹出AlertView,输入数据后显示在单元格中 3.点击单元格又弹出AlertView,对当前单元格进行修改 4.点击右上角Edit按钮,进入编辑模式,进行删除操作 5.点击每行右侧的圆圈,进入B界面球员,点击右上角加号弹出AlertView进行添加,显示到单元格,删除功能,修改功能同第一个界面 6.当返回到A界面时,右侧的人数根据B界面添加的球员有关 TeamsTableViewController.h TeamsTableViewController.m #import "PlayersTableViewController.h" #import "TeamsTableViewController.h" #import "AppDelegate.h" #import "Team.h" @interface TeamsTableViewController ()<UIAlertViewDelegate> @property (nonatomic, weak)AppDelegate *app; @property (nonatomic, strong)NSArray *teams; @property (nonatomic, strong)Team *editTeam; @end @implementation TeamsTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.app = [UIApplication sharedApplication].delegate; self.navigationItem.rightBarButtonItem = self.editButtonItem; UIBarButtonItem *addItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addTeam)]; self.navigationItem.leftBarButtonItem = addItem; } //弹出alertView -(void)addTeam{ UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请输入球队名称" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; [alertView show]; } //实现协议 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ NSString *name = [alertView textFieldAtIndex:0].text; if (buttonIndex==1) { if (alertView.tag==0) { Team *t = [NSEntityDescription insertNewObjectForEntityForName:@"Team" inManagedObjectContext:self.app.managedObjectContext]; t.name = name; }else{//修改 self.editTeam.name = name; } [self.app saveContext]; [self.tableView reloadData]; } } //更新 -(void)viewWillAppear:(BOOL)animated{ [self.tableView reloadData]; } //几行 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Team"]; self.teams = [self.app.managedObjectContext executeFetchRequest:request error:nil]; return self.teams.count; } //单元格内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; Team *t = self.teams[indexPath.row]; cell.textLabel.text = t.name; cell.detailTextLabel.text = [NSString stringWithFormat:@"%d人",t.players.count]; UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoLight]; btn.tag = indexPath.row; [btn addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside]; cell.accessoryView = btn; return cell; } //点击圆圈按钮 -(void)clicked:(UIButton*)btn{ Team *team = self.teams[btn.tag]; [self performSegueWithIdentifier:@"playersvc" sender:team]; } //删除 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { Team *t = self.teams[indexPath.row]; [self.app.managedObjectContext deleteObject:t]; [self.app saveContext]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } //点击单元格 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ self.editTeam = self.teams[indexPath.row]; UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请修改球队名称" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; alertView.tag = 1; [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; UITextField *tf = [alertView textFieldAtIndex:0]; tf.text = self.editTeam.name; [alertView show]; } //把值传到B界面 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { PlayersTableViewController *vc = segue.destinationViewController; vc.team = sender; } @end PlayersTableViewController.h #import <UIKit/UIKit.h> #import "Team.h" @interface PlayersTableViewController : UITableViewController @property (nonatomic, strong)Team *team; @end PlayersTableViewController.m #import "PlayersTableViewController.h" #import "Player.h" #import "AppDelegate.h" @interface PlayersTableViewController () @property (nonatomic, strong)NSArray *players; @property (nonatomic, weak)AppDelegate *app; @property (nonatomic, strong)Player *editPlayer; @end @implementation PlayersTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.app = [UIApplication sharedApplication].delegate; self.title = self.team.name; UIBarButtonItem *addItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addPlayer)]; self.navigationItem.rightBarButtonItems = @[self.editButtonItem,addItem]; } -(void)addPlayer{ UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请输入球员名称" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; [alertView show]; } //实现协议 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ NSString *name = [alertView textFieldAtIndex:0].text; if (buttonIndex==1) { if (alertView.tag==0) { Player *p = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:self.app.managedObjectContext]; p.name = name; p.myTeam = self.team; }else{//修改 self.editPlayer.name = name; } [self.app saveContext]; [self.tableView reloadData]; } } //几行 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 把set转成array self.players = [self.team.players allObjects]; return self.team.players.count; } //单元格内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; Player *p = self.players[indexPath.row]; cell.textLabel.text = p.name; return cell; } //删除单元格 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { Player *p = self.players[indexPath.row]; [self.app.managedObjectContext deleteObject:p]; [self.app saveContext]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } //点击单元格 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ self.editPlayer = self.players[indexPath.row]; UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"请修改球员名称" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; alertView.tag = 1; [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; UITextField *tf = [alertView textFieldAtIndex:0]; tf.text = self.editPlayer.name; [alertView show]; } @end ? ? ? ========================================================================================= 知识点 八、多媒体 (图片、音频、视频) 1.查看系统相册保存的照片 UIImagePickerController 1.创建查看系统照片的实例 ,需设置代理,遵守协议 2.实现方法 ,点击照片到下面的scrollView中 3.每张照片排布存放,每个照片的右上角有编辑按钮,点击可以删除图片 4.scrollView的右上角有完成按钮返回到主界面,显示到最上边 5.长按图片移动到中间的View区域 6.在view中点击图片,图片可以实现移动,旋转,缩放,置上的功能 7.当点击长按图片拖放到最下边的imageView中时,此时中间View的背景图更换成下边imageView中显示的图片 8.将view中的内容保存在一张照片中 9.点击最右下角的保存按钮,将图片保存到内存中,在照片查看器可以查看到 10.为图片添加滤镜效果 知识点: 1.) UIImagePickerController 获取系统照片 需设置代理,遵守协议 < UINavigationControllerDelegate, UIImagePickerControllerDelegate> UIImagePickerController *ipc = [[UIImagePickerController alloc]init]; [ipc setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; ipc.delegate = self; ipc.allowsEditing = YES; [self presentViewController:ipc animated:YES completion:nil]; 2.)属性: //设置查看照片的保存方式 [pic setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; //文件夹形式显示 UIImagePickerControllerSourceTypeCamera //照相机形式显示 3.)照片的Key值 UIImagePickerControllerOriginalImage //原始照片 UIImagePickerControllerMediaType // 4.)向一个控件插入一个控件 (atIndex 代表插入到哪) [self.editView insertSubview:self.backgroundIV atIndex:0]; 5.)取出一个控件中的内容,保存在一张图里(截图功能) -(UIImage *)getImageFromView:(UIView *)view{ ** //想保存的控件传给view即可 //创建一个画布 UIGraphicsBeginImageContext(view.frame.size); //把view中的内容渲染到画布中 [view.layer renderInContext:UIGraphicsGetCurrentContext()]; //把画布中的图片取出来 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); //结束渲染 UIGraphicsEndImageContext(); return image; 6.)保存照片 1.把图片保存到系统相册中 UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 2.将照片保存在指定的位置 UIImage *image = [info objectForKey:@"UIImagePickerControllerEditedImage"]; NSString *url = [[info objectForKey:UIImagePickerControllerReferenceURL] description]; // 把UIImage转成Data if ([url hasSuffix:@"JPG"]) { // 把UIImage转成Data self.imageData = UIImageJPEGRepresentation(image, 1); }else{//PNG self.imageData = UIImagePNGRepresentation(image); } self.selectedIV.image = image; 保存在此imageView [self dismissViewControllerAnimated:YES completion:nil]; 7.)手势自带一个视图view ,相当于此时点击的图片 UIImageView *tapIV = (UIImageView *)longPress.view; 8.)连续手势都有state属性,就是点击的时机 (一般配合switch使用) UIGestureRecognizerStateBegan 开始 UIGestureRecognizerStateChanged 拖动 UIGestureRecognizerStateEnded 结束 例:day06—照片控制器 #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *bgIV; @property (weak, nonatomic) IBOutlet UIView *editView; @property (nonatomic, strong)UIImageView *backgroundIV; @property (nonatomic, strong) UIImageView *dragIV; @property (weak, nonatomic) IBOutlet UIScrollView *mySV; - (IBAction)clicked:(id)sender; @property (nonatomic, strong)UIScrollView *selectedImageSV; @property (nonatomic, strong)NSMutableArray *selectedImages; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } //获取图片和保存图片的按钮 - (IBAction)clicked:(id)sender { UIButton *btn = sender; if (btn.tag == 0) { self.selectedImages =[NSMutableArray array];UIImagePickerController *ipc = [[UIImagePickerController alloc]init]; ipc.delegate = self; // ipc.allowsEditing = YES; [ipc setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; [self presentViewController:ipc animated:YES completion:nil]; }else{//保存图片 UIImage *image = [self getImageFromView:self.editView]; //把图片保存到系统相册中selector不要瞎写 点到方法内部 告诉你怎么写了 UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); } } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ UIAlertView *alertView =[[UIAlertView alloc]initWithTitle:@"提示" message:@"图片保存完成" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alertView show]; } -(UIImage *)getImageFromView:(UIView *)view{ //创建一个画布 UIGraphicsBeginImageContext(view.frame.size); // 把view中的内容渲染到画布中 [view.layer renderInContext:UIGraphicsGetCurrentContext()]; // 把画布中的图片取出来 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); //结束渲染 UIGraphicsEndImageContext(); return image; } //点击图片 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ NSLog(@"%@",info); UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; UIImageView *iv = [[UIImageView alloc]initWithFrame:CGRectMake(self.selectedImages.count*80, 0, 80, 80)]; UIButton *deleteBtn = [[UIButton alloc]initWithFrame:CGRectMake(60, 0, 20, 20)]; [deleteBtn setTitle:@"X" forState:UIControlStateNormal]; [iv addSubview:deleteBtn]; iv.userInteractionEnabled = YES; [deleteBtn addTarget:self action:@selector(deleteImage:) forControlEvents:UIControlEventTouchUpInside]; iv.image = image; [self.selectedImageSV addSubview:iv]; [self.selectedImages addObject:iv]; [self.selectedImageSV setContentSize:CGSizeMake(self.selectedImages.count*80, 0)]; // [self dismissViewControllerAnimated:YES completion:nil]; } //删除按钮 -(void)deleteImage:(UIButton *)btn{ UIImageView *iv = (UIImageView *)btn.superview; [self.selectedImages removeObject:iv]; [iv removeFromSuperview]; for (int i=0;i<self.selectedImages.count;i++) { UIImageView *iv = self.selectedImages[i]; [UIView animateWithDuration:.5 animations:^{ iv.frame = CGRectMake(i*80, 0, 80, 80); }]; } } -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{ if (navigationController.viewControllers.count==2) { UIView *v = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height-100, 320, 100)]; v.backgroundColor = [UIColor redColor]; [viewController.view addSubview:v]; self.selectedImageSV = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 20, 320, 80)]; self.selectedImageSV.backgroundColor = [UIColor blueColor]; [v addSubview:self.selectedImageSV]; UIButton *doneBtn = [[UIButton alloc]initWithFrame:CGRectMake(260, 0, 60, 20)]; [doneBtn setTitle:@"Done" forState:UIControlStateNormal]; [doneBtn addTarget:self action:@selector(doneAction) forControlEvents:UIControlEventTouchUpInside]; [v addSubview:doneBtn]; } } -(void)doneAction{ [self dismissViewControllerAnimated:YES completion:nil]; for (UIImageView *iv in self.selectedImages) { UIButton *btn = [iv.subviews lastObject]; [btn removeFromSuperview]; [self.mySV addSubview:iv]; // 添加长按手势 UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longAction:)]; [iv addGestureRecognizer:longPress]; } self.mySV.contentSize = self.selectedImageSV.contentSize; } //长按将图片添加到view -(void)longAction:(UILongPressGestureRecognizer*)longPress{ UIImageView *tapIV = (UIImageView *)longPress.view; CGPoint p = [longPress locationInView:self.view]; switch ((int)longPress.state) { case UIGestureRecognizerStateBegan: { CGRect oldFrame = tapIV.frame; CGRect newFrame = [self.mySV convertRect:oldFrame toView:self.view]; self.dragIV = [[UIImageView alloc]initWithFrame:newFrame]; self.dragIV.image = tapIV.image; [self.view addSubview:self.dragIV]; } break; case UIGestureRecognizerStateChanged: self.dragIV.center = p; break; case UIGestureRecognizerStateEnded: if (CGRectContainsPoint(self.editView.frame, p)) { CGPoint oldCenter = self.dragIV.center; CGPoint newCenter = [self.view convertPoint:oldCenter toView:self.editView]; [self.editView addSubview:self.dragIV]; self.dragIV.center = newCenter; //添加各种手势 [self addGesture]; }else if (CGRectContainsPoint(self.bgIV.frame, p)){//背景区松手 self.bgIV.image = self.dragIV.image; //只有第一次往界面加背景的时候需要创建 添加到最下面 以后每次只需要修改即可 if (!self.backgroundIV) { self.backgroundIV = [[UIImageView alloc]initWithFrame:self.editView.bounds]; [self.editView insertSubview:self.backgroundIV atIndex:0]; } self.backgroundIV.image = self.dragIV.image; [self.dragIV removeFromSuperview]; }else{ [self.dragIV removeFromSuperview]; } break; } } //添加到view的图片手势 -(void)addGesture{ self.dragIV.userInteractionEnabled = YES; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)]; UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panAction:)]; UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotationAction:)]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchAction:)]; [self.dragIV addGestureRecognizer:tap]; [self.dragIV addGestureRecognizer:pan]; [self.dragIV addGestureRecognizer:rotation]; [self.dragIV addGestureRecognizer:pinch]; } -(void)tapAction:(UITapGestureRecognizer *)tap{ //把某个子视图带到前面来 [self.editView bringSubviewToFront:tap.view]; } -(void)panAction:(UIPanGestureRecognizer*)pan{ pan.view.center = [pan locationInView:self.editView]; } -(void)rotationAction:(UIRotationGestureRecognizer *)rotation{ [rotation.view setTransform:CGAffineTransformRotate(rotation.view.transform, rotation.rotation)]; rotation.rotation = 0; } -(void)pinchAction:(UIPinchGestureRecognizer *)pinch{ [pinch.view setTransform:CGAffineTransformScale(pinch.view.transform, pinch.scale, pinch.scale)]; pinch.scale = 1; } @end ? ? ? 2.音频 AVAudioPlayer 1.导入头文件 #import <AVFoundation/AVFoundation.h> 2.创建音频实例 AVAudioPlayer *player=[[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:@"/Users/tarena/Downloads/小苹果.mp3"] error:nil] 3.属性 [player play];播放 [player prepareToPlay]; //准备播放 是把数据加载到内存中 以便播放时能够及时播放 [self.player pause]; 暂停 player.volume = .2; 音量 player.currentTime 当前时间 player.currentTime = 50;从50秒开始唱 player.duration 总时间 知识点: 1.取出歌曲的名字 cell.textLabel.text = [[[musicPath lastPathComponent]componentsSeparatedByString:@"."]firstObject];省去.前面的(剪切) 2.实现界面结束,但歌曲不结束,将其AVAudioPlayer在AppDelegate中声明成属性,并在显示界面时用self.app.player获取 3.歌曲完成一首播放时,下一步该进行的事 a.将player设置为当前代理,遵守协议<AVAudioPlayerDelegate> 实现其方法即可 4.字符串分割 componentsSeparatedByString:@“” 字符串中的内容为剪切的起始点 5.字典排序 keys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { if ([obj1 intValue]<[obj2 intValue]) { return NSOrderedAscending; 升序 }else return NSOrderedDescending; 降序 }]; 6.cell中的文本的设置 cell.textLabel.font = [UIFont systemFontOfSize:13]; 字体大小 [cell.textLabel setTextAlignment:NSTextAlignmentCenter]; 居中 cell.selectedBackgroundView = [[UIView alloc]initWithFrame:cell.bounds]; 点击的背景 cell.selectedBackgroundView.backgroundColor = [UIColor clearColor]; cell.textLabel.highlightedTextColor = [UIColor redColor]; 文本颜色 7.UISegmentedControl 分割控件 .selectedSegmentIndex==0 根据此属性来确定点击的是哪个控件 例:day07 1.A 界面tableView显示音乐播放列表,歌曲名单 2.点击每行的时候,跳转到B界面,进入B 界面后就开始播放点击这行的歌曲 3.B界面有两个进度条,一个调节音量,一个显示歌曲进度 4.显示进度的还可以滑动调节歌曲的进度 5.两个label,一个显示歌曲总的秒数,另一个根据歌曲的进度显示秒数 6.一个button控制歌曲的播放和暂停 7.一首歌曲结束后自动播放下一曲 8.点击按钮,实现循环播放,随机播放和单曲循环 UISegmentedControl按钮,调去某个按钮 用.selectedSegmentIndex属性 9.添加图片和歌词 10.歌词以tableView的形式显示出来,从网上下载rlc形式的歌词,然后将歌词利用字符串的形式进行裁剪,并将裁剪的形式保存到数组和字典中,然后在显示的界面,将歌曲的当前的时间与字典中的存储的歌词对应起来 AppDelegate.h #import <UIKit/UIKit.h> #import <AVFoundation/AVFoundation.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (nonatomic, strong)AVAudioPlayer *player; MusicsTableViewController.h MusicsTableViewController.m #import "MusicsTableViewController.h" #import "ViewController.h" @interface MusicsTableViewController () @property (nonatomic, strong)NSMutableArray *musicPaths; @end @implementation MusicsTableViewController - (void)viewDidLoad { [super viewDidLoad]; //根据路径获取音乐 然后将路径都保存到数组中 self.musicPaths = [NSMutableArray array]; NSString *path = @"/Users/tarena6/Desktop/音乐"; NSFileManager *fm = [NSFileManager defaultManager]; NSArray *fileNames = [fm contentsOfDirectoryAtPath:path error:nil]; for (NSString *fileName in fileNames) { if ([fileName hasSuffix:@"mp3"]) { //获取完整的路径 NSString *filePath = [path stringByAppendingPathComponent:fileName]; [self.musicPaths addObject:filePath]; } } } #pragma mark - Table view data source //几行 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.musicPaths.count; } //每行 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; NSString *musicPath = self.musicPaths[indexPath.row]; cell.textLabel.text = [[[musicPath lastPathComponent]componentsSeparatedByString:@"."]firstObject]; return cell; } //点击每行的时候跳转到b界面,并记录每行的索引 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [self performSegueWithIdentifier:@"playvc" sender:@(indexPath.row)]; } //将数组和内行内容所对应的索引值传给b界面 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { ViewController *vc = segue.destinationViewController; vc.musicPaths = self.musicPaths; vc.currentIndex = [sender intValue]; } @end ViewController.h ViewController.m #import "Utils.h" #import "AppDelegate.h" #import "ViewController.h" #import <AVFoundation/AVFoundation.h> @interface ViewController ()<AVAudioPlayerDelegate,UITableViewDataSource,UITableViewDelegate> @property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel; @property (nonatomic, strong)NSArray *keys; @property (nonatomic, strong)NSDictionary *lrcDic; @property (weak, nonatomic) IBOutlet UISlider *progressSlider; @property (weak, nonatomic) IBOutlet UILabel *totalTimeLabel; @property (nonatomic, weak)AppDelegate *app; @property (weak, nonatomic) IBOutlet UISegmentedControl *playModeSC; @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (weak, nonatomic) IBOutlet UIImageView *artworkIV; @property (nonatomic, strong)NSTimer *timer; @end @implementation ViewController //根据滑动的slider显示总的时间,控制音量的大小 - (IBAction)sliderValueChange:(UISlider *)sender { if (sender.tag==0) { self.app.player.currentTime = sender.value; }else{ self.app.player.volume = sender.value; } } - (void)viewDidLoad { [super viewDidLoad]; self.app = [UIApplication sharedApplication].delegate; self.timer = [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(updateUI) userInfo:nil repeats:YES]; [self playMusic]; } -(void)playMusic{ if (self.currentIndex==-1) { self.currentIndex = self.musicPaths.count-1; } if (self.currentIndex==self.musicPaths.count) { self.currentIndex = 0; } self.musicPath = self.musicPaths[self.currentIndex]; self.artworkIV.image = [Utils getArtworkByPath:self.musicPath]; self.app.player = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:self.musicPath] error:nil]; self.app.player.delegate = self; self.title = [[[self.musicPath lastPathComponent]componentsSeparatedByString:@"."]firstObject]; //准备播放 是把数据加载到内存中 以便播放时能够及时播放 // [self.app.player prepareToPlay]; [self.app.player play]; self.totalTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d",(int)self.app.player.duration/60,(int)self.app.player.duration%60]; self.progressSlider.maximumValue = self.app.player.duration; //判断有没有歌词 NSString *lrcPath = [self.musicPath stringByReplacingOccurrencesOfString:@"mp3" withString:@"lrc"]; if ([[NSFileManager defaultManager]fileExistsAtPath:lrcPath]) { NSString *lrcString = [NSString stringWithContentsOfFile:lrcPath encoding:NSUTF8StringEncoding error:nil]; self.lrcDic = [Utils parseLrcWithString:lrcString]; self.keys = [self.lrcDic.allKeys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { if ([obj1 intValue]<[obj2 intValue]) { return NSOrderedAscending; }else return NSOrderedDescending; }]; [self.tableView reloadData]; } } //页面将要结束的时候,关闭时间计时器 -(void)viewWillDisappear:(BOOL)animated{ [self.timer invalidate]; self.app.player.delegate =nil; 页面结束的时候会奔 } //根据当前的时间来更新label显示的进度 -(void)updateUI{ self.progressSlider.value = self.app.player.currentTime; self.currentTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d",(int)self.app.player.currentTime/60,(int)self.app.player.currentTime%60]; //让歌词和歌曲同步 for (int i=0; i<self.keys.count; i++) { int time = [self.keys[i]intValue]; if (time>self.app.player.currentTime) { int line = i - 1; [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:line inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle]; break; } } } //点击按钮的时候,根据tag值来确定点击的是哪个按钮,选择上一曲、下一曲还是播放个暂停 - (IBAction)clicked:(UIButton *)sender { switch (sender.tag) { case 0: if (self.app.player.isPlaying) { [self.app.player pause]; [sender setTitle:@"播放" forState:UIControlStateNormal]; }else{ [self.app.player play]; [sender setTitle:@"暂停" forState:UIControlStateNormal]; } break; case 1: self.currentIndex--; [self playMusic]; break; case 2: self.currentIndex++; [self playMusic]; break; } } - (void)dealloc { NSLog(@"播放页面销毁了"); } //当一首歌播放完成的时候,根据点击的按钮来确定随机播放还是循环播放还是单曲循环 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ if (self.playModeSC.selectedSegmentIndex==0) { self.currentIndex++; }else if (self.playModeSC.selectedSegmentIndex ==2){ self.currentIndex = arc4random()%self.musicPaths.count; } [self playMusic]; } //tableView来显示歌词 #pragma mark UITableViewDelegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.lrcDic.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; NSNumber *key = self.keys[indexPath.row]; cell.textLabel.text = [self.lrcDic objectForKey:key]; cell.textLabel.font = [UIFont systemFontOfSize:13]; [cell.textLabel setTextAlignment:NSTextAlignmentCenter]; cell.selectedBackgroundView = [[UIView alloc]initWithFrame:cell.bounds]; cell.selectedBackgroundView.backgroundColor = [UIColor clearColor]; cell.textLabel.highlightedTextColor = [UIColor redColor]; return cell; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 15; } @end 用来显示图片和歌词的配置 Utils.h Utils.m (配置歌词) +(NSDictionary *)parseLrcWithString:(NSString *)lrcString{ NSMutableDictionary *lrcDic = [NSMutableDictionary dictionary]; NSArray *lines = [lrcString componentsSeparatedByString:@"\n"]; for (NSString *line in lines) { if ([line hasPrefix:@"[0"]) { NSArray *timeAndText = [line componentsSeparatedByString:@"]"]; NSString *timeString = [[timeAndText firstObject] substringFromIndex:1]; NSArray *times = [timeString componentsSeparatedByString:@":"]; float time = [times[0]intValue]*60+[times[1]intValue]; [lrcDic setObject:timeAndText[1] forKey:@(time)]; } } return lrcDic; } @end ? ? 3.音频(录音) #import "ViewController.h" #import <AVFoundation/AVFoundation.h> @interface ViewController () @property (nonatomic, strong)AVAudioPlayer *player; @property (nonatomic, strong)AVAudioRecorder *recorder; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSMutableDictionary *settings = [NSMutableDictionary dictionary]; // 录音格式 [settings setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; // 采样率 [settings setObject:@(11025) forKey:AVSampleRateKey]; // 通道数 [settings setObject:@(2) forKey:AVNumberOfChannelsKey]; // 音频质量 [settings setObject:@(AVAudioQualityMin) forKey:AVEncoderAudioQualityKey]; NSURL *url = [NSURL fileURLWithPath:@"/Users/tarena/Desktop/a.caf"]; self.recorder = [[AVAudioRecorder alloc]initWithURL:url settings:settings error:nil]; } //开始录音 - (IBAction)beginAction:(id)sender { [self.recorder record]; } //开始播放录音 - (IBAction)stopAction:(id)sender { [self.recorder stop]; self.player = [[AVAudioPlayer alloc]initWithContentsOfURL:self.recorder.url error:nil]; [self.player play]; } 封装成类 RecordButton.h #import <UIKit/UIKit.h> #import <AVFoundation/AVFoundation.h> @interface RecordButton : UIButton @property (nonatomic, strong)AVAudioPlayer *player; @property (nonatomic, strong)AVAudioRecorder *recorder; @end RecordButton.m #import "RecordButton.h" @implementation RecordButton - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self initRecord]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self initRecord]; } return self; } //-(void)awakeFromNib{ // [super awakeFromNib]; //} -(void)initRecord{ NSMutableDictionary *settings = [NSMutableDictionary dictionary]; // 录音格式 [settings setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey]; // 采样率 [settings setObject:@(11025) forKey:AVSampleRateKey]; // 通道数 [settings setObject:@(2) forKey:AVNumberOfChannelsKey]; // 音频质量 [settings setObject:@(AVAudioQualityMin) forKey:AVEncoderAudioQualityKey]; NSURL *url = [NSURL fileURLWithPath:@"/Users/tarena/Desktop/a.caf"]; self.recorder = [[AVAudioRecorder alloc]initWithURL:url settings:settings error:nil]; [self addTarget:self action:@selector(beginAction) forControlEvents:UIControlEventTouchDown]; [self addTarget:self action:@selector(stopAction) forControlEvents:UIControlEventTouchUpInside]; } -(void)beginAction{ [self.recorder record]; } -(void)stopAction{ [self.recorder stop]; // 播放功能 // self.player = [[AVAudioPlayer alloc]initWithContentsOfURL:self.recorder.url error:nil]; // [self.player play]; NSData *audioData = [NSData dataWithContentsOfFile:self.recorder.url.path]; NSString *audioString = [audioData base64EncodedStringWithOptions:0]; [[NSNotificationCenter defaultCenter]postNotificationName:@"recordfinish" object:nil userInfo:@{@"audioString":audioString}]; } @end 4.视频播放 MPMoviePlayerController 1.导入头文件 #import <MediaPlayer/MediaPlayer.h> 2.根据路径 创建实例,设置视屏屏幕view显示大小 ,然后加入view 3.设置控制样式 [self.player setControlStyle:MPMovieControlStyleNone]; 不可以修改 4.点击按钮截取图片 方法一:self.player thumbnailImageAtTime:方法 如下 方法二:设置监听截屏通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didReceiveImage:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:nil]; [self.player requestThumbnailImagesAtTimes:@[@(self.player.currentPlaybackTime)] timeOption:MPMovieTimeOptionExact]; 在点击按钮事件中添加 5.播放完以后视频从界面删除 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didfi:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; 例:截图,播放完删除功能 #import "ViewController.h" @interface ViewController () @property(nonatomic,strong)MPMoviePlayerController* player; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSURL*sur=[NSURL fileURLWithPath:@"/Users/tarena6/Desktop/day08/mtv.mp4"]; self.player=[[MPMoviePlayerController alloc]initWithContentURL:sur]; self.player.view.frame=CGRectMake(0, 0, 320, 200); [self.view addSubview:self.player.view]; [self.player setControlStyle:MPMovieControlStyleDefault]; [self.player play]; //监听截屏通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(startSing:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:nil]; //删除 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didfi:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; } -(void)didfi:(NSNotification*)ti{ [self.player.view removeFromSuperview]; } -(void )startSing:(NSNotification*)noti{ UIImage *image = [noti.userInfo objectForKey:MPMoviePlayerThumbnailImageKey]; UIImageView *iv = [[UIImageView alloc]initWithFrame:CGRectMake(0, 300, 320, 200)]; iv.image = image; [self.view addSubview:iv]; } - (IBAction)saveImage:(UIButton *)sender { /*方法一截图*/ // UIImage* iamge=[self.player thumbnailImageAtTime:self.player.currentPlaybackTime timeOption:MPMovieTimeOptionExact]; // UIImageView*imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 300, 320, 200)]; // imageView.image=iamge; // [self.view addSubview:imageView]; [self.player requestThumbnailImagesAtTimes:@[@(self.player.currentPlaybackTime)] timeOption:MPMovieTimeOptionExact]; } @end ? ================================================================================== 知识点 七、多线程 1.应用场景: 1.解决耗时操作导致界面阻塞问题 2.同时做几件事 3.多线程下载 2.切记!耗时操作不能放到主线程不然会导致界面阻塞 修改页面的代码不要放在子线程 3.三种开启线程的方式: 1.NSThread:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销 2.GCD:Grand Central Dispatch 中央任务分发 基于C语言 执行效率高 ,GCD是一个替代NSThread, NSOperationQueue,NSInvocationOperation等技术的很高效强大的技术。 3.NSOperation: 更灵活 可以设置线程和线程之间的关系 不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。 Cocoa operation相关的类是NSOperation, NSOperationQueue. NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子 类: NSInvocationOperation和NSBlockOperation. 4.线程队列: 1.串行队列:依次执行队列中的线程 2.并行队列:同时执行队列中的线程 1.NSThread开启线程方式 1.创建多线程 1. [NSThread detachNewThreadSelector:@selector(runAction) toTarget:self withObject:nil]; 2.初始化方法 ,但需要 [t start] 2.创建出更改的页面,但让当前线程睡眠状态 [NSThread sleepForTimeInterval:1]; 1为睡眠时间。即界面显示的时间间隔 3.然后把修改页面的代码回到主线程去执行 [self performSelectorOnMainThread:@selector(updateUI:) withObject:v waitUntilDone:NO]; 4.加载到界面显示 5. 若不显示的时候,需要刷新一下 [im setNeedsDisplay]; //刷新本控件显示的时候调用 [im setNeedsLayout];//刷新子控件显示的时候调用 如: #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (IBAction)clicked:(id)sender { [NSThread detachNewThreadSelector:@selector(runAction) toTarget:self withObject:nil]; } -(void)runAction{ for (int i=0; i<10; i++) { //让当前线程睡眠 [NSThread sleepForTimeInterval:1]; UIView *v = [[UIView alloc]initWithFrame:CGRectMake(100, i*50, 40, 40)]; v.backgroundColor = [UIColor redColor]; //把修改页面的代码回到主线程去执行 [self performSelectorOnMainThread:@selector(updateUI:) withObject:v waitUntilDone:NO]; } } -(void)updateUI:(UIView *)v{ [self.view addSubview:v]; [v setNeedsDisplay]; //刷新本控件显示的时候调用 [v setNeedsLayout];//刷新子控件显示的时候调用 } @end ? 利用多线程,在点击按钮后,红色的方框就会逐步挨个出现。不然会整体出现 例: 1.打地鼠,每1秒随机出现一个地鼠(按钮) 2.按钮上显示数据3-2-1-0,显示到0就消失 3.当点击了按钮,按钮也消失 4.用两个label来记录成功和失败的地鼠数(通知) ViewController.h ViewController.m #import "ViewController.h" #import "Mouse.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *failLabel; @property (weak, nonatomic) IBOutlet UILabel *successLabel; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(addMouse) toTarget:self withObject:nil]; //监听地鼠数量 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(statusChangeAction:) name:@"statusChange" object:nil]; } //记录地鼠数量 -(void)statusChangeAction:(NSNotification *)noti{ NSString *status = [noti.userInfo objectForKey:@"status"]; if ([status isEqualToString:@"success"]) { int count = self.successLabel.text.intValue; self.successLabel.text = [NSString stringWithFormat:@"%d",count+1]; }else{ int count = self.failLabel.text.intValue; self.failLabel.text = [NSString stringWithFormat:@"%d",count+1]; } } //子线程 -(void)addMouse{ while (YES) { [NSThread sleepForTimeInterval:1]; [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO]; } } //主线程 -(void)updateUI{ Mouse *m = [[Mouse alloc]initWithFrame:CGRectMake(arc4random()%300, arc4random()%548, 20, 20)]; [self.view addSubview:m]; } @end Mouse.h #import <UIKit/UIKit.h> @interface Mouse : UIButton @end Mouse.m #import "Mouse.h" @implementation Mouse - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor blueColor]; [self setTitle:@"3" forState:UIControlStateNormal]; [NSThread detachNewThreadSelector:@selector(countDownAction) toTarget:self withObject:nil]; [self addTarget: self action:@selector(clicked) forControlEvents:UIControlEventTouchUpInside]; } return self; } //点击按钮时 -(void)clicked{ [self removeFromSuperview]; [[NSNotificationCenter defaultCenter]postNotificationName:@"statusChange" object:nil userInfo:@{@"status": @"success"}]; } //倒计时3秒,自己消失 -(void)countDownAction{ for (int i=2; i>=0; i--) { [NSThread sleepForTimeInterval:.5]; if (!self.superview) { //如果不再父视图就返回 return; } [self performSelectorOnMainThread:@selector(updateUI:) withObject:@(i) waitUntilDone:NO]; } //因为删除老鼠的代码写在了子线程的最后面 执行完成之后 并没有耗时的代码 所以虽然是修改页面的 也能在子线程中执行 [self removeFromSuperview]; // 失败次数+1 [[NSNotificationCenter defaultCenter]postNotificationName:@"statusChange" object:nil userInfo:@{@"status": @"fail"}]; } -(void)updateUI:(NSNumber *)number{ [self setTitle:[NSString stringWithFormat:@"%@",number] forState:UIControlStateNormal]; } @end ? 2.GCD开启线程方式 1.创建线程队列 串行队列 线程1——0 线程1——1... 线程2——0... dispatch_queue_t myQueue1 = dispatch_queue_create("myQueue", NULL); 2. 获取系统提供的并行队列 (只有多个dispatch_async(myQueue2, ^{……}时)线程2——0 线程1——0 dispatch_queue_t myQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 3.执行某段代码 在某个队列中 dispatch_async(myQueue2, ^{ } 4.执行一段代码在主线程中 dispatch_async(dispatch_get_main_queue(), ^{ } 例:每隔1秒。每隔单元格加载出一张图片 #import "TableViewController.h" @interface TableViewController () @end @implementation TableViewController - (void)viewDidLoad { [super viewDidLoad]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 100; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; cell.imageView.image = nil;//避免单元格的重用 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ UIImage *image = [self downloadImage]; dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image =image; [cell setNeedsLayout];//刷新每行 }); }); return cell; } -(UIImage *)downloadImage{ [NSThread sleepForTimeInterval:1]; return [UIImage imageNamed:@"a.jpg"]; } @end ? 3.NSOperation开启线程方式 1.创建多线程 a.NSBlockOperation *op1 = [[NSBlockOperation blockOperationWithBlock:^{。。。。}]; b.NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(runAction) object:nil]; 注意:两个都需要开启[op1 start];但注意start并没有开启子线程,只有添加到子线程才会实行子线程加载 2.将多线程添加到子线程队列当中 NSOperationQueue *opQueue = [[NSOperationQueue alloc]init]; 1)//设置最大并行线程数量 [opQueue setMaxConcurrentOperationCount:1]; 1为串行 2)//添加线程之间的依赖关系 [op1 addDependency:op2]; 先显示op2,在显示op1 [opQueue addOperation:op1];普通的添加 例:每行的图片渐渐的淡出 #import "MyTableViewController.h" @interface MyTableViewController () @property(nonatomic,strong)NSOperationQueue*opQueue; @end @implementation MyTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.opQueue=[[NSOperationQueue alloc]init]; [self.opQueue setMaxConcurrentOperationCount:1]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 100; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; //cell.imageView.image=nil; cell.imageView.alpha=0; NSBlockOperation*op=[NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1]; UIImage*image=[UIImage imageNamed:@"Dandelion.tif"]; //主线程 dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image=image; [cell setNeedsLayout]; //淡化 [UIView animateWithDuration:1 animations:^{ cell.imageView.alpha=1; }]; }); }]; //添加到子线程 [self.opQueue addOperation:op]; return cell; } @end ? ============================================================================================== 知识点 八、线程同步 当多条线程同时操作同一份数据的时候可能会出现问题,需要用到线程同步来解决此问题 1.同步:串行 不同时 线程同步的方式有三种方式 : 1.同步代码块 2.NSLock 3.NSCondicion 2.异步:并行 同时 1. 同步代码块 @synchronized(self){。。。} 当有其A对象在执行,那么其他的对象需要等到A对象执行完毕才能进入执行 例:模拟卖票系统 #import "ViewController.h" @interface ViewController () @property (nonatomic)int totalCount; @property (nonatomic)int selledCount; //@property (nonatomic, strong)NSLock *myLock; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.totalCount = 100; // self.myLock = [[NSLock alloc]init]; NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(sellAction) object:nil]; t1.name = @"一号窗口"; [t1 start]; NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(sellAction) object:nil]; t2.name = @"二号窗口"; [t2 start]; } -(void)sellAction{ while (YES) { NSString *name = [NSThread currentThread].name; //参数需要一个id类型的对象 对象中有个锁旗标 @synchronized(self){ //[self.myLock lock]; NSLog(@"%@开始卖%d号票",name,self.selledCount+1); [NSThread sleepForTimeInterval:.5]; self.selledCount++; NSLog(@"%@卖了%d号票,还剩%d",name,self.selledCount,self.totalCount-self.selledCount); } // [self.myLock unlock]; } } @end 一号窗口开始卖1号票 一号窗口卖了1号票,还剩99 二号窗口开始卖2号票 二号窗口卖了2号票,还剩98 一号窗口开始卖3号票 一号窗口卖了3号票,还剩97 2.NSLock 1.创建实例 self.myLock = [[NSLock alloc]init]; 2.开锁 [self.myLock lock]; 3.解锁 [self.myLock unlock]; 3.NSCondicion 用法同上 4.线程之间的通信 就是在进行进程内的两个执行的河流之间进行的数据传递 performSelectOnTheThread方法 5.进程间的通信 (两个程序之间的通信) a)和自己的程序建立连接 在Supporting Files—>.plist—>右击创建Add Row—->倒数第二个—>下拉Item 0—>修改为URL-Schemes—>下拉Item 0—>最右侧双击写上名字myApp ? 要打开的程序设置 - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{ NSString *info = [[[url description]componentsSeparatedByString:@"//"]lastObject]; UILabel *l = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; l.backgroundColor = [UIColor redColor]; l.text = info; [self.window.rootViewController.view addSubview:l]; return YES; } 创建的新文件 ViewController.h ViewController.m - (IBAction)clicked:(UIButton *)sender { NSString *path = nil; switch (sender.tag) { case 0: path = @"tel://10010";//打电话 break; case 1: path = @"sms://10010";//发短信 break; case 2: path = @"mailto://[email protected]";//发邮件 break; case 3: path = @"http://www.youku.com";//浏览器 break; case 4: path = @"myApp://hehe";//打开自己的程序 break; } //将路径添加到app [[UIApplication sharedApplication]openURL:[NSURL URLWithString:path]]; } ? ============================================================================================================= 知识点 (day09) 九、网络 TCP:三次握手,建立链接,数据安全,相对于UDP传输效率较低 UDP:传输速度块,一对多广播,安全性较低 1.TCP 1.服务器端 127.0.0.1 回路ip a.导入框架,从网上 b.导入头文件 #import "AsyncSocket.h"AsyncSocket框架 c.创建实例,遵守协议,创建监听端口 d.实现协议,三步 day09 框架 AsyncSocket ViewController.h ViewController.m #import "ViewController.h" #import "AsyncSocket.h" @interface ViewController ()<AsyncSocketDelegate> @property (nonatomic, strong)AsyncSocket *serverSocket; @property (nonatomic, strong)AsyncSocket *myNewSocket; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.serverSocket = [[AsyncSocket alloc]initWithDelegate:self]; //监听端口 [self.serverSocket acceptOnPort:8000 error:nil]; } //监听到管道链接过来 但是并没连上呢 -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{ self.myNewSocket = newSocket; } //管道打通 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ NSLog(@"链接成功 对方ip:%@",host); //调用获取数据的方法 [self.myNewSocket readDataWithTimeout:-1 tag:0]; } //显示对方传过的数据 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ NSString *info = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"接收到:%@",info); //接着要数据 [sock readDataWithTimeout:-1 tag:0]; } @end 链接成功 对方ip:172.60.13.175 接收到:该吃放了! 2.客户端 a.导入文件,创建实例,遵守协议,创建与服务器的监听端口 b.实现协议,和服务器建立连接 c.向服务器发送消息 #import "ViewController.h" #import "AsyncSocket.h" @interface ViewController ()<AsyncSocketDelegate> - (IBAction)clicked:(UIButton *)sender; @property (nonatomic, strong)AsyncSocket *clientSocket; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.clientSocket = [[AsyncSocket alloc]initWithDelegate:self]; [self.clientSocket connectToHost:@"对方的ip地址" onPort:8000 error:nil]; } -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ NSLog(@"和%@建立链接",host); } - (IBAction)clicked:(UIButton *)sender { NSData *data = [@"该吃饭了!" dataUsingEncoding:NSUTF8StringEncoding]; [self.clientSocket writeData:data withTimeout:-1 tag:0]; } @end 和172.60.13.60建立链接 2.服务器和用户端在同一界面执行 #import "ViewController.h" #import "AsyncSocket.h" @interface ViewController ()<AsyncSocketDelegate> @property (nonatomic, strong)AsyncSocket *serverSocket; @property (nonatomic, strong)AsyncSocket *myNewSocket; @property (nonatomic, strong)AsyncSocket *clientScoket; @property (weak, nonatomic) IBOutlet UITextField *otherIP; @property (weak, nonatomic) IBOutlet UITextView *contentTV; @property (weak, nonatomic) IBOutlet UITextField *contentFV; @end @implementation ViewController - (IBAction)close:(UITextField *)sender { [sender resignFirstResponder]; } - (void)viewDidLoad { [super viewDidLoad]; self.serverSocket = [[AsyncSocket alloc]initWithDelegate:self]; //监听端口 [self.serverSocket acceptOnPort:8000 error:nil]; } //监听到管道链接过来 但是并没连上呢 -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{ self.myNewSocket = newSocket; } //管道打通 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ // NSLog(@"链接成功 对方ip:%@",host); self.otherIP.text=[NSString stringWithFormat:@"%@",host]; //调用获取数据的方法 [self.myNewSocket readDataWithTimeout:-1 tag:0]; } //我发送的数据 - (IBAction)clicked:(id)sender { self.clientScoket = [[AsyncSocket alloc]initWithDelegate:self]; NSString*str=self.otherIP.text; [self.clientScoket connectToHost:str onPort:8000 error:nil]; self.contentTV.text=[self.contentTV.text stringByAppendingFormat:@"\n我说:%@",self.contentFV.text]; NSString* str1=self.contentFV.text; NSData *data = [str1 dataUsingEncoding:NSUTF8StringEncoding]; [self.clientScoket writeData:data withTimeout:-1 tag:0]; } //接受的数据 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ NSString *info = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSString*str=[self.otherIP.text stringByAppendingFormat:@"说:%@",info]; self.contentTV.text=[self.contentTV.text stringByAppendingFormat:@"\n %@",str]; // NSLog(@"接收到:%@",info); //接着要数据 [sock readDataWithTimeout:-1 tag:0]; } @end ? ***3.传输文件和大型图片和视频 #import "ViewController.h" #import "AsyncSocket.h" @interface ViewController ()<AsyncSocketDelegate> @property (nonatomic, strong)AsyncSocket *serverSocket; @property (nonatomic, strong)AsyncSocket *myNewSocket; @property (nonatomic, strong)AsyncSocket *clientScoket; @property (nonatomic, strong)NSMutableData *allData; @property (nonatomic)int fileLength; @property (nonatomic, copy)NSString *fileName; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.allData = [NSMutableData data]; self.serverSocket = [[AsyncSocket alloc]initWithDelegate:self]; //监听端口 [self.serverSocket acceptOnPort:8000 error:nil]; self.clientScoket = [[AsyncSocket alloc]initWithDelegate:self]; // 127.0.0.1 回路ip [self.clientScoket connectToHost:@"127.0.0.1" onPort:8000 error:nil]; } //监听到管道链接过来 但是并没连上呢 -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{ self.myNewSocket = newSocket; } //管道打通 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ NSLog(@"链接成功 对方ip:%@",host); //调用获取数据的方法 [self.myNewSocket readDataWithTimeout:-1 tag:0]; } //接收数据 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ //取出头100个字节,转化为字符串 NSData *headerData = [data subdataWithRange:NSMakeRange(0, 100)]; NSString *headerString = [[NSString alloc]initWithData:headerData encoding:NSUTF8StringEncoding]; if (headerString&&[headerString componentsSeparatedByString:@"&&"].count==2) {//第一部分数据 NSArray *headers = [headerString componentsSeparatedByString:@"&&"]; self.fileName = [headers firstObject]; self.fileLength = [[headers lastObject] intValue]; self.allData = [NSMutableData data]; //取出抛去头剩下的文件数据 NSData *subFileData = [data subdataWithRange:NSMakeRange(100, data.length-100)]; [self.allData appendData:subFileData]; }else{//不是第一部分 接收到的data时文件data [self.allData appendData:data]; } //判断文件是否下载完成 if (self.allData.length == self.fileLength) { NSLog(@"文件传输完成"); NSString *newPath = [@"/Users/tarena6/Desktop" stringByAppendingPathComponent:self.fileName]; [self.allData writeToFile:newPath atomically:YES]; } //接着要数据 保证后面的数据接收到 [sock readDataWithTimeout:-1 tag:0]; } //发送 - (IBAction)clicked:(id)sender { NSString *imagePath = @"/Users/tarena6/Desktop/老师课件/day08/mtv.mp4"; NSData *fileData = [NSData dataWithContentsOfFile:imagePath]; NSLog(@"%@",fileData); // 1.准备头字符串 (文件名+文件长度) NSString *headerString = [NSString stringWithFormat:@"%@&&%d",[imagePath lastPathComponent],fileData.length]; // 2.把头字符串转成data NSData *headerData = [headerString dataUsingEncoding:NSUTF8StringEncoding]; // 3.把headerData 装进100个字节的data中 NSMutableData *sendAllData = [NSMutableData dataWithLength:100]; [sendAllData replaceBytesInRange:NSMakeRange(0, headerData.length) withBytes:headerData.bytes]; // 4.把文件数据 追加在后面 [sendAllData appendData:fileData]; [self.clientScoket writeData:sendAllData withTimeout:-1 tag:0]; } @end 例:day10FTP 服务器接收的步骤: 1.导入框架(网上) AsyncSocket.h 2.创建实例和监听接口,遵守协议 self.serverSocket = [[AsyncSocket alloc]initWithDelegate:self]; [self.serverSocket acceptOnPort:8000 error:nil]; 3.监听到管道链接过来 (但并没有连接上) -(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket{ self.myNewSocket = newSocket; } 4.打通管道,获得对方的IP //管道打通 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ NSLog(@"链接成功 对方ip:%@",host); //调用获取数据的方法 [self.myNewSocket readDataWithTimeout:-1 tag:0]; } 5.接受数据 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 1.先获取头数据 a.取出0-100字节的头字符串data 转换为字符串 b.判断是否是头字符串,是的话就通过&&拆分为几部分保存在数组中(名字+长度) c.提取出名字和长度 d.就剩下的100-data.length-100的data存放到data数组中 2.没有第一部分就直接接受data 3.继续接收数据 [sock readDataWithTimeout:-1 tag:0]; 3.接收完成后保存创建保存的路径 通过判断self.fileAllData.length == self.fileLength来确定是否接收完成 [self.fileAllData writeToFile:saveFilePath atomically:YES]; 客户端发送的步骤: 1.导入文件,创建实例,遵守协议,创建与服务器的监听端口, 输入服务器IP self.clientScoket = [[AsyncSocket alloc]initWithDelegate:self]; [self.clientScoket connectToHost:@"127.0.0.1" onPort:8000 error:nil]; 2.实现协议,和服务器建立连接 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ NSLog(@"和%@建立链接",host); } 3.向服务器发送消息 a.将要传送的路径转换成data b.取出头字符串(文件名+长度) c.把头字符串转成data d.新创建一个可变100个字节的data d.把头Data 装进100个字节的data中 e.把整个文件数据 追加在后面 4.发送数据 [self.clientScoket writeData:sendAllData withTimeout:-1 tag:0]; 新知识点: 1.设置进度条,来显示文件的下载速度 (文件数据长度/包括文件头的数据长度) self.myProgressView.progress = self.fileAllData.length*1.0/self.fileLength; 2.获取文件的长度,以字符串M和kb显示出来 NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:filePath]; int length = (int)[fh seekToEndOfFile]; NSString *size = @""; size = length>1024*1024?[NSString stringWithFormat:@"%dM",length/1024/1024] : [NSString stringWithFormat:@"%dKB",length/1024] 3.从客户端获取服务器中储存的数据 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 2.UDP: 框架 AsyncUdpSocket.h 0.创建实例 self.myUDPSocket = [[AsyncUdpSocket alloc]initWithDelegate:self]; 1.绑定端口 [self.myUDPSocket bindToPort:9000 error:nil]; 2.开启广播 [self.myUDPSocket enableBroadcast:YES error:nil]; 3.接收数据 [self.myUDPSocket receiveWithTimeout:-1 tag:0]; 4.接到对方传过来的数据 -(BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{ *** //继续读取数据 [self.myUDPSocket receiveWithTimeout:-1 tag:0]; 5.点击按钮发送消息 255.255.255.255 大家在一个网段就都可以收到 NSData *data = [@"大家好!" dataUsingEncoding:NSUTF8StringEncoding]; [self.myUDPSocket sendData:data toHost:@"255.255.255.255" port:9000 withTimeout:-1 tag:0]; 例:实现和大家传输,也可以针对性的传输 知识点: 1.查看数组中是否包含此数据 if (![self.hosts containsObject:host]) { [self.hosts addObject:host]; [self.tableView reloadData]; } 2.离开页面的时候 关闭socket 关闭后别人就不能给你发送了 -(void)viewDidDisappear:(BOOL)animated{ [self.myUDPSocket close]; } 3.判断不是以点开头的ip if (![host hasPrefix:@":"]) { 开始给host发送消息 ViewController.h ViewController.m #import "ViewController.h" #import "AsyncUdpSocket.h" @interface ViewController ()<UITableViewDataSource, UITableViewDelegate> @property (nonatomic, copy)NSString *toHost; @property (weak, nonatomic) IBOutlet UILabel *statusLabel; @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (weak, nonatomic) IBOutlet UITextView *historyTV; - (IBAction)valueChangeAction:(UISwitch *)sender; @property (weak, nonatomic) IBOutlet UITextField *sendInfoTF; @property (weak, nonatomic) IBOutlet UISwitch *mySwitch; @property (nonatomic, strong)AsyncUdpSocket *myUDPSocket; @property (nonatomic, strong)NSMutableArray *hosts; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.toHost = @"255.255.255.255"; self.hosts = [NSMutableArray array]; self.myUDPSocket = [[AsyncUdpSocket alloc]initWithDelegate:self]; // 绑定端口 [self.myUDPSocket bindToPort:9000 error:nil]; //开启广播 [self.myUDPSocket enableBroadcast:YES error:nil]; //接收数据 [self.myUDPSocket receiveWithTimeout:-1 tag:0]; [self checkingOnline]; [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(checkingOnline) userInfo:nil repeats:YES]; } -(void)checkingOnline{ NSData *data = [@"谁在线" dataUsingEncoding:NSUTF8StringEncoding]; [self.myUDPSocket sendData:data toHost:@"255.255.255.255" port:9000 withTimeout:-1 tag:0]; } - (IBAction)clicked:(id)sender { [self.sendInfoTF resignFirstResponder]; NSData *data = [self.sendInfoTF.text dataUsingEncoding:NSUTF8StringEncoding]; [self.myUDPSocket sendData:data toHost:self.toHost port:9000 withTimeout:-1 tag:0]; NSString *toName = self.toHost; if ([toName isEqualToString:@"255.255.255.255"]) { toName = @"所有人"; } self.historyTV.text = [self.historyTV.text stringByAppendingFormat:@"我对%@说:%@",toName,self.sendInfoTF.text]; } -(BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{ if (![host hasPrefix:@":"]) { NSString *info = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@说:%@",host,info); if ([info isEqualToString:@"谁在线"]) { NSData *d = [@"我在线" dataUsingEncoding:NSUTF8StringEncoding]; [self.myUDPSocket sendData:d toHost:host port:9000 withTimeout:-1 tag:0]; }else if ([info isEqualToString:@"我在线"]){ if (![self.hosts containsObject:host]) { [self.hosts addObject:host]; [self.tableView reloadData]; } }else{//接收到的是聊天内容 self.historyTV.text = [self.historyTV.text stringByAppendingFormat:@" %@说:%@",host,info]; } } //继续读取数据 [self.myUDPSocket receiveWithTimeout:-1 tag:0]; return YES; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.hosts.count; } //几行 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; NSString *host = self.hosts[indexPath.row]; cell.textLabel.text = [[host componentsSeparatedByString:@"."]lastObject]; return cell; } //点击每行时针对个人ip -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ self.toHost = self.hosts[indexPath.row]; self.statusLabel.text = [NSString stringWithFormat:@"对%@说:",self.toHost]; [self.mySwitch setOn:NO animated:YES]; } - (IBAction)valueChangeAction:(UISwitch *)sender { if (sender.isOn) { self.statusLabel.text = @"对所有人说:"; }else{//开着变关 if ([self.toHost isEqualToString:@"255.255.255.255"]) { [sender setOn:YES animated:YES]; return; } self.statusLabel.text = [NSString stringWithFormat:@"对%@说:",self.toHost]; } } @end ? ? 例:通过UDF 服务器与客户端建立链接,然后在通过FTP双方互相通信,实现点击屏幕打地鼠的游戏 day11 ========================================================================= 知识点 十、 NSJSONSerialization 数据接口(JSON) 1.将此种类型转换后显示{ 聚合数据 "resultcode": "200", "reason": "success", "result": [{ "rid": "1", "name": "灰姑娘", "wk": "2015.3.16 – 2015.3.22(单位:万元)", "wboxoffice": "¥18000", "tboxoffice": "¥34200" }, 1.得到要解的路径,以data的形式保存起来 2.存放到字典中,在将字典存放到数组中 3.开始遍历数组,取出字典中存放的数据,分别赋给创建好接收的类的属性 4.再将类的属性以tableView的形式显示出来 NSString *path = [[NSBundle mainBundle]pathForResource:@"hongshaorou" ofType:@"txt"]; NSData *jsonData = [NSData dataWithContentsOfFile:path]; NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; self.menus = [JsonParser parseMenusByDic:dic];//将值赋给JsonParser类解析 经典例子解析: ? JsonParser.h #import <Foundation/Foundation.h> @interface JsonParser : NSObject +(NSMutableArray *)parseMenusByDic:(NSDictionary *)dic; @end JsonParser.m 解析类 #import "JsonParser.h" #import "Menu.h" #import "Step.h" @implementation JsonParser +(NSMutableArray *)parseMenusByDic:(NSDictionary *)dic{ NSDictionary *resultDic = [dic objectForKey:@"result"]; NSArray *dataArr = [resultDic objectForKey:@"data"]; NSMutableArray *menus = [NSMutableArray array]; for (NSDictionary *menuDic in dataArr) { Menu *m = [[Menu alloc]init]; m.title = [menuDic objectForKey:@"title"]; m.menuID = [menuDic objectForKey:@"id"]; m.intro = [menuDic objectForKey:@"imtro"]; m.album = [[menuDic objectForKey:@"albums"] firstObject]; NSArray *stepsArr = [menuDic objectForKey:@"steps"]; for (NSDictionary *stepDic in stepsArr) { Step *s = [[Step alloc]init]; s.img = [stepDic objectForKey:@"img"]; s.step = [stepDic objectForKey:@"step"]; [m.steps addObject:s]; } [menus addObject:m]; } return menus; } @end WebUtils.h typedef void (^Callback)(id obj); 传值 #import <Foundation/Foundation.h> @interface WebUtils : NSObject +(void)menusByName:(NSString*)name andCallback:(Callback)callback; @end WebUtils.m 网络请求类 #import "WebUtils.h" #import "JsonParser.h" @implementation WebUtils +(void)menusByName:(NSString*)name andCallback:(Callback)callback{ NSString*path=@"http://apis.juhe.cn/cook/query"; NSURL* url=[NSURL URLWithString:path]; NSMutableURLRequest*request=[NSMutableURLRequest requestWithURL:url]; NSString*params=[NSString stringWithFormat:@"key=80996127f667eac43832103850b3b13a&menu=%@",name]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSMutableArray*menus=[JsonParser parseMenusByDic:dic]; callback(menus); }]; } @end Menu.h #import <Foundation/Foundation.h> @interface Menu : NSObject @property (nonatomic, copy)NSString *title; @property (nonatomic, copy)NSString *menuID; @property (nonatomic, copy)NSString *intro; @property (nonatomic, strong)NSMutableArray *steps; @property (nonatomic, copy)NSString *album; @end Menu.m 菜单 #import "Menu.h" @implementation Menu - (instancetype)init { self = [super init]; if (self) { self.steps = [NSMutableArray array]; } return self; } @end Step.h 步骤 #import <Foundation/Foundation.h> @interface Step : NSObject @property (nonatomic, copy)NSString *img; @property (nonatomic, copy)NSString *step; @end Step.m TableViewController.h TableViewController.m 显示菜单界面 #import "MenuTableViewCell.h" #import "TableViewController.h" #import "JsonParser.h" #import "Menu.h" #import "Step.h" #import "WebUtils.h" @interface TableViewController () @property (nonatomic, strong)NSMutableArray *menus; @end @implementation TableViewController - (void)viewDidLoad { [super viewDidLoad]; [WebUtils menusByName:self.str andCallback:^(id obj) { self.menus=obj; [self.tableView reloadData]; }]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.menus.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { MenuTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; cell.m = self.menus[indexPath.row]; return cell; } MenuTableViewCell.h MenuTableViewCell.m 自定义单元格 #import "MenuTableViewCell.h" @implementation MenuTableViewCell -(void)layoutSubviews{ [super layoutSubviews]; //更新页面显示 self.titleLabel.text = self.m.title; self.menuIDLabel.text = self.m.menuID; self.introTV.text = self.m.intro; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.m.album]]; dispatch_async(dispatch_get_main_queue(), ^{ self.albumIV.image = [UIImage imageWithData:imageData]; }); }); } @end SeekViewController.h SeekViewController.m 搜索界面 #import "SeekViewController.h" #import "TableViewController.h" @interface SeekViewController () - (IBAction)beganSeek:(UIButton *)sender; @property (weak, nonatomic) IBOutlet UITextField *textField; @end @implementation SeekViewController - (void)viewDidLoad { [super viewDidLoad]; } - (IBAction)beganSeek:(UIButton *)sender { [self.textField resignFirstResponder]; } -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ TableViewController*vc=segue.destinationViewController; vc.str=self.textField.text; } @end ? ?? 注意:显示照片:不能直接赋值 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:self.m.album]]; dispatch_async(dispatch_get_main_queue(), ^{ self.albumIV.image = [UIImage imageWithData:imageData]; }); }); =========================================================================== 知识点 十一、HTTP(GET /POST) NSURLConnection NSURLSession Http:基于连接协议 短链接(请求响应式),开发效率高,容错性强 ,传输效率低(每次数据传输都需要带头数据) Socket:对程序员要求高,开发效率低 ,长连接,传输效率高 (聊天) 1. Http: 网络接口、网络API、webService、网络服务、web接口、webAPI(服务器提供的接口) ***从接口文档当中需要得到信息:请求地址、请求参数、请求类型( GET/POST)、返回数据类型 支持格式(XML/JSON)、返回数据具体字段类型 2. HTTP请求方式: GET: 把请求参数放到请求地址后面用?连接, 应用场景: 一般是从服务器获取数据时用到 POST:把请求参数放到请求体里面, 数据安全,可以传递大的数据的参数, 应用场景 :需要把数据保存到服务器的时候 ? ? 3.HTTP发送请求的方式 与NSURLConnection相比,NSURLSession最直接的改善就是提供了配置每个会话的缓存,协议,cookie和证书政策(credential policies),甚至跨应用程序共享它们的能力。 1)NSURLConnection 2)NSURLSession 3)第三方框架 AFNetworking 1. 步骤:NSURLConnection 获取网络数据 1.使用GET请求方式+同步请求 1.请求地址 (GET请求方式是把请求参数放在路径的后面) 字典形式存在 ,中间加上& NSString *path = @"http://apis.juhe.cn/cook/query?key=80996127f667eac43832103850b3b13a&menu=红烧肉"; 2.请求地址中如果出现中文 需要进行URL编码 path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:path]; 3.创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url] 4.发出请求得到返回数据 NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];//发出同步请求得到返回数据 NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 2.使用POST请求方式+异步请求 NSString *path = @"http://apis.juhe.cn/cook/query"; NSURL *url = [NSURL URLWithString:path]; //创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSString *params = [NSString stringWithFormat:@"key=80996127f667eac43832103850b3b13a&menu=%@",self.menuName];//参数 //如果使用POST请求方式 需要做两件事: //1.设置请求方式 [request setHTTPMethod:@"POST"]; //2.把参数设置到请求体里面 [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; //发出异步请求得到返回数据 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; self.menus = [JsonParser parseMenusByDic:dic]; [self.tableView reloadData]; }]; 新知识点,将网络传输封装到一个类,block反向传值 WebUtils.h typedef void (^Callback)(id obj);//定义类型 #import <Foundation/Foundation.h> @interface WebUtils : NSObject +(void)requestMenusByName:(NSString *)name andCallback:(Callback)callback; @end WebUtils.m #import "WebUtils.h" #import "JsonParser.h" @implementation WebUtils +(void)requestMenusByName:(NSString *)name andCallback:(Callback)callback{ NSString *path = @"http://apis.juhe.cn/cook/query"; NSURL *url = [NSURL URLWithString:path]; //创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSString *params = [NSString stringWithFormat:@"key=80996127f667eac43832103850b3b13a&menu=%@",name]; //如果使用POST请求方式 需要做两件事: // 1.设置请求方式 [request setHTTPMethod:@"POST"]; // 2.把参数设置到请求体里面 [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSMutableArray *menus = [JsonParser parseMenusByDic:dic]; callback(menus);//返回 }]; } @end TableViewController.h TableViewController.m 调用的时候 [WebUtils requestMenusByName:self.menuName andCallback:^(id obj) { self.menus = obj; [self.tableView reloadData]; }]; 2.NSURLSession 获取解析数据 //解析明星 +(void)requestStarInfoWithStarName:(NSString *)name andCallback:(MyCallback)callback{ NSString *path = [NSString stringWithFormat:@"http://web.juhe.cn:8080/video/p?appkey=f0e620e13cb28728e6a5f4cc5e0e9dfb&v=1.0&pname=com.tarena&keyword=%@",name]; path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:path]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //创建Session 会话 NSURLSession *session = [NSURLSession sharedSession]; // 分为三种请求:1.上传请求 2.下载请求 3.数据请求 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; Star *s = [JsonParser parseStarByDic:dic]; callback(s); }]; //***获取数据 [task resume]; } 3.通过网址直接观看视频 UIWebView UIWebView *wv = [[UIWebView alloc]initWithFrame:self.view.bounds]; [self.view addSubview:wv]; //obj是网址 NSURL *url = [NSURL URLWithString:obj]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [wv loadRequest:request]; day12通过搜索明星和电影名字,可以查看明星资料和电影资料,并且可以播放电影 1.用到了通过三种接口 解析数据 (即原始路径不同) ? 界面 ? ? ? ? ? 4.NSURLSession的下载请求 1.导入下载路径,保存成NSURL 发送请求 NSString *path = @"http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/238979467/124380645248400128.mp3?xcode=5bc281221e17f22342438bc98f009a4aa957e289de92d109&song_id=124380645"; NSURL *url = [NSURL URLWithString:path]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url] 1).下载任务 直接直接下载完成时用此方法 NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { NSLog(@"%@",location); [task resume]; 2).有下载过程的下载任务 不支持后台defaultSessionConfiguration 可以实现后台下载 NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"abcd"]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]]; NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request]; [task resume]; } 2.可以查看下载进度 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ NSLog(@"%lld %lld %lld",bytesWritten,totalBytesWritten,totalBytesExpectedToWrite); //每次下载任务/总的下载任务 进度条 self.myPV.progress = totalBytesWritten*1.0/totalBytesExpectedToWrite; } 3.将下载下来默认的保存路径更改 -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSLog(@"下载完成"); NSString *path = @"/Users/tarena/Desktop/匆匆那年.mp3"; NSURL *newURL = [NSURL fileURLWithPath:path]; [[NSFileManager defaultManager]moveItemAtURL:location toURL:newURL error:nil]; } ***5.HTTP 发送请求通过第三方框架 AFNetworking UIKit+AFNetworking 1.照片的路径可以不用解析直接利用框架扩展的方法直接调用 #import "UIImageView+AFNetworking.h" UIImageView *iv = [[UIImageView alloc]initWithFrame:self.view.bounds]; [self.view addSubview:iv]; NSString *imgPath = @"http://g.hiphotos.baidu.com/image/pic/item/d000baa1cd11728be1f6a446cbfcc3cec2fd2cfe.jpg"; [iv setImageWithURL:[NSURL URLWithString:imgPath]]; 2.HTTP 请求 网络数据 1)通过AFHTTPRequestOperationManager发出请求 NSString *path = @"http://web.juhe.cn:8080/video/p"; NSDictionary *params = @{@"appkey": @"f0e620e13cb28728e6a5f4cc5e0e9dfb",@"v":@"1.0",@"pname":@"com.tarena",@"keyword":@"刘德华"}; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; //设置响应数据序列化 [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]]; //GET POST请求方式根据需要定 [manager GET:path parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil]; NSLog(@"%@",dic); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"请求失败"); }]; 2)通过AFHTTPRequestOperation发出请求 NSString *path = @"http://api.dianping.com/v1/business/find_businesses"; NSDictionary *params = @{@"city": @"北京"}; path = [DianpingApi serializeURL:path params:params];//DianpingApi? NSLog(@"%@",path); NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]]; AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc]initWithRequest:request]; [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil]; NSLog(@"%@",dic); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"请求失败"); }]; [op start]; ======================================================================================================= 知识点 十二、美团网站APP 例:day14页面上方加的搜索框 例:day15通过首字母分类(plist文件) ? ? ? ========================================================== 知识点 十三、地图 MKMapView 类似于cell的一些特性,比如点击事件和滚动地图都是可以选择时机方法的 1.注意:使用地图要在项目中增加 ? 2.右侧故事板中还可以修改地图的样式 Hybrid 有建筑物标志 Satellite 卫星地图 Map 街道形式 3.导入头文件 #import <MapKit/MapKit.h> 4. //删除之前的位置 [self.mapView removeAnnotations:self.mapView.annotations]; //当前地图中心经纬度 CLLocationCoordinate2D coord = mapView.centerCoordinate; 5.获得地图的位置,将点击的坐标点转换成经纬度 CLLocationCoordinate2D coord = [self.mapView convertPoint:point toCoordinateFromView:self.view]; 6.自定义大头针 1.继承自 MKAnnotationView 2.初始化方法,添加大头针的照片 -(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{ if ([super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) { UIImage *image = [UIImage imageNamed:@"001.png"]; self.image = image; } return self; } 3.控制器调用的时候 设置代理 ,在故事版中要连接到当前控制器的代理 @interface ViewController ()<MKMapViewDelegate> 4.实现方法 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{ static NSString *identifer = @"annotationView"; MyAnnotationView *annView = (MyAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifer]; if (!annView) { annView = [[MyAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:identifer]; } return annView; } 7.移动到哪就在哪显示大头针 - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{ [WeiboApi requestWeibosWithCoord:mapView.centerCoordinate andCallBack:^(id obj) { [self loadAnnotationsWithWeibos:obj]; 创建大头针的方法 }]; } 例: MyAnnotation.h 点击即添加大头针 ,继承自NSObject #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface MyAnnotation : NSObject<MKAnnotation> 增加协议,点进协议,复制方法 @property (nonatomic) CLLocationCoordinate2D coordinate; // Title and subtitle for use by selection UI. @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *subtitle; // Called as a result of dragging an annotation view. - (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate; @end MyAnnotation.m #import "MyAnnotation.h" @implementation MyAnnotation @end ViewController.h ViewController.m #import "ViewController.h" #import <MapKit/MapKit.h> #import "MyAnnotation.h" #import "MyAnnotationView.h" @interface ViewController ()<MKMapViewDelegate> @property (weak, nonatomic) IBOutlet MKMapView *mapView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 北纬:39.90960456049752 // 东经:116.3972282409668 CLLocationCoordinate2D coord; coord.longitude = 116.3972282409668; coord.latitude = 39.90960456049752; //指定显示的地图 [self.mapView setRegion:MKCoordinateRegionMake(coord, MKCoordinateSpanMake(0.001, 0.001))]; } //点击到达的地图位置 - (IBAction)tapAction:(UITapGestureRecognizer *)sender { CGPoint point = [sender locationInView:self.view]; //获得地图的位置,将点击的坐标点转换成经纬度 CLLocationCoordinate2D coord = [self.mapView convertPoint:point toCoordinateFromView:self.view]; //地图显示范围 0.1代表范围大 [self.mapView setRegion:MKCoordinateRegionMake(coord, MKCoordinateSpanMake(0.001, 0.001)) animated:YES]; //添加大头针 MyAnnotation *ann = [[MyAnnotation alloc]init]; ann.coordinate = coord; ann.title = @"这是标题"; ann.subtitle = @"这是详情"; [self.mapView addAnnotation:ann]; } //自定义大头针 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{ static NSString *identifer = @"annotationView"; MyAnnotationView *annView = (MyAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifer]; if (!annView) { annView = [[MyAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:identifer]; } return annView; } @end //自定义大头针 MyAnnotationView.h #import <MapKit/MapKit.h> @interface MyAnnotationView : MKAnnotationView @end MyAnnotationView.m #import "MyAnnotationView.h" @implementation MyAnnotationView -(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{ if ([super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) { UIImage *image = [UIImage imageNamed:@"001.png"]; self.image = image; } return self; } @end ? ? =============================================================================== 知识点 十二、UIWebView 1.直接通过故事板创建web,和控制器连线设置代理 2.图中四个按钮,无需写代码,只需要右击web,直接与按钮连线即可 ? ? #import "ViewController.h" @interface ViewController ()<UIWebViewDelegate> @property (weak, nonatomic) IBOutlet UITextField *myTF; @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *juhua; @property (weak, nonatomic) IBOutlet UIWebView *webView; - (IBAction)Go:(UIButton*)sender; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } //文本框输入网址,点击go按钮跳转到此页面 - (IBAction)Go:(id)sender { NSURL *url = [NSURL URLWithString:self.myTF.text]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [self.webView loadRequest:request]; } //页面开始加载的时候,路径中如果不包含baidu,就不跳转 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSString *urlPath = [request.URL description]; if ([urlPath rangeOfString:@"baidu"].length>0) {//路径中是否包含baidu return YES; } return NO; } //页面加载的时候,进度圈加载 -(void)webViewDidStartLoad:(UIWebView *)webView{ [self.juhua startAnimating]; } //页面加载完成的时候,进度圈停止加载 -(void)webViewDidFinishLoad:(UIWebView *)webView{ [self.juhua stopAnimating]; } @end ========================================================================================================================= 知识点 十四、微博项目 day17 1.注册微博开发者账号,获取appKey即 client_id 2.用代码加载登陆页面 (设置delegate,故事板需要连线) NSString *appKey = @"1629509029"; //重定向地址 NSString *uri = @"https://api.weibo.com/oauth2/default.html"; //加载登录页面 NSString *path = [NSString stringWithFormat:@"https://api.weibo.com/oauth2/authorize?client_id=%@&redirect_uri=%@",appKey,uri]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]]; [self.webView loadRequest:request]; ? ? 3.通过代码发送微博 4.网络请求例子 (照片和文本) //发送照片和文本 +(void)sendWeiboWithText:(NSString *)text andImageData:(NSData *)imageData andCallback:(MyCallback)callback{ NSString *path = @"https://api.weibo.com/2/statuses/upload.json"; NSDictionary *params = @{@"access_token": [WeiboApi getToken],@"status":text}; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]]; [manager POST:path parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { //发送照片 [formData appendPartWithFileData:imageData name:@"pic" fileName:@"abc.jpg" mimeType:@"image/jpg"]; } success:^(AFHTTPRequestOperation *operation, id responseObject) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil]; callback(dic); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"发送带图失败"); }]; } 5.自定义cell xib创建 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *identifier = @"Cell"; WeiboCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[[NSBundle mainBundle]loadNibNamed:@"WeiboCell" owner:self options:nil]firstObject]; } cell.weibo = self.weibos[indexPath.row]; weibo是自定义类,记录属性的 return cell; } 6.*****侧滑栏 1.框架 ICSDrawerController 2.借助TabBarViewController来实现多个界面的侧滑 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { MenuTableViewController *menuvc = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"menuvc"]; self.mainvc = (MainTabbarController*)self.window.rootViewController; //通过框架来实现, self.drawerController = [[ICSDrawerController alloc]initWithLeftViewController:menuvc centerViewController:self.mainvc]; //跟视图控制器 self.window.rootViewController = self.drawerController; return YES; } ? ***7.创建控件的时候需要调用的方法 //通过纯代码创建自定义的控件的时候调用此方法 -(id)initWithFrame:(CGRect)frame //通过storyboard或xib创建控件时调用此方法 此时子控件没有初始化 为nil -(id)initWithCoder:(NSCoder *)aDecoder //通过storyboard或xib创建控件时调用此方法 此时子控件有值 -(void)awakeFromNib ***8.根据微博文本的高度确定显示的cell大小 自定义微博 Weibo.h Weibo.m #import "Weibo.h" @implementation Weibo -(float)getWeiboHeight{ // 1.拿到文本 计算文本所占高度 // 2.如果有图片加上图片高度 // 3.判断是否有转发 如果有转发 // 转发微博对象也有getWeiboHeight方法 float height = 0; //计算微博文本的高度 CGRect frame = [self.text boundingRectWithSize:CGSizeMake(300, 999) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil]; //加上文本高度 height += frame.size.height; //加上微博图片的高度----------------------- if (self.thumbnailImage != nil && ![@"" isEqualToString:self.thumbnailImage]) { height += 110; } //加上转发的微博的高度 if (self.relWeibo) { //转发微博视图的高度 float repostHeight = [self.relWeibo getWeiboHeight]; height += repostHeight; } if (_isRepost == YES) {//如果有转发 多加20个像素 为了美观 height += 20; } return height; } @end 每条微博高度 weiboView.h #import <UIKit/UIKit.h> #import "Weibo.h" @interface weiboView : UIView @property (nonatomic, strong)Weibo *weibo; @property (nonatomic, strong)UITextView *textView; @property (nonatomic, strong)UIImageView *imageView; //转发微博 @property(nonatomic,strong)weiboView*relWeiboView; @end weiboView.m #import "weiboView.h" #import "UIImageView+AFNetworking.h" @implementation weiboView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { //此位置初始化用到的两个控件 [self initUI]; } return self; } -(void)initUI{ //添加文本控件 self.textView = [[UITextView alloc]initWithFrame:CGRectZero]; self.textView.userInteractionEnabled = NO; self.textView.font = [UIFont systemFontOfSize:14]; [self addSubview:self.textView]; self.textView.backgroundColor = [UIColor clearColor]; // 添加图片控件 self.imageView = [[UIImageView alloc]initWithFrame:CGRectZero]; [self.imageView setContentMode:UIViewContentModeScaleAspectFit]; [self addSubview:self.imageView]; } -(void)layoutSubviews{ [super layoutSubviews]; //此位置写显示内容和更改尺寸的代码 float width = 300; //计算文本高度 CGRect frame = [self.weibo.text boundingRectWithSize:CGSizeMake(width, 999) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil]; self.textView.frame = CGRectMake(5, 0, width, frame.size.height+10); // 设置显示的文本 self.textView.text = self.weibo.text; //如果存在图片 if (self.weibo.thumbnailImage && ![self.weibo.thumbnailImage isEqualToString:@""]) { // 因为不是每一条微博都有图片 weiboView又是不停在复用 self.imageView.hidden = NO; self.imageView.frame = CGRectMake(5,self.textView.frame.size.height+10, width, 100); [self.imageView setImageWithURL:[NSURL URLWithString:self.weibo.thumbnailImage]]; }else{//如果没有图片把图片隐藏 self.imageView.hidden = YES; } //**************添加转发************************** if (self.weibo.relWeibo) { //第一次需要创建 之后直接复用 if (!self.relWeiboView) { self.relWeiboView = [[weiboView alloc]initWithFrame:CGRectZero]; [self.relWeiboView setBackgroundColor:[UIColor colorWithRed:223/255.0 green:223/255.0 blue:223/255.0 alpha:1]]; [self addSubview:self.relWeiboView]; } // 复用的时候如果没有转发需要隐藏 有则显示出来 self.relWeiboView.hidden = NO; self.relWeiboView.frame = CGRectMake(5, self.textView.frame.size.height+5, width, [self.relWeiboView.weibo getWeiboHeight]); self.relWeiboView.weibo = self.weibo.relWeibo; }else{//如果没有转发就隐藏 self.relWeiboView.hidden = YES; } //******************************************** } @end 自定义cell WeiboCell.h @property(nonatomic,strong)Weibo * weibo; @property(nonatomic,strong)weiboView*weiboView; WeiboCell.m -(void)awakeFromNib{ [super awakeFromNib]; self.weiboView = [[weiboView alloc]initWithFrame:CGRectZero]; [self addSubview:self.weiboView]; } //千万不能在此方法中创建控件 -(void)layoutSubviews{ [super layoutSubviews]; //更新每一条微博的高度 self.weiboView.frame = CGRectMake(5, 45, 300, [self.weibo getWeiboHeight]); self.weiboView.weibo = self.weibo; } 显示控制器 SelfMessageViewController.h SelfMessageViewController.m -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ Weibo *weibo = self.showSendweibos[indexPath.row]; return [weibo getWeiboHeight]+110; //显示行高 } 9.从下面的toolbar弹出键盘,并收起 记得要遵守协议<UIAlertViewDelegate,UITextFieldDelegate> -(void)initToolbar{ self.toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height-45, 320, 45)]; self.sendInfoTF = [[UITextField alloc]initWithFrame:CGRectMake(10, 5, 260, 35)]; [self.sendInfoTF setPlaceholder:@"写评论"]; [self.sendInfoTF setBorderStyle:UITextBorderStyleBezel]; UIBarButtonItem *bbi = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"03话题详情_11.png"] style:UIBarButtonItemStyleBordered target:self action:@selector(sendAction)]; UIBarButtonItem *sendInfoBBI = [[UIBarButtonItem alloc]initWithCustomView:self.sendInfoTF]; self.toolbar.items = @[sendInfoBBI,bbi]; [self.navigationController.view addSubview:self.toolbar]; //监听软键盘 事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(WillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; self.sendInfoTF.delegate = self; } //离开页面的时候把toolbar删除 -(void)viewWillDisappear:(BOOL)animated{ [self.toolbar removeFromSuperview]; } //控制toolbar位置 #pragma mark - keyboard reference -(void)WillChangeFrame:(NSNotification *)notif{ NSDictionary *info = [notif userInfo]; NSValue *value = [info objectForKey:UIKeyboardFrameEndUserInfoKey]; CGRect keyboardFrame = [value CGRectValue]; [UIView animateWithDuration:.2 animations:^{ CGRect frame = self.toolbar.frame; frame.origin.y = keyboardFrame.origin.y-45; self.toolbar.frame = frame; }]; } //发送按钮 -(void)sendAction{ [self.sendInfoTF resignFirstResponder]; //清空文本输入框 self.sendInfoTF.text = @""; } ===================================================================================================================== 知识点 十五、XMPP 10.xmpp:基于xml的即时通讯协议 (主要用于聊天) 通过一个框架进行操作 0.找到第三方框架 iphone XMPP 1.把第三方框架中的一个.h文件和六个文件夹拖到自己的工程中 2.此时编译会出现1个错,百度解决 Build Settings—>Search Paths—>Header Search Paths—>点开后增加一行/usr/include/libxml2 ? 3.把xmpp项目中用到的5个黄色工具箱和三个白色的文件拖到自己的工程中 (拖拽时一定不要勾选copy,删除的时候选择第二个Remove Reference,删的只是引用) 知识点: 1.base64编码 把data转成string //将照片转换为data NSData *imageData = nil; if ([imageURL hasSuffix:@"JPG"]) { imageData = UIImageJPEGRepresentation(image, 1); }else{//png imageData = UIImagePNGRepresentation(image); } NSString *imageString = [imageData base64EncodedStringWithOptions:0]; 2. base64编码 把string转成data NSData *imageData = [[NSData alloc]initWithBase64EncodedString:imageString options:0]; day21XMLL (放送文本,图片,音频) //封装XMLL类 XMPPManager.h #import <Foundation/Foundation.h> #import "XMPPFramework.h" @interface XMPPManager : NSObject @property (nonatomic, strong)XMPPStream *xmppStream; @property (nonatomic, copy)NSString *password; //单例1.声明一个获取对象的静态方法 +(XMPPManager *)shareManager; -(void)initXMPPWithUserName:(NSString *)name andPassword:(NSString *)pw; -(XMPPMessage *)sendMessageWithType:(NSString *)type andToName:(NSString *)toName andBody:(NSString *)body; @end XMPPManager.m #import "XMPPFramework.h" #import "XMPPManager.h" //2.声明一个静态的变量 static XMPPManager *_manager; @implementation XMPPManager //创建实例对象 +(XMPPManager *)shareManager{ if (!_manager) { _manager = [[XMPPManager alloc]init]; } return _manager; } //个人用户登陆 -(void)initXMPPWithUserName:(NSString *)name andPassword:(NSString *)pw{ self.password = pw; self.xmppStream = [[XMPPStream alloc]init]; // 设置delegate [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; // 设置服务器地址 // 172.60.5.100 124.207.192.18 [self.xmppStream setHostName:@"172.60.5.100"]; // 设置端口 [self.xmppStream setHostPort:5222]; // 设置当前用户 NSString *jidString = [NSString stringWithFormat:@"%@@tarena.com",name]; XMPPJID *jid = [XMPPJID jidWithString:jidString]; [self.xmppStream setMyJID:jid]; // 连接服务器 [self.xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:nil]; } //向对方发送消息 -(XMPPMessage *)sendMessageWithType:(NSString *)type andToName:(NSString *)toName andBody:(NSString *)body{ XMPPMessage *message = [XMPPMessage messageWithType:type to:[XMPPJID jidWithString:[NSString stringWithFormat:@"%@@tarena.com",toName]]]; [message addBody:body]; [self.xmppStream sendElement:message]; return message; } -(void)xmppStreamDidConnect:(XMPPStream *)sender{ NSLog(@"已经连接"); //登录授权 [self.xmppStream authenticateWithPassword:self.password error:nil]; } -(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{ NSLog(@"断开连接"); } -(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{ NSLog(@"登录成功"); //通知服务器登录状态 [self.xmppStream sendElement:[XMPPPresence presence]]; } -(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{ NSLog(@"%@说%@",message.fromStr,message.body); //通过通知把消息传递出去 [[NSNotificationCenter defaultCenter]postNotificationName:@"receiveMessage" object:nil userInfo:@{@"message": message}]; } -(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{ NSLog(@"登录失败"); //注册 [self.xmppStream registerWithPassword:self.password error:nil]; } -(void)xmppStreamDidRegister:(XMPPStream *)sender{ NSLog(@"注册成功!"); [self.xmppStream authenticateWithPassword:self.password error:nil]; } -(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error{ NSLog(@"注册失败"); } @end 已经连接 登录失败 注册成功! 登录成功 (第一次登陆) 已经连接 登录成功 ViewController.h ViewController.m #import "ViewController.h" #import "XMPPManager.h" #import <AVFoundation/AVFoundation.h> @interface ViewController ()<UITableViewDelegate,UITableViewDataSource,UIImagePickerControllerDelegate,UINavigationControllerDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (nonatomic, strong)AVAudioPlayer *player; @property (weak, nonatomic) IBOutlet UITextField *sendInfoTF; @property (weak, nonatomic) IBOutlet UITextField *toNameTF; @property (nonatomic, strong)NSMutableArray *messages; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.messages = [NSMutableArray array]; [[XMPPManager shareManager]initXMPPWithUserName:@"1412liuguobin" andPassword:@"aaaaaaaa"]; //监听接收到的消息 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didReceiveMessage:) name:@"receiveMessage" object:nil]; //监听录音完成的通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(recordfinish:) name:@"recordfinish" object:nil]; } -(void)recordfinish:(NSNotification *)noti{ NSString *audioString = [noti.userInfo objectForKey:@"audioString"]; XMPPMessage *message = [[XMPPManager shareManager]sendMessageWithType:@"audio" andToName:self.toNameTF.text andBody:audioString]; [self.messages addObject:message]; [self.tableView reloadData]; } -(void)didReceiveMessage:(NSNotification *)noti{ XMPPMessage *message = [noti.userInfo objectForKey:@"message"]; [self.messages addObject:message]; [self.tableView reloadData]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.messages.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; XMPPMessage *message = self.messages[indexPath.row]; NSString *name = [[message.fromStr componentsSeparatedByString:@"@"]firstObject]; if (!message.fromStr) { name = @"我"; } if ([message.type isEqualToString:@"text"]) { cell.textLabel.text = [NSString stringWithFormat:@"%@说:%@",name,message.body]; }else if ([message.type isEqualToString:@"image"]) { cell.textLabel.text = [NSString stringWithFormat:@"%@的图片信息",name]; }else if ([message.type isEqualToString:@"audio"]) { cell.textLabel.text = [NSString stringWithFormat:@"%@的音频",name]; } return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ XMPPMessage *message = self.messages[indexPath.row]; if ([message.type isEqualToString:@"image"]) { NSString *imageString = message.body; NSData *imageData = [[NSData alloc]initWithBase64EncodedString:imageString options:0]; UIImage *image = [UIImage imageWithData:imageData]; UIViewController *vc = [[UIViewController alloc]init]; UIImageView *iv = [[UIImageView alloc]initWithFrame:vc.view.bounds]; iv.image = image; [vc.view addSubview:iv]; [self.navigationController pushViewController:vc animated:YES]; }else if ([message.type isEqualToString:@"audio"]) { NSString *audioString = message.body; NSData *audioData = [[NSData alloc]initWithBase64EncodedString:audioString options:0]; self.player = [[AVAudioPlayer alloc]initWithData:audioData error:nil]; [self.player play]; } } - (IBAction)clicked:(id)sender { UIButton *btn = sender; if (btn.tag==0) { XMPPMessage *message = [[XMPPManager shareManager]sendMessageWithType:@"text" andToName:self.toNameTF.text andBody:self.sendInfoTF.text]; [self.messages addObject:message]; [self.tableView reloadData]; }else{ UIImagePickerController *ipc = [[UIImagePickerController alloc]init]; ipc.delegate = self; ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; [self presentViewController:ipc animated:YES completion:nil]; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSString *imageURL = [[info objectForKey:UIImagePickerControllerReferenceURL]description]; NSData *imageData = nil; if ([imageURL hasSuffix:@"JPG"]) { imageData = UIImageJPEGRepresentation(image, 1); }else{//png imageData = UIImagePNGRepresentation(image); } NSLog(@"%@",info); // base64编码 把data转成string NSString *imageString = [imageData base64EncodedStringWithOptions:0]; XMPPMessage *message = [[XMPPManager shareManager]sendMessageWithType:@"image" andToName:self.toNameTF.text andBody:imageString]; [self.messages addObject:message]; [self.tableView reloadData]; [self dismissViewControllerAnimated:YES completion:nil]; } @end ================================================================================ 乱乱的知识点 1.国际化 i18n (internationalization ) 设置了以后,如果当模拟器修改语言以后,故事板中的控件显示的文本就会跟着修改 1. 在故事板中创建的按钮,修改显示语言 先添加按钮—>在故事板中增加语言: 根目录—>左上角三角—>Project—>Localization—>增加语言—>修改按钮上想要显示的文字样式 如:”gMY-9U-YVd.normalTitle" = "按钮"; ? ? 2.在类中用代码创建的控件,修改语言 a.在控制器中创建好控件—>新建类OS X—>Resource—>Strings File—>名字必须是Localizable—>完成 b.创建好以后—>在文件中输入:”hello"="hello”;键值对—>右侧边栏添加语言—>点击修改即可 ? ? - (void)viewDidLoad { [super viewDidLoad]; UILabel *l = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; l.text = NSLocalizedString(@"hello", @"这是个你好的意思”);//此处的hello是个key值 [self.view addSubview:l]; } 3.根据不同的语言环境,项目的名称可以跟着改变 Supporting Files—>InfoPlist.strings—>自己写CFBundleDisplayName=“小图标名称”; ? 2.本地通知 UILocalNotification 功能当用户退出程序后,每过3秒界面上拉框会提示,然后点击提示还会切换到此程序,还可以进行传值 (提高用户和程序的粘合度 ) - (void)viewDidLoad { [super viewDidLoad]; UILocalNotification *noti = [[UILocalNotification alloc]init]; noti.fireDate = [[NSDate new] dateByAddingTimeInterval:5]; [noti setRepeatInterval:NSCalendarUnitMinute]; noti.alertBody = @"大爷 好久没来玩儿了"; //传递参数 noti.userInfo = @{@"name": @"小丽"}; // 图标上面的数量 noti.applicationIconBadgeNumber = 3; [[UIApplication sharedApplication]scheduleLocalNotification:noti]; } //显示传递过来的参数 AppDelegate.h AppDelegate.m -(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{ NSString *name = [notification.userInfo objectForKey:@"name"]; NSLog(@"%@",name); 小丽 } ? 练习:点击直接到达新浪或微博页面 AppDelegate.h AppDelegate.m -(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{ //删除所有的通知 [[UIApplication sharedApplication]cancelAllLocalNotifications]; // 把程序图标上面的数去掉 [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0]; [[NSNotificationCenter defaultCenter]postNotificationName:@"backAction" object:nil userInfo:notification.userInfo]; } ViewController.h ViewController.m #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(bacAction:) name:@"backAction" object:nil]; } -(void)bacAction:(NSNotification *)noti{ NSString *name = [noti.userInfo objectForKey:@"name"]; NSString *path = @"http://www.baidu.com"; if ([name isEqualToString:@"新浪"]) { path = @"http://www.sina.com.cn"; }else if ([name isEqualToString:@"腾讯"]){ path = @"http://www.qq.com"; } UIViewController *vc = [[UIViewController alloc]init]; UIWebView *webView = [[UIWebView alloc]initWithFrame:self.view.bounds]; [webView loadRequest:[NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]]]; [vc.view addSubview:webView]; [self.navigationController pushViewController:vc animated:YES]; } - (IBAction)clicked:(UIButton *)sender { UILocalNotification *noti = [[UILocalNotification alloc]init]; noti.fireDate = [[NSDate new] dateByAddingTimeInterval:5]; [noti setRepeatInterval:NSCalendarUnitMinute]; NSString *title = [sender titleForState:UIControlStateNormal]; noti.alertBody =[NSString stringWithFormat: @"大爷 好久没来玩儿了 %@",title]; //传递参数 noti.userInfo = @{@"name": title}; // 图标上面的数量 noti.applicationIconBadgeNumber = 5; [[UIApplication sharedApplication]scheduleLocalNotification:noti]; } @end ? ? ? 3.重力传感器 CMMotionManager 只有在真机上才能调试,模拟器不可以 #import "ViewController.h" #import <CoreMotion/CoreMotion.h> //导入框架 @interface ViewController () @property (weak, nonatomic) IBOutlet UIButton *myBall;//界面上的按钮,假设为球 @property (nonatomic, strong)CMMotionManager *manager; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.manager = [[CMMotionManager alloc]init]; //设置更新频率 [self.manager setAccelerometerUpdateInterval:1.0/60]; //开始更新 [self.manager startAccelerometerUpdates]; [NSTimer scheduledTimerWithTimeInterval:1.0/60 target:self selector:@selector(getInfo) userInfo:nil repeats:YES]; } -(void)getInfo{ //获取手机的x,y,z值 CMAcceleration acc = self.manager.accelerometerData.acceleration; //球跟着值改变 self.myBall.center = CGPointMake(self.myBall.center.x+acc.x, self.myBall.center.y-acc.y); //x代表左右,y代表上下摇动,z代表侧面 // NSLog(@"x=%lf y=%lf z=%lf",acc.x,acc.y,acc.z);随之手机的晃动值会改变 } -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{ NSLog(@"开始摇了"); } -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{ NSLog(@"摇完了"); } -(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{ NSLog(@"以外中断了"); } @end 4.GPS (陀螺仪)CLLocationManager #import "ViewController.h" #import <CoreLocation/CoreLocation.h> @interface ViewController ()<CLLocationManagerDelegate> @property (nonatomic, strong)CLLocationManager *manager; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 询问是否开启了位置服务 if ([CLLocationManager locationServicesEnabled]) { self.manager = [[CLLocationManager alloc]init]; // 设置精度 [self.manager setDesiredAccuracy:kCLLocationAccuracyBest]; // 设置更新距离 距离越小越精准 越费电 [self.manager setDistanceFilter:20]; self.manager.delegate = self; //开始更新经纬度 [self.manager startUpdatingLocation]; //获取方向 陀螺仪的值 [self.manager startUpdatingHeading]; } } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ CLLocationCoordinate2D coord = newLocation.coordinate; NSLog(@"经度:%lf 维度:%lf",coord.longitude,coord.latitude); } //获取陀螺仪的值 -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{ NSLog(@"角度:%f",newHeading.trueHeading); } @end 真机调试: $99 个人 公司:可以分配权限(多人使用) 可上线AppStore 只允许100台设备 $299 大企业 公司内部使用 不限制设备数量 Certificates:证书列表 创建证书时需要用到当前mac电脑的信息 创建出来的证书 只能供当前电脑使用 在当前电脑上导出的证书就可以应用到别的电脑上了 网上下载的证书格式是cer 导出的是p12 只有p12的才能在别的电脑上使用 ?Identifiers:标示 限制应用的公司名称 一般给一个* 不做限制 ?Devices:设备列表 显示已经和账号绑定的设备 ?Provisioning Profiles:描述文件 会在手机上面和电脑上面都安装 以后每次 调试 不需要联网也可以 和真机相关的知识点: 1.GPS定位 2.重力感应 3.陀螺仪
时间: 2024-10-02 18:46:09