UI-2

2數據庫

多控制器處理

爲了方便管理控制器,ios提供了2種特殊的控制器來管理多控制器
- UINavigationController
- UITabBarController數組

導航控制器UINavigationController

  • 凡有導航條的,都是這個控制器
  • 導航控制器有本身的view,還有一個導航條(y = 20),還有一個棧頂控制器view
  • 添加就是pushViewController
  • 凡是導航條下面的scrollView默認都會有一個64的contentInset偏移量,若是不想默認,就須要設置self.automaticallyAdjustsScrollViewInset = NO表示不要自動設置偏移量。這樣下面的滾動表格就從0,0開始了。網絡

  • app

  • 當一個控制器被添加到了導航控制器,它的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經常使用的存儲iview

1. plist歸檔  
2. Prference偏好設置
3. NSKeyedArchiver歸檔(NSCoding)--> 保存自定義對象
4. SQLite3
5. Core Date(對SQLite3的封裝,有一套庫來操做數據庫)

每個ios應用都有本身的沙盒,而且每個app都是文件隔離,獨立的沙盒,沙盒目錄以下
-w200dom

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];    
}
相關文章
相關標籤/搜索