探索iPhone控制中心交互實現(xiàn)

【項目Github地址】
先展示一張效果圖:

Untitled.gif

從圖片上可以看出都是彈簧效果,自然而然就會想到用系統(tǒng)的UIScrollView類來實現(xiàn)。
接下來我將仔細的一步步的教你完成這個項目。
首先咱們先創(chuàng)建一個工程,打開ViewController.m文件,我們先簡單創(chuàng)建一個UIScrollView,讓他的contentSize為兩倍的屏幕寬度,再加入兩張圖片。
代碼如下:

#import "ViewController.h"

#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
#define kLeftRightMargin 8
#define kBottomMargin 16
#define kImageViewWidth (kScreenWidth - kLeftRightMargin*2)
#define kImageViewHeight (kImageViewWidth*392/344.5)
#define kImageViewY (kScreenHeight - kBottomMargin - kImageViewHeight)

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIImageView *leftView;
@property (nonatomic, strong) UIImageView *rightView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor lightGrayColor];
    self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    self.scrollView.delegate = self;
    self.scrollView.contentSize = CGSizeMake(kScreenWidth*2, kScreenHeight);
    self.scrollView.pagingEnabled = YES;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.showsVerticalScrollIndicator = NO;
    [self.view addSubview:self.scrollView];
    
    self.leftView = [[UIImageView alloc] initWithFrame:CGRectMake(kLeftRightMargin, kImageViewY, kImageViewWidth, kImageViewHeight)];
    self.leftView.backgroundColor = [UIColor yellowColor];
    self.leftView.layer.cornerRadius = 15;
    self.leftView.layer.masksToBounds = YES;
    [self.scrollView addSubview:self.leftView];
    
    self.rightView = [[UIImageView alloc] initWithFrame:CGRectMake(kLeftRightMargin+kScreenWidth, kImageViewY, kImageViewWidth, kImageViewHeight)];
    self.rightView.backgroundColor = [UIColor greenColor];
    self.rightView.layer.cornerRadius = 15;
    self.rightView.layer.masksToBounds = YES;
    [self.scrollView addSubview:self.rightView];
}
@end

運行之后,你將看到如下圖的效果:


初始化工程.gif

但是你們也會發(fā)現(xiàn)有如下的反應,向上向下并沒有彈性效果:


上下沒有彈性.gif

因為此時scrollView的高度和它contentSize的高度是相同的,所以它在橫向上是可以滾動的,而縱向上就不可以了。但是我們想要縱向也有彈性效果,腫么辦?如果你熟悉UIScrollView,你就曉得有一個屬性可以讓它簡單實現(xiàn)了。
咱們在創(chuàng)建scrollView的地方加入一行代碼:

...
self.scrollView.alwaysBounceVertical = YES; // <--
[self.view addSubview:self.scrollView];

效果圖如下:

上下彈性.gif

但此時可能你會發(fā)現(xiàn)另一個問題,此時的scrollView滾動的方向沒有限制:

方向無限制.gif

那咱們就去UIScrollView頭文件里去瞧瞧,說不定蘋果爸爸已經(jīng)給我們預設了解決辦法了也說不定。
很快你就會看到有這樣一個屬性定義

@property(nonatomic,getter=isDirectionalLockEnabled) BOOL directionalLockEnabled;         // default NO. if YES, try to lock vertical or horizontal scrolling while dragging

那就用起來:

    self.scrollView.alwaysBounceVertical = YES;
    self.scrollView.directionalLockEnabled = YES; // <--
    [self.view addSubview:self.scrollView];

突然很神奇般的,好像是有效了。至少我在iOS10.3的模擬器上是沒問題,但是如果你在真機上運行的話,試試你就會發(fā)現(xiàn),當你滑動角度在45°左右的時候,前面設置的方向鎖屬性就不生效沒用了。

仍然存在問題.gif

蘋果官方也承認這個是個BUG了。在stackoverflow上也有在討論這個問題。
我在上面選擇了一個解決方式,實現(xiàn)UIScrollViewdelegate,代碼如下:

@interface ViewController () <UIScrollViewDelegate>
...
@property (nonatomic, assign) CGPoint beginDragPoint;
@end
@implementation ViewController
...
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _beginDragPoint = scrollView.contentOffset;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.contentOffset.x == _beginDragPoint.x) {
        self.scrollView.contentOffset = CGPointMake(_beginDragPoint.x, (self.scrollView.contentOffset.y));
    } else {
        self.scrollView.contentOffset = CGPointMake((self.scrollView.contentOffset.x), _beginDragPoint.y);
    }
}
@end

接下來說說另一個問題,手勢向下移動過程中,手指移動多少視圖就移動多少,而我們寫的工程卻還是彈性的。

系統(tǒng)向下移.gif

工程向下移.gif

我的思路就是,那么就讓scrollViewcontentSize高度大于本身的高度吧,那么也能手移動多少,他就移動多少了。那高度設為多少合適呢?你可以先自己嘗試看看,我最終的結(jié)果是兩倍的本身高度,感覺非常巧妙,最后視圖消失與否都通過pagingEnabled已經(jīng)搞定了。
修改代碼如下:

