iOS框架

蘋果框架

UIKit -> UI
QuartzCore(繪圖)
Core Animation
MapKit(地圖)
CoreLocation(定位)
AVFoundation(音視頻)
...

UIKit常用的總結(jié):

UIButton內(nèi)部細(xì)節(jié)

#import "XLShopViewButton.h"

@implementation XLShopViewButton

# 內(nèi)部會(huì)調(diào)用的指定初始化器
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        // 禁止點(diǎn)擊
        self.enabled = NO;  
        // 高亮狀態(tài)下不要調(diào)整按鈕顯示的圖片
        self.adjustsImageWhenHighlighted = NO;
        // disabled狀態(tài)下不要調(diào)整按鈕顯示的圖片
        self.adjustsImageWhenDisabled = NO;
    }
    return self;
}
+ (instancetype)shopView {
    return [[self alloc] init];
}

+ (instancetype)shopWithModel:(id)model {
    XLShopViewButton *button = [self shopView];
    return button;
}


# 布局子控件方式一
// 返回文字的位置和尺寸
//- (CGRect)titleRectForContentRect:(CGRect)contentRect {  
//    return CGRectMake(0, 0, contentRect.size.width, contentRect.size.width);
//}
// 返回圖片的位置和尺寸
//- (CGRect)imageRectForContentRect:(CGRect)contentRect {  
//    return CGRectMake(0, 70, contentRect.size.width, contentRect.size.height - 70);
//}
- (void)configationLabelAndImageValues {
    [self setImage:[UIImage imageNamed:@"tab1"] forState:UIControlStateNormal];
    [self setTitle:@"我是文字" forState:UIControlStateNormal];
    [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
# 布局子控件方式二
- (void)layoutSubviews {
    [super layoutSubviews];
   // 蘋果內(nèi)部會(huì)在這個(gè)方法布局子控件,它布局好后,在改回需求的位置尺寸和位置
    CGFloat imageWidth = self.frame.size.width;
    CGFloat height = self.frame.size.height;
    self.imageView.frame = CGRectMake(0, 0, imageWidth, imageWidth);
    self.titleLabel.frame = CGRectMake(0, imageWidth, imageWidth, height - imageWidth);
}
@end

按鈕調(diào)整內(nèi)部控件的eg.

// 去調(diào)整按鈕內(nèi)部的titleLabel的大小
// 返回按鈕titleLabel內(nèi)部的尺寸
// contentRect:按鈕當(dāng)前的位置尺寸
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
    
    return CGRectMake(0, 0, contentRect.size.width, contentRect.size.width);
}
// 去調(diào)整按鈕內(nèi)部的imageView的大小
// 返回按鈕imageView內(nèi)部的尺寸
// contentRect:按鈕當(dāng)前的位置尺寸
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
    
    CGFloat w = 40;
    CGFloat h = 45;
    CGFloat x = (contentRect.size.width - w) * 0.5;
    CGFloat y = 20;
    return CGRectMake(x, y, w,h);
}

ScrollView常用屬性

#import "XLShopScrollView.h"

@interface XLShopScrollView ()
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;

@end
@implementation XLShopScrollView

#pragma mark - life cycle

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor redColor];
        self.contentSize = CGSizeZero;
        self.scrollEnabled = YES;
        self.scrollsToTop = YES;
        // 是否開啟分頁功能
        // 以scrollView的尺寸為標(biāo)準(zhǔn)分頁
        self.pagingEnabled = YES;
        // 是否展示指示器
        self.showsVerticalScrollIndicator = NO;
        self.showsHorizontalScrollIndicator = NO;
        // 是否有彈簧效果
        self.bounces = YES;
        // 即使沒有設(shè)置contentSize,也是有彈簧效果,用該屬性做下拉刷新。
        self.alwaysBounceVertical = YES;
        self.alwaysBounceHorizontal = NO;
        // 設(shè)置內(nèi)容的偏移量,得知內(nèi)容的偏移量
        // 手機(jī)中的坐標(biāo)系:水平方向右邊是正方向,豎直方向下邊是正方向
        // scrollView的偏移量是scrollView的原點(diǎn)位置減去內(nèi)容的原點(diǎn)位置
        // 向左滑動(dòng)偏移量是正的;向上滑動(dòng)偏移量是正的
        self.contentOffset = CGPointMake(0, 100);
        // 是否動(dòng)畫設(shè)置偏移量
        [self setContentOffset:CGPointZero animated:YES];
        // 設(shè)置內(nèi)邊距,偏移量還是之前的計(jì)算方式,只是可以多滾動(dòng)出內(nèi)邊距的距離
        self.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
        // 設(shè)置代理監(jiān)聽控件的內(nèi)部事件
        self.delegate = self;
        // 設(shè)置最大和最小縮放比例并實(shí)現(xiàn)縮放的代理方法,返回縮放對(duì)象
        // option + shift 可以移動(dòng)模擬器的縮放手勢(shì),松開shift間然后再縮放
        self.minimumZoomScale = 0.5;
        self.maximumZoomScale = 2;

        [self addSubview:self.activityIndicatorView];
    }
    return self;
}

//MARK: 
// 系統(tǒng)的短的指定初始化器會(huì)去調(diào)用長(zhǎng)的指定初始化器,
// 利用系統(tǒng)的指定初始化器來初始化自定義控件,把要自定義的控件放到長(zhǎng)的初始化方法內(nèi)部, 不管調(diào)用短的初始化還是長(zhǎng)的初始化方法都會(huì)得到自定義控件的創(chuàng)建和初始化
- (instancetype)init {
    if (self = [super init]) {}
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.activityIndicatorView.center = CGPointMake(self.center.x, -30);
}

