zoukankan      html  css  js  c++  java
  • UI-2

    2

    多控制器处理

    为了方便管理控制器,ios提供了2种特殊的控制器来管理多控制器
    - UINavigationController
    - UITabBarController

    导航控制器UINavigationController

    • 凡是有导航条的,都是这个控制器

    • 导航控制器有自己的view,还有一个导航条(y = 20),还有一个栈顶控制器view

    • 添加就是pushViewController

    • 凡是导航条下面的scrollView默认都会有一个64的contentInset偏移量,如果不想默认,就需要设置self.automaticallyAdjustsScrollViewInset = NO表示不要自动设置偏移量。这样下面的滚动表格就从0,0开始了。

    • 当一个控制器被添加到了导航控制器,它的navgationController属性就有值了

    • 导航控制器是以栈的形式添加控制器的,先进后出,意味着被压住的控制器是不会被销毁的,只是被移走了,只有pop才会删除控制器,当pop跳跃移动的时候,那么这中间的控制器都会被销毁

    • 导航条内容是由当前栈顶控制器的UINavigationiItem模型属性决定的。就是说你这个页面想展示什么,就去这个页面自己设置

      • backBarButtonItem:返回按钮
      • titleView:中间标题视图
      • title:中间文字
      • leftBarButtonItem:左上角视图(可以是文字,图片,view)
        • 自定义View的时候不需要设置位置,直接sizeToFit即可
      • rightBarButtonItem:右上角视图
    • 设置Item内容的时候会遇到渲染问题,可以单独渲染图片Origion,可以全局渲染

    /**
     全局设置状态栏白色
     全局设置导航栏背景mainColor
     全局设置导航栏标题文字颜色白色,微软雅黑
     全局设置导航栏Item颜色 白色
     */
    - (void)systemSetting {
        
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
        [[UINavigationBar appearance] setBarTintColor:mainColor];
        [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
        [[UINavigationBar appearance] setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [UIColor whiteColor], NSForegroundColorAttributeName, [UIFont fontWithName:@"MicrosoftYaHei" size:18.0], NSFontAttributeName, nil]];
    }
    

    控制器(view)的生命周期

    控制器View的生命周期方法:只要是控制器的生命周期方法,都是以view开头.
    
    控制器View加载完成时调用
    - (void)viewDidLoad { 
         [super viewDidLoad]; 
    
    } 
    
    控制器的View显示完成时调用
    -(void)viewDidAppear:(BOOL)animated{ 
    [super viewDidAppear:animated]; 
    
    } 
    
    控制器的View即将显示的时候调用
    -(void)viewWillAppear:(BOOL)animated{ 
    [super viewWillAppear:animated]; 
    
    } 
    
    控制器的View完全消失的时候调用
    -(void)viewDidDisappear:(BOOL)animated{ 
    [super viewDidDisappear:animated]; 
    
    } 
    
    控制器的View即将消失的时候调用.
    -(void)viewWillDisappear:(BOOL)animated{ 
    [super viewWillDisappear:animated]; 
    
    } 
    
    布局控制器View的子控件完成时调用
    -(void)viewDidLayoutSubviews{ 
    [super viewDidLayoutSubviews]; 
    
    } 
    将要布局控制器的View里面子控件的时候就会调用.
    -(void)viewWillLayoutSubviews{ 
    [super viewWillLayoutSubviews]; 
    
    } 
    
    ARC的生命周期 
    viewDidLoad->viewWillAppear->viewDidLayoutSubviews->viewDidLayoutSubviews->viewDidAppear->
    viewWillDisappear->viewDidDisappear
    
    
    在非ARC当中. 
    当前控制器的View即将被销毁的时候会调用
    -(void)viewWillUnload{ 
    [super viewWillUnload]; 
    } 
    
    当前控制器的View被销毁的时候会调用
    -(void)viewDidUnload{ 
    [super viewDidUnload]; 
         清空界面上的数据. 
         self.dataList = nil; 
    } 
    
    viewDidLoad->viewWillAppear->viewDidLayoutSubviews->viewDidLayoutSubviews->viewDidAppear->
    viewWillDisappear->viewDidDisappear->接收到内存警告->viewWillUnload->释放View->viewDidUnload
    
    

    通讯录

    1. 监听 账号密码都有的时候才能登陆
    2. switch记住密码连调
    3. 从ios8后,UIAlertView和UIActionSheet合并成了UIAlertController(用block回调,要创建AlertController,再创建AlertAction按钮,然后在presentVC,使用简单)
    4. SVProgress
    5. 控制器反向传值(block 代理),通知

    数据存储

    ios常用的存储

    1. plist归档	
    2. Prference偏好设置
    3. NSKeyedArchiver归档(NSCoding)--> 保存自定义对象
    4. SQLite3
    5. Core Date(对SQLite3的封装,有一套库来操作数据库)
    

    每一个ios应用都有自己的沙盒,并且每一个app都是文件隔离,独立的沙盒,沙盒目录如下
    -w200

    Documents存储着应用运行时需要持久化的数据,iTunes同步设备会备份该目录,例如游戏文档(网络下载的存这里直接苹果退回)
    tmp是保存临时数据,不备份,系统可能会清除
    library/Caches存储持久化数据,iTunes不会备份,一般存储体积大不用备份的非重要数据
    library/Preference保存应用程序所有偏好设置,iTunes会备份,
    这些数据如果存储错了,那么打包可能会被苹果退回来的


    • plist
    - (IBAction)save:(id)sender {
       
        数据存储是保存在手机里面的 
        plist文件存储一般都是存取字典和数组,直接写成plist文件,把它存到应用沙盒当中. 
        只有在ios当中才有plist存储,它是ios特有的存储方式. 
       
        获取沙盒根根路径,每一个应用在手机当中都有一个文件夹,这个方法就是获取当前应用在手机里安装的文件. 
        NSString *homeDir = NSHomeDirectory(); 
        NSLog(@"homeDir = %@",homeDir); 
       
        在某个范围内搜索文件夹的路径. 
        directory:获取哪个文件夹 
        domainMask:在哪个路径下搜索 
        expandTilde:是否展开路径. 
        这个方法获取出的结果是一个数组.因为有可以搜索到多个路径. 
        NSArray *array =  NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
        在这里,我们指定搜索的是Cache目录,所以结果只有一个,取出Cache目录 
        NSString *cachePath = array[0]; 
        拼接文件路径 
        NSString *filePathName = [cachePath stringByAppendingPathComponent:@"agePlist.plist"]; 
        想要把这个字典存储为plist文件. 
        直接把字典写入到沙盒当中 
        用字典写, plist文件当中保存的是字典. 
        NSDictionary *dict = @{@"age" : @18,@"name" : @"gaowei"}; 
        获取沙盒路径 
        ToFile:要写入的沙盒路径 
        [dict writeToFile:filePathName atomically:YES];
        用数组写,plist文件当中保存的类型是数组. 
        NSArray *dataArray = @[@56,@"asdfa"]; 
        获取沙盒路径 
        ToFile:要写入的沙盒路径 
        [dataArray writeToFile:filePathName atomically:YES];   
    }
    
    读取数据
    - (IBAction)reader:(id)sender {
        这个方法获取出的结果是一个数组.因为有可以搜索到多个路径. 
        NSArray *array =  NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
        在这里,我们指定搜索的是Cache目录,所以结果只有一个,取出Cache目录 
        NSString *cachePath = array[0]; 
        拼接文件路径 
        NSString *filePathName = [cachePath stringByAppendingPathComponent:@"agePlist.plist"]; 
       
        从文件当中读取字典, 保存的plist文件就是一个字典,这里直接填写plist文件所存的路径 
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePathName]; 
       
        如果保存的是一个数组.那就通过数组从文件当中加载. 
        NSArray *dataArray = [NSArray arrayWithContentsOfFile:filePathName];
        NSLog(@"%@",dataArray);
        
    }
    
    • 偏好设置:一般用来保存用户名,密码,字体大小等设置。
    - (IBAction)save:(id)sender {
        偏好设置NSUserDefaults 
        底层就是封闭了一个字典,利用字典的方式生成plist文件 
        好处:不需要关心文件名(它会自动生成)快速进行键值对存储. 
        NSUserDefaults *defautls = [NSUserDefaults standardUserDefaults];
        [defautls setObject:@"gaowei" forKey:@"name"];
        [defautls setBool:YES forKey:@"isBool"];
        [defautls setInteger:5 forKey:@"num"];
        //同步,立即写入文件.
        [defautls synchronize];
       
    } 
    - (IBAction)reader:(id)sender {
        存是用什么key存的, 读的时候就要用什么key值取 
        存的时候使用的什么类型,取的时候也要用什么类型. 
        NSString *str = [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
        BOOL isBool  = [[NSUserDefaults standardUserDefaults] boolForKey:@"isBool"];
        NSInteger num = [[NSUserDefaults standardUserDefaults] integerForKey:@"num"];
        NSLog(@"name =%@-isBool=%d-num=%ld",str,isBool,num);
        
    }
    
    
    • 归档:偏好设置和pilsh都是字典数组,存储对象需要归档
    保存数据
    - (IBAction)save:(id)sender {
        归档一般都是保存自定义对象的时候,使用归档.因为plist文件不能够保存自定义对象. 
        如果一个字典当中保存有自定义对象,如果把这个字典写入到文件当中,它是不会生成plist文件的. 
        Persion *persion = [[Persion alloc] init];
        persion.name = @"gaowei"; 
        persion.age = 18; 
       
        获取沙盒临时目录 
        NSString *tempPath = NSTemporaryDirectory(); 
        NSString *filePath = [tempPath stringByAppendingPathComponent:@"persion.data"];
        archiveRootObject这个方法底层会去调用保存对象的encodeWithCoder方法,去询问要保存这个对象的哪些属性. 
        所以要实现encodeWithCoder方法, 告诉要保存这个对象的哪些属性. 
        [NSKeyedArchiver archiveRootObject:persion toFile:filePath]; 
    }
    读取数据
    - (IBAction)reader:(id)sender {
        获取沙盒临时目录 
        NSString *tempPath = NSTemporaryDirectory(); 
        NSString *filePath = [tempPath stringByAppendingPathComponent:@"persion.data"];
        NSKeyedUnarchiver会调用initWithCoder这个方法,来让你告诉它去获取这个对象的哪些属性.
        所以我们在保存的对象当中实现initWithCoder方法. 
        Persion *persion = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; 
        NSLog(@"name=%@---age=%d",persion.name,persion.age); 
    }
    
    
    
    要保存的对象
    #import <Foundation/Foundation.h> 
    要遵守<NSCoding>协议
    @interface Persion : NSObject<NSCoding>
    
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, assign) int age;
    
    @end
    
    archiveRootObject这个方法底层会去调用保存对象的encodeWithCoder方法,去询问要保存这个对象的哪些属性.
    只有遵守了NSCoding协议之后才能够实现这个方法.
    -(void)encodeWithCoder:(NSCoder *)encode{
    
        [encode encodeObject:self.name forKey:@"name"];
        [encode encodeInt32:self.age forKey:@"age"];
    }
    
    NSKeyedUnarchiver会调用initWithCoder这个方法,来让你告诉它去获取这个对象的哪些属性.
    initWithCoder什么时候调用:解析一个文件的时候就会调用.
    -(instancetype)initWithCoder:(NSCoder *)decoder{   
         这个地方为什么没有[super initWithCoder]
         是因为它的父类没有遵守NSCoding协议
        if (self = [super init]) {
           要给它里面的属性进行赋值,外界取得对象时访问该属性,里面才会用值. 
           self.age = [decoder decodeInt32ForKey:@"age"];
           self.name = [decoder decodeObjectForKey:@"name"];
        }
        return self;
    }
    
    

    UITabBarController

    • 也用于管理多控制器,也有一个子控制器view,底部有一个UITabBar。UITabBar的高度是49selecyedIndex属性控制当前显示控制器,并且也有一个数组childViewControllers,但是添加的时候不是栈的形式了,当选中(显示)别的控制器的时候,不显示的控制器还是一直在的,因为都在数组里,没释放的。
    • UITabBar内容:
      • 如果UITabBarController有N个子控制器,那么UITabBar就有N个UITabBarButton。UITabBarButton里面的内容由UITabBarItem 模型控制,其中有title,image,selectedImage,badgeValue等属性。

    MODAL跳转

    • 从地下钻出来的控制器,就是modal。任何控制器都能modal出来,用present 和 dismiss来完成。push一般是有关系的两个页面,modal一般注册啊这个那个都可以用。

    手势&绘图

    对UIView的形变:transform

    在做动画的时候,一般会对View做平移,旋转,缩放等操作,就用到transform

    - CGAffineTransformTranslate
    - CGAffineTransformRotate
    - CGAffineTransformScale
    

    IOS的事件

    1.ios的事件分为三大类,触摸,加速计,远程控制事件,分别是触摸,摇动手机,控制耳机按钮等。

    触摸:ios中不是任何对象都能处理事件,只有继承了UIResponder的对象才能响应,称为响应者对象。UIApplication,UIViewController,UIView都继承自UIResponder,都能接收并且处理事件。

    //当开始触摸屏幕的时候调用
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        NSLog(@"%s",__func__);
    }
    
    //触摸时开始移动时调用(移动时会持续调用)
    //NSSet:无序
    //NSArray:有序
    -(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
         //NSLog(@"%s",__func__);
        //做UIView拖拽
        UITouch *touch = [touches anyObject];
        
        //求偏移量 = 手指当前点的X - 手指上一个点的X
        CGPoint curP = [touch locationInView:self];
        CGPoint preP = [touch previousLocationInView:self];
        NSLog(@"curP====%@",NSStringFromCGPoint(curP));
        NSLog(@"preP====%@",NSStringFromCGPoint(preP));
        
        CGFloat offsetX = curP.x - preP.x;
        CGFloat offsetY = curP.y - preP.y;
        
        //平移
        self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
        
    }
    
    //当手指离开屏幕时调用
    -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
         NSLog(@"%s",__func__);
    }
    
    
    //当发生系统事件时就会调用该方法(电话打入,自动关机)
    -(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
         NSLog(@"%s",__func__);
    }
    
    

    2.事件的传递:touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法中的touches记录了你当前触摸的点,上一次触摸的点,你触摸的当前控制器或者view,而event就保存着事件,里面保存着当前发生的事件的时间,类型等。


    当发生一个触摸事件,系统会将该事件加入一个由UIApplication管理事件的队列(先进先出),然后拿出最前面的事件(也就是先触摸的),然后交给主窗口keyWindow,主窗口会根据视图层次结构找到一个最合适的视图来处理触摸事件,找到了就调用touch方法了。


    UIAppcation -> UIWindow -> 父控件 -> 一层层到子控件。如果父控件无法接受触摸,那么子控件也无法接收触摸。如果父控件隐藏,子控件也隐藏。如果父控件aplha = 0,子控件也是0.

    3.UIView不接收触摸的三种情况

    - userIntercationEnable = NO
    - hidden = YES
    - alpha = 0.0 ~ 0.01
    - UIImageView默认用户交互是No
    

    4.如何寻找最适合View?

    先看自己能否接收事件,再看触摸点在不在自己身上,然后从后往前遍历子控件,如果没有合适的就是自己最合适处理。如果用代码来寻找,就要调用hitTest方法。(开发中没用过 )

    //作用:去寻找最适合的View
    //什么时候调用:当一个事件传递给当前View,就会调用.
    -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
        NSLog(@"%s",__func__);
        return [super hitTest:point withEvent:event];
    }
    

    5 事件触摸

    在产生一个事件时,系统会将该事件加入到一个由UIApplication管理的事件队列中,
    UIApplication会从事件队列中取出最前面的事件,将它传递给先发送事件给应用程序的主窗口.
    主窗口会调用hitTest方法寻找最适合的视图控件,找到后就会调用视图控件的touches方法来做具体的事情.
    当调用touches方法,它的默认做法, 就会将事件顺着响应者链条往上传递,
    传递给上一个响应者,接着就会调用上一个响应者的touches方法

    如何去寻找上一个响应者?
    1.如果当前的View是控制器的View,那么控制器就是上一个响应者.
    2.如果当前的View不是控制器的View,那么它的父控件就是上一个响应者.
    3.在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
    4.如果window对象也不处理,则其将事件或消息传递给UIApplication对象
    5.如果UIApplication也不能处理该事件或消息,则将其丢弃
    

    6.手势识别

    手势识别器UIGrestreRecognizer是一个抽象类,使用它的子类才能处理手势,有点按,长按,轻扫,拖动,旋转,捏合六种。

    添加点按手势
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)]; 
    手势也可以设置代理
    tap.delegate = self; 
    添加手势
    [self.imageV addGestureRecognizer:tap];
    
    代理方法:
    是否允许接收手指
    -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
        让图片的左边不可以点击, 
        获取当前手指所在的点.是在图片的左边还是在图片的右边. 
        CGPoint curP = [touch locationInView:self.imageV];
        if (curP.x > self.imageV.bounds.size.width * 0.5) {
            在图片的右侧
            return YES;
        }else{
            在图片的左侧
            return NO;
        }
        return YES;
    }
    
    
    添加长按手势
    UILongPressGestureRecognizer *longP = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longP:)];  
    [self.imageV addGestureRecognizer:longP];
    
    当长按时调用.
    这个方法会调用很多次, 当手指长按在上面不松,来回移动时,会持续调用. 
    所以要判断它的状态.
    - (void)longP:(UILongPressGestureRecognizer *)longP{
        if(longP.state == UIGestureRecognizerStateBegan){
            NSLog(@"开始长按");
        }else if(longP.state == UIGestureRecognizerStateChanged){
            NSLog(@"长按时手指移动");
        }else if(longP.state == UIGestureRecognizerStateEnded){
            NSLog(@"手指离开屏幕");
        }
    }
    
    添加轻扫手势
    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
     轻扫手势默认是向右边称轻扫
     可以设置轻扫的方法.
     一个轻扫手势只能设置一个方法的轻扫.想要让它有多个方向的手势,必须得要设置的
      swipe.direction =  UISwipeGestureRecognizerDirectionLeft;
      [self.imageV addGestureRecognizer:swipe];
       
    添加轻扫手势
    UISwipeGestureRecognizer *swipe2 = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
    轻扫手势默认是向右边称轻扫
    可以设置轻扫的方法.
    一个轻扫手势只能设置一个方法的轻扫.想要让它有多个方向的手势,必须得要设置的
      swipe2.direction =  UISwipeGestureRecognizerDirectionUp;
      [self.imageV addGestureRecognizer:swipe2];
    
    
    - (void)swipe:(UISwipeGestureRecognizer *)swipe{
         判断的轻扫的方向
        if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
            NSLog(@"向左轻扫");
        }else if(swipe.direction == UISwipeGestureRecognizerDirectionUp){
            NSLog(@"向上轻扫");
        }
    }
    
    
    添加平移手势
    
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
        [self.imageV addGestureRecognizer:pan];
    
    当手指拖动时调用
    - (void)pan:(UIPanGestureRecognizer *)pan{
       
        拖动手势也有状态 
        if(pan.state == UIGestureRecognizerStateBegan){
              开始拖动  
        }else if(pan.state == UIGestureRecognizerStateChanged){
            可能获取不当前手指移动的举例 
            是相对于最原始的点 
            CGPoint transP = [pan translationInView:self.imageV];
           
            会清空上一次的形变 
            self.imageV.transform = CGAffineTransformMakeTranslation(transP.x,transP.y); 
            self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);
    
            复位,让它相对于上一次. 
            [pan setTranslation:CGPointZero inView:self.imageV];
        }else if(pan.state == UIGestureRecognizerStateEnded){
            结束拖动       
        }
    }
    
    添加捏合手势
    UIPinchGestureRecognizer *pinGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinGes:)];
    设置代理使其能够同时支持多个手势
    pinGes.delegate = self;
    [self.imageV addGestureRecognizer:pinGes];
    
    
    旋转时调用
    - (void)pinGes:(UIPinchGestureRecognizer *)pin{
        self.imageV.transform = CGAffineTransformScale(self.imageV.transform, pin.scale, pin.scale);
        复位 
        [pin setScale:1];
       
    }
    
    添加旋转手势
     UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
     设置代理使其能够同时支持多个手势
     rotation.delegate = self;
     [self.imageV addGestureRecognizer:rotation];
    
    
    当手指开始旋转时调用.
    - (void)rotation:(UIRotationGestureRecognizer *)rotation{ 
        self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, rotation.rotation);
        复位. 
        [rotation setRotation:0]; 
    }
    
    

    可以添加多个手势,例如又旋转又放大又捏合(支持代理,实现方法就行,要找一找,方法比较多,是一个允许支持多手势的代理方法),

    手势案例 -- 抽屉效果

    
    Make by:弓_虽_子 
    
    
    
    第一步:搭建界面
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        搭建界面 
        [self setUpView]; 
    }
    
    - (void)setUpView{
        添加左边的View 
        UIView *leftV = [[UIView alloc] initWithFrame:self.view.bounds]; 
        左边蓝色
        leftV.backgroundColor = [UIColor blueColor];
        [self.view addSubview:leftV];
        添加右边的View 
        UIView *rightV = [[UIView alloc] initWithFrame:self.view.bounds]; 
        右边縁色 
        rightV.backgroundColor = [UIColor greenColor];
        self.rightV = rightV;
        [self.view addSubview:rightV];
        添加中间的View(中间一个最后添加,显示到最外面.) 
        UIView *mainV = [[UIView alloc] initWithFrame:self.view.bounds]; 
        中间红色
        mainV.backgroundColor = [UIColor redColor];
        self.mainV = mainV;
        [self.view addSubview:mainV];
    }
    
    
    第二步.添加手势.能够让中间的红色View左右移动,要在控制器View加载完成时就要添加View
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        搭建界面 
        [self setUpView]; 
        拖动手势 
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; 
        添加手势 
        [self.mainV addGestureRecognizer:pan];
    }
    
     实现手势方法:
    当手指拖动时调用.
    -(void)pan:(UIPanGestureRecognizer *)pan{
        获取手指在屏幕上面的偏移量 
        CGPoint transP = [pan translationInView:self.mainV];
        在这里为什么不用Transform,是因为我们移动时,要改变的尺寸大小.用Transform只能改变它的位置. 
        self.mainV.transform  = CGAffineTransformTranslate(self.mainV.transform, transP.x, 0);
        计算mainV的Frame 
        单独抽出一个方法来计算mainV的frame.因为要计算它的Y值和高度.代码会很多.所以单独抽出一个方法
        self.mainV.frame = [self frameWithOffsetX:transP.x]; 
        每次移动时判断当前MainV的x值是大于0还是小于0.如果是大于0 , 显示左边,小于0 显示右边 
        if (self.mainV.frame.origin.x > 0) {
            self.rightV.hidden = YES;
        }else if(self.mainV.frame.origin.x < 0){
            self.rightV.hidden = NO; 
        }
        注意要做复位
        [pan setTranslation:CGPointZero inView:self.mainV];    
    }
    
    最大Y值为100
    #define maxY 100 
    根据偏移量计算mainV的frame.
    - (CGRect)frameWithOffsetX:(CGFloat)offsetX{   
        取出最原始的Frame 
        CGRect frame = self.mainV.frame; 
        frame.origin.x += offsetX;
        获取屏幕的宽度 
    
        (计算Y值如果下图:找最大值.当Main.x拖动最大的时候,Main.y值也最大.main.x最大为屏幕的宽度)
         设定一个最大Y值MaxY为100,正好.当max.x为屏幕的宽度时,最大Y等于100
         所以Y值等于 main.y = main.x * maxY / ScreenW;
                   100 = 375 * 100 / 375;) 
        有可能frame.origin.x有可能是小于0,小于0的话, 得出的Y值就会小于0,小于0就会出现, 红色的View向上走. 
        对结果取绝对值.
        frame.origin.y =  fabs(frame.origin.x * maxY / screenW); 
        计算frame的高度 
            (当前Main的高度等于屏幕的高度减去两倍的Y值.)
        frame.size.height = screenH - 2 * frame.origin.y; 
        返回计算好的frame.
        return frame; 
    }
    
        
    
    
    第三步:当手指松开时做到自动定位. 
    
    MainV定位到右侧的X值
    #define targetR 275 
    MainV定位到右侧的X值
    #define targetL -275
    
    当手指拖动时调用.
    -(void)pan:(UIPanGestureRecognizer *)pan{
        获取手指在屏幕上面的偏移量 
        CGPoint transP = [pan translationInView:self.mainV];
        在这里为什么不用Transform,是因为我们移动时,要改变的尺寸大小.用Transform只能改变它的位置. 
        self.mainV.transform  = CGAffineTransformTranslate(self.mainV.transform, transP.x, 0);
        计算mainV的Frame 
        单独抽出一个方法来计算mainV的frame.因为要计算它的Y值和高度.代码会很多.所以单独抽出一个方法
        self.mainV.frame = [self frameWithOffsetX:transP.x]; 
        每次移动时判断当前MainV的x值是大于0还是小于0.如果是大于0 , 显示左边,小于0 显示右边 
        if (self.mainV.frame.origin.x > 0) {
            self.rightV.hidden = YES;
        }else if(self.mainV.frame.origin.x < 0){
            self.rightV.hidden = NO; 
        }
        判断手指的状态 
        if(pan.state == UIGestureRecognizerStateEnded){ 
            当手指松开时进入执行 
            记录最终判断结果后.定位的值.
            CGFloat target = 0; 
            当手指松开,要判断MainV的x值是否大于屏幕的一半.如果大于屏幕一半时, 自动定位到右边一个位置. 
            if (self.mainV.frame.origin.x > screenW * 0.5) {
                target = targetR; 
            }else if(CGRectGetMaxX(self.mainV.frame) < screenW * 0.5){
                当手指松开,要判断MainV的最大的X值是否小于屏幕的一半.如果小于屏幕的一半时, 自动定位到左边的位置. 
                target = targetL; 
            }
            最终定位的x值 - 当前的main.x的值. 求出便宜量.使其定位
            CGFloat offsetX = target - self.mainV.frame.origin.x; 
            
      
            根据便宜量设置mainV的frame值 
            CGRect frame = [self frameWithOffsetX:offsetX]; 
            [UIView animateWithDuration:0.25 animations:^{ 
                 伴随动画设置frame
                self.mainV.frame = frame; 
            }];
        }
        注意要做复位
        [pan setTranslation:CGPointZero inView:self.mainV];    
    }
    
    
    
    
    
    
  • 相关阅读:
    Hadoop安装教程_单机/伪分布式配置_Hadoop2.6.0/Ubuntu14.04
    练oj时的小技巧(大多都在oj记录里,这是被忘记的部分)
    HDU 3032 (SG打表找规律)
    SG 大法(Sprague-Grundy函数)
    基于Linux的MySQL基本操作
    java.sql.SQLException: Unable to load authentication plugin ‘caching_sha2_password‘.解决方法
    手把手教你安装和配置MYSQL数据库
    理解Python闭包,这应该是最好的例子
    SQL基础
    MySQL令人咋舌的隐式转换
  • 原文地址:https://www.cnblogs.com/sgxx/p/7120092.html
Copyright © 2011-2022 走看看