// #define kImageViewY (kScreenHeight - kBottomMargin - kImageViewHeight)
#define kImageViewY (kScreenHeight*2 - kBottomMargin - kImageViewHeight)

    self.scrollView.delegate = self;
//    self.scrollView.contentSize = CGSizeMake(kScreenWidth*2, kScreenHeight);
    self.scrollView.contentSize = CGSizeMake(kScreenWidth*2, kScreenHeight*2); // <--
    self.scrollView.contentOffset = CGPointMake(0, kScreenHeight); // <--
    self.scrollView.pagingEnabled = YES;

效果圖如下:

消失出現(xiàn).gif

好了,基本大功告成了。但是還有一個致命的問題,一般人找不到解決辦法,我也是不斷嘗試才發(fā)現(xiàn)原來還可以這么整。
問題就是,當手指在非視圖區(qū)域移動時,視圖是不會動不會有反應的,一旦移動到靠近視圖的時候,視圖才開始移動的。如圖:

非視圖區(qū).gif

先分析一下,UIScrollView的滾動時因為內(nèi)部封裝的pan手勢,那我們可不可以拿到手勢調(diào)用的方法,重寫它,然后判斷如果手指沒有到達位置時,手勢的UIGestureRecognizerStateChanged事件就不傳遞給父類,父類就不能處理,那么視圖就不會移動了。
我們先來找找事件方法:

WechatIMG10.jpeg

打斷點發(fā)現(xiàn),手勢屬性里有個SEL方法handlePan:,嗯,應該就是它了,創(chuàng)建一個UIScrollView的子類重寫這個方法吧。

// 創(chuàng)建類
@interface LYScrollView : UIScrollView
@end
@implementation LYScrollView
- (void)handlePan:(UIPanGestureRecognizer *)pan {
    NSLog(@"%s", __func__);
}
@end
.
.
#import "LYScrollView.h"
// 替換創(chuàng)建方法
// @property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) LYScrollView *scrollView;
...
// self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.scrollView = [[LYScrollView alloc] initWithFrame:self.view.bounds];

很是令人欣慰啊,方法回調(diào)了。


屏幕快照 2017-03-06 上午11.03.54.png

可是問題又出現(xiàn)了,當滿足條件的時候,我要把事件還給父類的,可是編譯器通過不了:

屏幕快照 2017-03-06 上午11.07.20.png

(個人想的比較挫的解決方法啊,有好的方式麻煩告知下哈。)
再創(chuàng)建一個中間類,聲明一個handlePan:方法。。。

@interface MyScrollView : UIScrollView
- (void)handlePan:(UIPanGestureRecognizer *)pan;
@end
@interface LYScrollView : MyScrollView
@end
// 去除`.m`文件因為沒有實現(xiàn)該方法而有的警告問題
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
@implementation MyScrollView
#pragma clang diagnostic pop
@end

最終方法實現(xiàn)如下:

@implementation LYScrollView {
    BOOL _canMove;
}

- (void)handlePan:(UIPanGestureRecognizer *)pan {
    CGPoint point = [pan locationInView:pan.view];
    
    if (pan.state == UIGestureRecognizerStateBegan) {
        [pan setTranslation:CGPointZero inView:pan.view];
        [super handlePan:pan];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        
        // 一旦手指位置到達視圖時,則開始移動
        if (!_canMove && point.y > kImageViewY) {
            _canMove = YES;
            [pan setTranslation:CGPointZero inView:pan.view];
        }
        
        if (_canMove) {
            [super handlePan:pan];
        }
        
    } else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
        _canMove = NO;
        [pan setTranslation:CGPointZero inView:pan.view];
        [super handlePan:pan];
    }
}
@end

這邊需要[pan setTranslation:CGPointZero inView:pan.view];調(diào)用一下,將手勢的位置初始化為零后,再傳給父類,不然就位置突變了。

最終效果.gif

大問題都解決完啦,剩下的背景色漸變、出現(xiàn)消失、再封裝等實現(xiàn)就不在這里贅述了,可去看下工程代碼。

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

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

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,422評論 4 61
  • 持續(xù)一周多,終于把自己的個人成長經(jīng)歷寫完了。這是第四次寫了吧。記得第一次寫,是做咨詢,寫到動情處,哭的稀里嘩啦。第...
    綻蕊向陽閱讀 1,954評論 4 1
  • 要明白什么是跨域,先要了解什么是同源策略,它的含義是指: A網(wǎng)頁設置的 Cookie,B網(wǎng)頁不能打開,除非這兩個網(wǎng)...
    書中有涼氣閱讀 850評論 0 50
  • 一個人的財富多少,個人的分析能力是重要決定因素之一的。一個人擁有分析能力,不管起點多低或高,都有機會獲得財富。 之...
    Amanda_w閱讀 375評論 0 1
  • 1.你的未來五年的職業(yè)規(guī)劃是什么 我的短期職業(yè)規(guī)劃是找到一份工作,并努力勝任這份工作。長期的職業(yè)規(guī)劃是,希望通過在...
    mengyudezheng閱讀 343評論 1 3

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