#pragma mark - UIScrollViewDelegate

// scrollView正在滾動(dòng)的時(shí)候就會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {}                                        

// 用戶即將拖拽scrollview的時(shí)候會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {}

// 用戶即將停止拖拽scrollview的時(shí)候會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset API_AVAILABLE(ios(5.0)) {
    
}
// 用戶已經(jīng)停止拖拽scrollview的時(shí)候會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        NSLog(@"用戶已經(jīng)停止拖拽scrollView,scrollView停止?jié)L動(dòng)");
    } else {
        NSLog(@"用戶已經(jīng)停止拖拽scrollView,但是scrollview由于慣性會(huì)繼續(xù)滾動(dòng),并減速");
    }
}

// 停止?jié)L動(dòng)
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSLog(@"scrollView減速完畢,停止?jié)L動(dòng)");
}     
// 將要開始減速
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {}  

// scrollView已經(jīng)結(jié)束帶有動(dòng)畫的滑動(dòng)
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {}
// 返回帶縮放的控件
- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {return nil;}
// scrollView將要開始縮放
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view  {}
// scrollView已經(jīng)結(jié)束縮放
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale {}
// scrollview已經(jīng)結(jié)束縮放
- (void)scrollViewDidZoom:(UIScrollView *)scrollView API_AVAILABLE(ios(3.2)){}
// scrollView是否可以滑動(dòng)到頂部
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {return YES;}
// scrollView已經(jīng)滑動(dòng)到頂部
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {}

// iOS11 新增方法
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView API_AVAILABLE(ios(11.0), tvos(11.0)) {
    
}

#pragma mark - getters and setters

- (UIActivityIndicatorView *)activityIndicatorView {
    if (_activityIndicatorView == nil) {
        _activityIndicatorView = [[UIActivityIndicatorView alloc] init];
        [_activityIndicatorView startAnimating];
    }
    return _activityIndicatorView;
}

@end

scrollView分頁實(shí)現(xiàn)

# 方式一
// scrollView正在滾動(dòng)的時(shí)候就會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
   // 在滾動(dòng)的時(shí)候來判斷是幾頁,四舍五入
    NSInteger pageNum = scrollView.contentOffset.x / scrollView.frame.size.width + 0.5;
}   

# 方式二
// 用戶已經(jīng)停止拖拽scrollview的時(shí)候會(huì)自動(dòng)調(diào)用這個(gè)方法
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        NSLog(@"用戶已經(jīng)停止拖拽scrollView,scrollView停止?jié)L動(dòng)");
        NSInteger pageNum = scrollView.contentOffset.x / scrollView.frame.size.width;
    } 
}

// 停止?jié)L動(dòng)
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
      NSLog(@"scrollView減速完畢,停止?jié)L動(dòng)");
      NSInteger pageNum = scrollView.contentOffset.x / scrollView.frame.size.width;
}  

- (UIPageControl *)pageControl {
    if (_pageControl == nil) {
        _pageControl = [[UIPageControl alloc] init];
        // 當(dāng)頁數(shù)隱藏控件
        _pageControl.hidesForSinglePage = YES;
    }
    return _pageControl;
}

UITableView&collectionView

# tableview對(duì)多個(gè)插入,刪除,重新加載和移動(dòng)操作進(jìn)行動(dòng)畫處理
- (void)beginUpdates;
- (void)endUpdates;
// UITableView中該方法在是iOS11才有的方法
- (void)performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates 
completion:(void (^ _Nullable)(BOOL finished))completion API_AVAILABLE(ios(11.0),
#  collectionView對(duì)多個(gè)插入,刪除,重新加載和移動(dòng)操作進(jìn)行動(dòng)畫處理
// 很早之前就有了,可以直接使用
- (void)performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates 
completion:(void (^ _Nullable)(BOOL finished))completion 

對(duì)tableView的性能優(yōu)化?
1. 緩存池
2. Cell行高問題,如果不是等高的cell,必須的提前計(jì)算好行高,在heightForRowAtIndexPath:直接返回,此方法會(huì)調(diào)用很多次,在此方法當(dāng)中盡量少做大量計(jì)算操作。可以提前估一個(gè)行高,estimatedRowHeight(200-250)之間(目的是減少heightForRowAtIndexPath方法調(diào)用次數(shù))
3. Cell內(nèi)部空間最好一次給添加完,不要?jiǎng)討B(tài)去添加子控件
4. 如果說cell內(nèi)容子控件比較多,可以考慮把不需要于用戶交互的控件通過drawRect生成圖片的方式進(jìn)行繪制
5. 如果cell當(dāng)中使用圓角圖片時(shí),圖片最好通過quartz2D裁剪成圖形.,如果使用layer.cornerRadius+layer.masksToBounds 會(huì)造成離屏渲染,會(huì)耗性能
6. imageView寬高出現(xiàn)小數(shù)點(diǎn),會(huì)造成鋸齒,會(huì)造成離屏渲染耗性能。
7. 如果cell里面有圖片時(shí),imageView的尺寸要與圖片保持一樣大,服務(wù)器提供兩套圖片,開始加載的是小圖,點(diǎn)擊時(shí)在去加載大圖。如果圖片的尺寸不一樣,要對(duì)圖片進(jìn)行壓縮操作(形變操作transform,進(jìn)行大量的計(jì)算會(huì)耗性能)

UITextFiled

@interface XLShopTextField ()<UITextFieldDelegate>

@end
@implementation XLShopTextField

#pragma mark - life cycle

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.delegate = self;   
        // 指定鍵盤
        self.inputView = [UIView new];
    }
    return self;
}
// 通過xib創(chuàng)建
- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        // 指定鍵盤
        self.inputView = [UIView new];

    }
    return self;
}

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}
#pragma mark - UITextFieldDelegate

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    return YES;  
} 
- (void)textFieldDidEndEditing:(UITextField *)textField {
    
}
- (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason API_AVAILABLE(ios(10.0));  {
    
}

// 是否允許改變字符竄
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return NO;  
} 

- (void)textFieldDidChangeSelection:(UITextField *)textField API_AVAILABLE(ios(13.0), tvos(13.0)) {
    
}

- (BOOL)textFieldShouldClear:(UITextField *)textField {
    return NO;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    return NO;
}

@end

UITableViewCell

@interface XLShopTableViewCell ()
@property (nonatomic, strong) UIView *customBackgroundView;
@property (nonatomic, strong) UIView *customSelectedBackgroundView;
@end

@implementation XLShopTableViewCell

#pragma mark - life cycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        // accessoryView的優(yōu)先級(jí)大于accessoryType
        // 設(shè)置cell的右邊的指示控件
        self.accessoryView = [UIView new];
        // 設(shè)置cell的右邊的指示樣式
        self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        // 設(shè)置cell的選中的樣式
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        // 設(shè)置cell的背景顏色,backgroundView 優(yōu)先級(jí)大于backgroundColor
        self.backgroundColor = [UIColor redColor];
        // UITableViewCell的背景控件可以用圖片
        self.backgroundView = self.customBackgroundView;
        // 設(shè)置選中的背景控件
        self.selectedBackgroundView = self.customSelectedBackgroundView;
        
        
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
#pragma mark - getters and setters

- (UIView *)customBackgroundView {
    if (_customBackgroundView == nil) {
        _customBackgroundView = [UIView new];
        _customBackgroundView.backgroundColor = [UIColor greenColor];
    }
    return _customBackgroundView;
}

- (UIView *)customSelectedBackgroundView {
    if (_customSelectedBackgroundView == nil) {
        _customSelectedBackgroundView = [UIView new];
        _customSelectedBackgroundView.backgroundColor = [UIColor purpleColor];
    }
    return _customSelectedBackgroundView;
}
@end

UIViewContentMode

typedef NS_ENUM(NSInteger, UIViewContentMode) {
  # 拉伸到填充整個(gè)imageView
    UIViewContentModeScaleToFill,
  # 等比拉伸到剛好看到整個(gè)圖片
    UIViewContentModeScaleAspectFit,  
  # 等比拉伸到圖片的寬度或者高度剛好等于imageView,寬先達(dá)到ImageView的寬或者高先達(dá)到,之后居中顯示。搭配`clipsToBounds`超過圖片的內(nèi)容裁剪掉
    UIViewContentModeScaleAspectFill,     
    // redraw on bounds change (calls -setNeedsDisplay)
    UIViewContentModeRedraw,          
  # 按圖片原大小顯示 搭配`clipsToBounds`超過圖片的內(nèi)容裁剪掉
    UIViewContentModeCenter,              
    UIViewContentModeTop,
    UIViewContentModeBottom,
    UIViewContentModeLeft,
    UIViewContentModeRight,
    UIViewContentModeTopLeft,
    UIViewContentModeTopRight,
    UIViewContentModeBottomLeft,
    UIViewContentModeBottomRight,
};

獲取軟件安裝包資源路徑

# 導(dǎo)入播放器框架
#import <AVFoundation/AVFoundation.h>
// 獲取軟件的安裝包
NSBundle *bundle = [NSBundle mainBundle];
// 獲取安裝包某一個(gè)資源的路徑
NSURL *url = [bundle URLForResource:@"fileName" withExtension:@"mp3"];
// 創(chuàng)建 AVPlayer
AVPlayer *player = [AVPlayer playerWithURL:url];
// 播放
[player play];

圖片內(nèi)存問題

# 緩存圖片只能訪問`Assets.xcassets`
UIImage *cachedImage = [UIImage imageNamed:@"fileName"];
# 不緩存圖片,不放在`Assets.xcassets`下,放在項(xiàng)目的其他位置,會(huì)打包到`NSBundle`里  
//MARK:OfFile:代表資源的全路徑
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"fileName" ofType:@"mp3"];
UIImage *noCachedImage = [UIImage imageWithContentsOfFile:imagePath];
Assets.png

xib

.c -> 編譯 -> .o
.xib -> 編譯 -> nib

# nib里可以有很多個(gè)view  
# 加載xib方式一
NSArray * viewArray1 = [[NSBundle mainBundle] loadNibNamed:@"contentView" owner:nil options:nil];
UIView *view = array.firstObject;
        
# 加載xib方式二
UINib *nib =  [UINib nibWithNibName:@"contentView" bundle:[NSBundle mainBundle]];  
NSArray *viewArry2 = [nib instantiateWithOwner:nil options:nil];
UIView *view = viewArry2.firstObject;
# MARK: 如果一個(gè)控件是從xib或者storyboard創(chuàng)建出來的,初始化的時(shí)候一定調(diào)用initWithCoder:這個(gè)方法,但是拿到的控件可能會(huì)空的不能再這里做初始化操作。
# MARK: 通過該方式創(chuàng)建非xib的自定義控件,可以被其他的xib文件中引用該控件
- (instancetype)initWithCoder:(NSCoder *)coder {
    if ([super initWithCoder:coder]) {
        
    }
    return self;
}

# MARK: 如果一個(gè)控件是從xib或者storyboard創(chuàng)建出來的,加載完畢的時(shí)候一定調(diào)用awakeFromNib這個(gè)方法
# MARK:在這里做一些屬性的初始化操作
- (void)awakeFromNib {
    [super awakeFromNib];
}

performSegueWithIdentifier:內(nèi)部實(shí)現(xiàn)

1. 根據(jù)標(biāo)志去storyBoard中查找有沒有標(biāo)志,如果有
2. 會(huì)創(chuàng)建segue對(duì)象UIStoryboardSegue *segue
3. 給segue對(duì)象的屬性賦值      segue.sourceViewController = self
4. 給segue對(duì)象設(shè)置目的地vc   segue.destinationViewController = vc;
5. 會(huì)調(diào)用當(dāng)前控制器的方法
// 告訴segue已經(jīng)準(zhǔn)備好開始跳轉(zhuǎn),在方法里面可以拿到要跳轉(zhuǎn)的控制器,可以在這里給目標(biāo)控制器傳遞數(shù)據(jù)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
6. 真正跳轉(zhuǎn)的方法是[segue perform];方法,我們不需要手動(dòng)實(shí)現(xiàn),上邊這個(gè)方法內(nèi)部已經(jīng)處理了

iOS 應(yīng)用數(shù)據(jù)存儲(chǔ)的常用方式

1. XML  屬性列表(plist)歸檔
2. Preference(偏好設(shè)置,存放一些用戶比較小的數(shù)據(jù))本質(zhì)也是plist文件,內(nèi)部做了一層封裝
3. NSKeyedArchiver 歸檔(NSCoding) 存放自定義對(duì)象
4. SQLite
5. CoreData
iOS 每個(gè)應(yīng)用都有一個(gè)獨(dú)立的沙盒,不允許訪問別的沙盒中的數(shù)據(jù)  
Documents:`保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù), itunes同步設(shè)備時(shí)會(huì) 備份該目錄例
如,游戲應(yīng)用可將游戲存檔保存在該目錄`
Library
    Caches:`保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù), iTunes同步設(shè)備時(shí)不會(huì)備份該目錄。一般存儲(chǔ)體
積大、不需要備份的非重要數(shù)據(jù)`
    Preference:`保存應(yīng)用的所有偏好設(shè)置,iOS的 Settings(設(shè)置) 應(yīng)用會(huì)在該目錄中査找應(yīng)用的設(shè)置信息。 iTunesi同步設(shè)備時(shí)會(huì)備份該目錄`
tmp: `保存應(yīng)用運(yùn)行時(shí)所需的臨時(shí)數(shù)據(jù),使用完畢后再將相應(yīng)的文件從該目錄刪除。應(yīng)用沒有運(yùn)行時(shí),系
統(tǒng)也可能會(huì)清除該目錄下的文件。 itunes同步設(shè)備時(shí) 不會(huì)備份該目錄`

ViewController注意點(diǎn)

// 是否隱藏狀態(tài)欄
- (BOOL)prefersStatusBarHidden {
    
}

導(dǎo)航控制器注意點(diǎn)

#1. 凡是在導(dǎo)航控制器下的 scrollview,會(huì)自動(dòng)設(shè)置一個(gè)上面的內(nèi)邊距64
 self, tableview, contentinset = (64, 0, 0, 0) 
//  如果不讓它自動(dòng)設(shè)置內(nèi)邊距,可以通過以下方法取消 
self. automaticallyadjustsscrollviewinsets =  NO;
// iOS 11 新增的方法
 UIScrollView.appearance.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever
# 2. 隱導(dǎo)航控制器的導(dǎo)航條
self.navigationController.navigationbar.hidden = YES
#3. 設(shè)置導(dǎo)航條的透明度
// 導(dǎo)航條以及它里面的子控件設(shè)置透明度是沒有效果的
self.navigationcontroller.navigationbar.alpha = 0;

# 4. 設(shè)置導(dǎo)航條的背景圖片
// 設(shè)置設(shè)置導(dǎo)航條的背景圖片必須得要使用 UIBarMetricsDefault
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"barBg"] forBarMetrics:(UIBarMetricsDefault)];

//如果說導(dǎo)航條的背景圖片設(shè)置的是ni1的話,會(huì)自動(dòng)幫你創(chuàng)建一個(gè)白色的半透明圖片,設(shè)置導(dǎo)航條的背景圖片
[self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:(UIBarMetricsDefault)];
// 設(shè)置導(dǎo)航條的背景圖片為透明
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:(UIBarMetricsDefault)];
// 隱藏導(dǎo)航條下面的陰影圖片
[self.navigationController.navigationBar setShadowImage:[UIImage new]];

# 設(shè)置導(dǎo)航條的字體大小,顏色等:
  setTitleTextAttributes:

# 配置全局的導(dǎo)航條樣式 (UIView遵守了這個(gè)協(xié)議UIAppearance)
UINavigationBar *navigationBar = [UINavigationBar appearance];
# 配置指定類的導(dǎo)航條樣式,防止把系統(tǒng)的導(dǎo)航條樣式給修改掉
 [[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[UISplitViewController class]]] setBarTintColor:myColor];
 [[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[UITabBarController class], [UISplitViewController class]]] setBarTintColor:myTabbedNavBarColor];

TableView添加拉伸控件注意點(diǎn)

坐標(biāo)系正方向,水平向右、豎直向下正方向。
contentSize:內(nèi)容,滾動(dòng)范圍,所有cell的集合+頭部,尾部視圖
contentInset:額外滾動(dòng)區(qū)域,超出內(nèi)容,還可以滾動(dòng)多少
contentOffset:移量, tableview頂點(diǎn)與內(nèi)容頂點(diǎn)差值
tableview 默認(rèn)會(huì)滾動(dòng)到最上面
額外滾動(dòng)區(qū)域,不屬于內(nèi)容
導(dǎo)航控制器和tabBarController會(huì)影響contentInset。
設(shè)置tableView指示器的內(nèi)間距
tableView.scrollIndicatorInsets = UIEdgeInsetsMake(64, 0, 49,0);

1. 通過上面的方法設(shè)置導(dǎo)航條透明
2. 不讓導(dǎo)航條自動(dòng)設(shè)置內(nèi)邊距,方法同上
3. 設(shè)置TableView的內(nèi)邊距,目的在當(dāng)前試圖控制器view上添加一個(gè)可拉伸的自定義控件
tableView.contentInset = UIEdgeInsetsMake(kCustomHeaderViewHeight, 0, 0, 0);
設(shè)置后會(huì)造成tableView內(nèi)容的偏移量發(fā)生變化  kTableViewOriginOffsetY =【tableView原點(diǎn)位置減去內(nèi)容原點(diǎn)位置】
4. 計(jì)算scrollView的偏移量
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
#define kCustomHeaderViewOriginHeight 200
#define kTableViewOriginOffsetY  -244
    //1. 計(jì)算偏移量
    // scrollView.contentInset = UIEdgeInsetsMake(244, 0, 0, 0);
    // 當(dāng)前scrollView偏移量減去原始的偏移量
    CGFloat offsetY =  scrollView.contentOffset.y - kTableViewOriginOffsetY;
    
    //2.計(jì)算高度 h = 原始高度 - 偏移量
    CGFloat height = kCustomHeaderViewOriginHeight - offsetY;
    if (height <= 64) {
        height = 64;
    }
    //3. 更新高度
    self.customHeaderViewHeight = height;
    
    //4.  動(dòng)態(tài)求出alpha
    // 求變化的值
    // 最大值的方法公式 
    // 1.最大值為多少(最大值為1)
    // 2.什么情況下最大(當(dāng)offset 為136.0 最大)
        // 當(dāng)offset等于136的時(shí)候 alpha = 1
        // 當(dāng)變化的值等于固定的值的時(shí)候?yàn)樽畲?    
    // 導(dǎo)航條內(nèi)部會(huì)做一次判斷 當(dāng)設(shè)置的背景圖片的alpha == 1,這張圖片會(huì)被設(shè)置半透明
    CGFloat alpha = offsetY * 1 / 136.0;
    if (alpha >= 1) {
        alpha = 0.99;
    }
    
    // 根據(jù)顏色,生成一張圖片
    UIColor *color = [[UIColor redColor] colorWithAlphaComponent:alpha];
    UIImage *image = [UIImage imageWithColor:color];
    [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:(UIBarMetricsDefault)];

    // 直接設(shè)置self.navigationItem.titleView.alpha變化是不起效果的
    // 設(shè)置titleLabel的textColor跟隨alpha變化
    UILabel *titlLabel = (UILabel *)self.navigationItem.titleView;
    titlLabel.textColor = [UIColor colorWithWhite:0 alpha:alpha];
}

我的實(shí)現(xiàn)表頭拉伸 eg:

#define kZommedContentViewHeith 244
#define kzommedHeaderViewHeight 200
#define kTableViewOriginOffsetY -244
#define kSepViewHeight 44
@interface PersonDetailViewController ()<UITableViewDataSource,UITableViewDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UIView *zoomedHeaderView;
@property (nonatomic, strong) UIView *sepView;
@property (nonatomic, strong) UIView *zommedContentView;
@property (nonatomic, assign) CGFloat navigationHegiht;
@end

@implementation PersonDetailViewController


#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.tableView];
    [self.view addSubview:self.zommedContentView];
    [self setupNavigationDefaultConfi];
    [self setupLayoutConstraintOfTableView];

}


- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.navigationHegiht = self.navigationController.navigationBar.frame.size.height + [[UIApplication sharedApplication] statusBarFrame].size.height;
}
#pragma mark - UITableViewDataSource


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIndentifer = @"cellIndentifer"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifer forIndexPath:indexPath];
    cell.textLabel.text = @"哈哈哈哈";
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 30;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    // 計(jì)算偏移量
    CGFloat offsetY = scrollView.contentOffset.y - kTableViewOriginOffsetY;
    
    // 計(jì)算高度 原始高度 - 偏移量
    CGFloat height =  kzommedHeaderViewHeight - offsetY;
    if (height <= self.navigationHegiht) {
        height = self.navigationHegiht;
    }
    [self.zommedContentView setNeedsLayout];
    [self.zommedContentView layoutIfNeeded];
    [self.zoomedHeaderView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.height.mas_equalTo(height);
    }];
    
    // 計(jì)算alpha
    CGFloat alpha =  1 * offsetY/(kzommedHeaderViewHeight - self.navigationHegiht);
    if (alpha >= 1) {
        alpha = 0.99;
    }
    
    // 生成帶alpha的顏色
    UIColor *color =    [[UIColor whiteColor] colorWithAlphaComponent:alpha];
    // 根據(jù)顏色生成一張圖片
    UIImage *image = [UIImage imageWithColor:color];
    [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:(UIBarMetricsDefault)];
    
    
    UILabel *label = (UILabel *)self.navigationItem.titleView;
    label.textColor = [UIColor colorWithWhite:0 alpha:alpha];

}

#pragma mark - private methods

- (void)setupNavigationDefaultConfi {
    if (@available(iOS 11.0, *)) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        // Fallback on earlier versions
        self.automaticallyAdjustsScrollViewInsets = NO;
    }
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = @"個(gè)人詳情";
    self.navigationItem.titleView = titleLabel;
    [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:(UIBarMetricsDefault)];
    [self.navigationController.navigationBar setShadowImage:[UIImage new]];
    
    UIBarButtonItem *leftBarbutton = [[UIBarButtonItem alloc] initWithTitle:@"點(diǎn)我" style:(UIBarButtonItemStylePlain) target:self action:@selector(clickAction:)];
    self.navigationItem.leftBarButtonItem = leftBarbutton;
}
- (void)clickAction:(UIBarButtonItem *)barBtton {
    
}
- (void)setupLayoutConstraintOfTableView {

    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_safeAreaLayoutGuideLeft);
        make.right.equalTo(self.view.mas_safeAreaLayoutGuideRight);
        make.top.equalTo(self.view);
        make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
    }];
    
    [self.zommedContentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_safeAreaLayoutGuideLeft);
        make.right.equalTo(self.view.mas_safeAreaLayoutGuideRight);
        make.top.equalTo(self.view);
    }];

}



#pragma mark getters and setters

- (UITableView *)tableView {
    if (_tableView == nil) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:(UITableViewStylePlain)];
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellIndentifer"];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.rowHeight = 80;
        _tableView.estimatedRowHeight = 200;
        _tableView.estimatedSectionFooterHeight = 0;
        _tableView.estimatedSectionHeaderHeight = 0;
        _tableView.contentInset = UIEdgeInsetsMake(244, 0, 0, 0);

    }
    return _tableView;
}

- (UIView *)zoomedHeaderView {
    if (_zoomedHeaderView == nil) {
        _zoomedHeaderView = [UIView new];
        _zoomedHeaderView.backgroundColor = [UIColor greenColor];
        _zoomedHeaderView.frame = CGRectMake(0, 0, self.view.bounds.size.width, kzommedHeaderViewHeight);
    }
    return _zoomedHeaderView;
}
- (UIView *)sepView {
    if (_sepView == nil) {
        _sepView = [UIView new];
        _sepView.frame = CGRectMake(0, kzommedHeaderViewHeight, self.view.bounds.size.width, kSepViewHeight);
        _sepView.backgroundColor = [UIColor yellowColor];
    }
    return _sepView;
}
- (UIView *)zommedContentView {
    if (_zommedContentView == nil) {
        _zommedContentView = [UIView new];
        _zommedContentView.frame = CGRectMake(0, 0, self.view.frame.size.width, kZommedContentViewHeith);
        _zommedContentView.backgroundColor = [UIColor blackColor];
        [_zommedContentView addSubview:self.zoomedHeaderView];
        [_zommedContentView addSubview:self.sepView];
        
        [self.zoomedHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.left.right.equalTo(_zommedContentView);
         make.height.mas_equalTo(kzommedHeaderViewHeight);
        }];

        [self.sepView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.zoomedHeaderView.mas_bottom);
            make.left.right.bottom.equalTo(_zommedContentView);
            make.height.mas_equalTo(kSepViewHeight);
        }];

    }
    return _zommedContentView;
}
@end

設(shè)置半透明指示器

# 1. 設(shè)置控件本身完全不透明
# 2. 設(shè)置控件背景色半透明
UILabel *hubLabel = [[UILabel alloc] init];
hubLabel.text = @"showHubTipStr";
hubLabel.alpha = 1.0;
hubLabel.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];

AutoLayout本質(zhì)是轉(zhuǎn)換成Frame布局

萬能公式:
obj1.property1 = (obj2.property2 * multiplier) + constant value

事件是怎樣產(chǎn)生于傳遞的?

觸摸事件的傳遞是才父控件傳遞到子控件
如果父控件不能接收觸摸事件,那么子控件就不能接收到觸摸事件

一個(gè)控件什么情況下不能夠接受事件?

1. 不能接收用戶交互時(shí)不能夠處理事件 userInteractionEnabled  = NO
2. 當(dāng)一個(gè)控件隱藏的時(shí)候不能接收事件 Hidden = YES的時(shí)候
3. 當(dāng)一個(gè)控件為透明時(shí)候也不能接收事件
4. UIImageView,UILabel 默認(rèn)用戶交互是NO

如何尋找最透合的View?

1. 先判斷自己是否能能夠接收觸摸事件,如果能再繼續(xù)往下判斷
2. 在判斷觸摸的當(dāng)前的點(diǎn)在不在自己身上
3. 如果在自己身上,他會(huì)從后往前遍歷子控件,遍歷出每個(gè)子控件后,重復(fù)前面的兩個(gè)步驟
4. 如果沒有符合條件的子控件,那么它自己即使最適合的view
  

hitTest方法

hitTest方法
作用:尋找最合適的view
參數(shù):當(dāng)前是手指所在的點(diǎn),產(chǎn)生的事件
返回值:返回誰,誰就是最合適的view
什么時(shí)候調(diào)用:只要一個(gè)事件,傳遞給一個(gè)控件時(shí),就會(huì)調(diào)用這個(gè)控件的hitTest方法
-(UIVIew*)hitTest:(CGPoint)point withEvent:(UIEvent *)event

PointInside方法

PointInside方法
判斷觸摸點(diǎn)是否在控件上
以控件坐上角為坐標(biāo)原點(diǎn)

UIResponder

@implementation XLShopResponder

// 觸摸事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {}

// 加速事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {}
// 遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {}

- (instancetype)init {
    if (self = [super init]) {

    }
    return self;
}

@end

UITouch

@implementation XLShopTouch
//    返回值表示觸摸在view上的位置
//    這里返回的位置是針對(duì)view的坐標(biāo)系的(以view的左上角為原點(diǎn)(0,O) 調(diào)用時(shí)傳入的view參數(shù)為nil的話,返回的是觸摸點(diǎn)在 UIWindoow的位置
- (CGPoint)locationInView:(UIView *)view {
    return [super locationInView:view];
}
//該方法記錄了當(dāng)前試圖,前一次觸摸點(diǎn)的位置。
- (CGPoint)preciseLocationInView:(UIView *)view {
    return  [super preciseLocationInView:view];
}
@end
@implementation XLResponderView

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self];
    CGPoint prePoint = [touch preciseLocationInView:self];
    CGFloat offsetx = currentPoint.x - prePoint.x;
    CGFloat offsety = currentPoint.y - prePoint.y;
    self.transform = CGAffineTransformTranslate(self.transform, offsetx, offsety);
}

@end

UIEvent

@implementation XLShopEvent

- (instancetype)init {
    if (self = [super init]) {
        // UIEvent稱為事件對(duì)象,記錄事件產(chǎn)生的類型和時(shí)刻
        // 事件的類型
        // @property(nonatomic,readonly) UIEventType     type API_AVAILABLE(ios(3.0));
        // @property(nonatomic,readonly) UIEventSubtype  subtype API_AVAILABLE(ios(3.0));
        // 事件的時(shí)間戳
        // @property(nonatomic,readonly) NSTimeInterval  timestamp;
    }
    return self;
}

@end


@implementation XLResponderView

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self];
    CGPoint prePoint = [touch preciseLocationInView:self];
    CGFloat offsetx = currentPoint.x - prePoint.x;
    CGFloat offsety = currentPoint.y - prePoint.y;
    self.transform = CGAffineTransformTranslate(self.transform, offsetx, offsety);
}

@end

手勢(shì)

@interface XLShopGestureRecognizer ()<UIGestureRecognizerDelegate>

@end
@implementation XLShopGestureRecognizer

#pragma mark life cycle

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // 點(diǎn)擊手勢(shì)
        UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGes:)];
        [self addGestureRecognizer:tapGes];
        
        // 平移手勢(shì)
        UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
        panGes.delegate = self;
        [self addGestureRecognizer:panGes];
        // 相對(duì)于坐標(biāo)系,坐標(biāo)系有大小有方向
//        - (CGPoint)translationInView:(nullable UIView *)view;
        // 轉(zhuǎn)換后清零操作
//        - (void)setTranslation:(CGPoint)translation inView:(nullable UIView *)view
        
        // 旋轉(zhuǎn)手勢(shì)
        UIRotationGestureRecognizer *rotationGes = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationGes:)];
        rotationGes.delegate = self;
        [self addGestureRecognizer:rotationGes];
        
        // 捏合手勢(shì)
        UIPinchGestureRecognizer *pinGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinGes:)];
        pinGes.delegate = self;
        [self addGestureRecognizer:pinGes];
        
        
        // 手勢(shì)默認(rèn)是不能同時(shí)支持多個(gè)手勢(shì)的,可以通過代理設(shè)置shouldRecognizeSimultaneouslyWithGestureRecognizer 返回YES,返回多個(gè)手勢(shì)
        
    }
    return self;
}


#pragma mark - event response

- (void)tapGes:(UITapGestureRecognizer *)tap {
    
}
// 獲取的偏移量是相對(duì)于最原始的點(diǎn)
- (void)panGes:(UIPanGestureRecognizer *)pan {
    // 相對(duì)于控件原點(diǎn)的位置的偏移量有大小
//    CGPoint point = [pan locationInView:self];
    // 相對(duì)于控件坐標(biāo)系的偏移量(有大小方向)
    CGPoint point = [pan translationInView:self];
    // 方式一:
    // 使用帶make松開手第二次再次平移的時(shí)候會(huì),控件的位置又會(huì)回到原點(diǎn)的位置。
//    self.transform = CGAffineTransformMakeTranslation(point.x, point.y);
    
    // 方式二:
    self.transform = CGAffineTransformTranslate(self.transform, point.x, point.y);
    // 清零操作
    [pan setTranslation:CGPointMake(0, 0) inView:self];
}

- (void)rotationGes:(UIRotationGestureRecognizer *)rorationGes {
    // 獲取選裝角度(已經(jīng)是弧度)
    // 相對(duì)于最原始的弧度
    CGFloat roration =  rorationGes.rotation;
    self.transform = CGAffineTransformRotate(self.transform, roration);
    // 清零操作    
    [rorationGes setRotation:0];
}

- (void)pinGes:(UIPinchGestureRecognizer *)pin {
    // 放大縮小
    // 獲取縮放比例相對(duì)于最原始的比例
    CGFloat scale = pin.scale;
    self.transform = CGAffineTransformScale(self.transform, scale, scale);
    // 清零操作
    [pin setScale:1];
    
}
#pragma mark - UIGestureRecognizerDelegate

// 是否允許接收手勢(shì)事件
// 詢問delegate是否允許手勢(shì)接收者接收一個(gè)touch對(duì)象
// 返回YES,則允許對(duì)這個(gè)touch對(duì)象審核,NO,則不允許。
// 這個(gè)方法在touchesBegan:withEvent:之前調(diào)用,為一個(gè)新的touch對(duì)象進(jìn)行調(diào)用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // 相對(duì)于控件的原點(diǎn)的位置(有大小)
    CGPoint point = [touch locationInView:self];
    // 判斷點(diǎn)在控件的左邊還是右邊
    if (point.x > self.frame.size.width * 0.5) {
        // 在控件的右邊允許Target接收事件
        return  YES;
    } 
    // 在控件的左邊不允許Target接收事件
    return NO;
}

// 是否允許同時(shí)支持多個(gè)手勢(shì),系統(tǒng)默認(rèn)返回NO
// 這個(gè)函數(shù)一般在一個(gè)手勢(shì)接收者要阻止另外一個(gè)手勢(shì)接收自己的消息的時(shí)候調(diào)用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
//  詢問一個(gè)手勢(shì)接收者是否應(yīng)該開始解析執(zhí)行一個(gè)觸摸的接收事件
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePress:(UIPress *)press {
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveEvent:(UIEvent *)event API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) {
    return YES;
}
//下面這個(gè)兩個(gè)方法也是用來控制手勢(shì)的互斥執(zhí)行的
//這個(gè)方法返回YES,第一個(gè)手勢(shì)和第二個(gè)互斥時(shí),第一個(gè)會(huì)失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
//這個(gè)方法返回YES,第一個(gè)和第二個(gè)互斥時(shí),第二個(gè)會(huì)失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

@end

抽屜效果

#define kXLScreenWidth [UIScreen mainScreen].bounds.size.width
#define TargetMaxX (kXLScreenWidth * 0.8)
@interface XLShopDrawViewController ()

@property (nonatomic, strong) UIView *leftView;
@property (nonatomic, strong) UIView *mainView;
@end

@implementation XLShopDrawViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.leftView];
    [self.view addSubview:self.mainView];    
    [self openDraw];
}


#pragma mark - event response

- (void)panGes:(UIPanGestureRecognizer *)pan {
    // 結(jié)束偏移操作
    if (pan.state == UIGestureRecognizerStateEnded) {
        // 控件的相對(duì)于父控件中心點(diǎn)左邊,關(guān)閉抽屜
        if (self.mainView.frame.origin.x < kXLScreenWidth * 0.5) {
            [self closeDraw];
            return;
        } else {    
            // 控件的相對(duì)于父控件中心點(diǎn)右邊,打開抽屜    
            [self openDraw];
            return;
        }
    }
    // 相對(duì)于坐標(biāo)系的偏移量
    CGPoint point = [pan translationInView:self.mainView];
    [self positionWithOffsetX:point.x];
    [pan setTranslation:CGPointZero inView:self.mainView];
}
#pragma mark - publickMethod
- (void)closeDraw {
    [UIView animateWithDuration:0.25 animations:^{
        // 清空形變
        self.mainView.transform = CGAffineTransformIdentity;
        // 位置復(fù)位
        self.mainView.frame = self.view.bounds;
    }];
}

- (void)openDraw {
    [UIView animateWithDuration:0.25 animations:^{
        CGFloat offset =  TargetMaxX - self.mainView.frame.origin.x;
         [self positionWithOffsetX:offset];
        CGFloat maxX = TargetMaxX;
        CGRect targetFrame = self.mainView.frame;
        targetFrame.origin.x = maxX;
        self.mainView.frame = targetFrame;
    }];
}
#pragma mark - private methods

// 根據(jù)原始偏移量,計(jì)算當(dāng)前位置大小
- (void)positionWithOffsetX:(CGFloat)offsetX {
    void(^animatonBlock)(void) = ^ {
       // 1. 控件偏移量修改
        // 方式一:修改frame
        // 方式二:做transform形變
        //  self.mainView.transform = CGAffineTransformTranslate(self.mainView.transform, offsetX, 0);
        // 修改mainView的frame 
        CGRect  fram = self.mainView.frame;
        fram.origin.x+= offsetX;    
        // 防止偏移量導(dǎo)致控件的位置偏移到屏幕的左邊
        if (fram.origin.x <= 0) {
            self.mainView.frame = self.view.bounds;
            return;
        }
        self.mainView.frame = fram;
        
        
        //2. 控件位置做縮放
        // 原始比例:1
        // 0.9 0.8 0.7
        // 1- 0.1 = 0.9
        // 1- 0.2 = 0.8
        // 1- 0.3 = 0.7
        // 縮放
        // 找最大值 最大值是0.3
        
        CGFloat scale = self.mainView.frame.origin.x  * 0.3 / kXLScreenWidth;
        scale = 1 - scale;
        // 做縮放操作
        self.mainView.transform = CGAffineTransformMakeScale(scale, scale);  
    };
    [UIView animateWithDuration:0.25 animations:animatonBlock];
    
}
#pragma mark - getters and setters
- (UIView *)leftView {
    if (_leftView == nil) {
        _leftView = [UIView new];
        _leftView.backgroundColor = [UIColor redColor];
        _leftView.frame = self.view.bounds;
    }
    return _leftView;
}

- (UIView *)mainView {
    if (_mainView == nil) {
        _mainView = [UIView new];
        _mainView.backgroundColor = [UIColor blueColor];
         UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
        [_mainView addGestureRecognizer:panGes];
        _mainView.frame = self.view.bounds;
    }
    return _mainView;
}

@end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容