iOS-關於一些手勢衝突問題(scrollView 嵌套 tableView)

来源:https://www.cnblogs.com/wangkejia/archive/2019/06/25/11081577.html
-Advertisement-
Play Games

簡單說下關於開發中容易遇到的父試圖添加手勢與子試圖點擊事件衝突,UIScrollView 嵌套 UIScrollView 、 UIScrollView 嵌套 UITableView的情況手勢衝突問題; 點擊衝突 如果給現有的基於 UIView 的 xkTestView 上加一個點擊手勢 gestTa ...


簡單說下關於開發中容易遇到的父試圖添加手勢與子試圖點擊事件衝突,UIScrollView 嵌套 UIScrollView 、 UIScrollView 嵌套 UITableView的情況手勢衝突問題;

點擊衝突

如果給現有的基於 UIView 的 xkTestView 上加一個點擊手勢 gestTap,然後在 xkTestView 中間區域添加一個 tableview,我們想響應 gestTap,同時也想響應 tableview 的 cell 點擊代理事件,這時可以添加 gestTap 點擊手勢代理:

<UIGestureRecognizerDelegate>

然後在點擊事件代理方法中實現

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([NSStringFromClass([touch.view class]) isEqualToString:@"xkTestView"]) {
        return YES;
    }
    return  NO;
}

 

scrollView 嵌套 tableView 類衝突

這裡直接用 scrollView 嵌套 tableView 來處理下滑動時的手勢衝突問題,其實蘋果並不建議我們這樣做,但是在實際項目中,有些需求會經常用嵌套來實現,在什麼情況下滑動 tableView 不滑動 scrollView,什麼情況下滑動 scrollView 不滑動 tableView,其實如果做其他的嵌套都是一樣的,先看下最終效果圖:

 

1)首先新建一個基於 UIScrollView 的 XKBaseScrollView ,並實現 <UIGestureRecognizerDelegate> 代理,XKBaseScrollView 用做主父試圖來添加子試圖內容

 XKBaseScrollView.h

#import <UIKit/UIKit.h>

@interface XKBaseScrollView : UIScrollView <UIGestureRecognizerDelegate>

@end

 

XKBaseScrollView.m

#import "XKBaseScrollView.h"

@implementation XKBaseScrollView

//是否支持多時候觸發,這裡返回YES
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

@end

 

2)然後新建一個基於 UITableView 的 XKTargetTableView ,並實現 <UIGestureRecognizerDelegate,UITableViewDelegate,UITableViewDataSource> 代理

XKTargetTableView.h

#import <UIKit/UIKit.h>


@interface XKTargetTableView : UITableView
///可否滑動
@property (nonatomic,assign) BOOL canSlide;
///滑動block通知
@property (nonatomic,copy) void (^slideDragBlock)(void);
@end

 

XKTargetTableView.m

#import "XKTargetTableView.h"
@interface XKTargetTableView ()<UIGestureRecognizerDelegate,UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,assign) CGFloat currOffsetY;
@end
@implementation XKTargetTableView

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{
    self = [super initWithFrame:frame style:style];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
        self.delegate = self;
        self.dataSource = self;
        self.tableFooterView = [UIView new];
        [self registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
    }
    return self;
}

//是否支持多時候觸發,這裡返回YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

#pragma mark ========== tableView 代理 ==========
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 20;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 50;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
    return cell;
}

#pragma mark ========== scrollview 代理 ==========
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    _currOffsetY = scrollView.contentOffset.y;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (!self.canSlide) {
        scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y == 0 ? 0 : _currOffsetY);
    }
    _currOffsetY = scrollView.contentOffset.y;
    if (scrollView.contentOffset.y < 0 ) {
        self.canSlide = NO;
        scrollView.contentOffset = CGPointZero;
        //到頂通知父視圖改變狀態
        if (self.slideDragBlock) {
            self.slideDragBlock();
        }
    }
    scrollView.showsVerticalScrollIndicator = self.canSlide ? YES : NO;
}
@end

 

3)最後在使用的 ViewController 中實現

#import "ViewController.h"
#import <SDAutoLayout.h>

#import "XKBaseScrollView.h"
#import "XKTargetTableView.h"

@interface ViewController ()<UIScrollViewDelegate>
///容器
@property (nonatomic,strong) XKBaseScrollView *scrollView;
@property (nonatomic,strong) XKTargetTableView *tableView;
///是否可以滑動 scrollView
@property (nonatomic,assign) BOOL canSlide;
@property (nonatomic,assign) CGFloat lastPositionY;
///滑動臨界範圍值
@property (nonatomic,assign) CGFloat dragCriticalY;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _dragCriticalY = 200;
    [self.view addSubview:self.scrollView];
    self.scrollView.sd_layout.
    topSpaceToView(self.view, 0).
    leftSpaceToView(self.view, 0).
    rightSpaceToView(self.view, 0).
    bottomSpaceToView(self.view, 0);
    
    [self.scrollView setupAutoContentSizeWithBottomView:self.tableView bottomMargin:0];
    __weak __typeof__(self) weekSelf = self;
    self.tableView.slideDragBlock = ^{
        weekSelf.canSlide = YES;
        weekSelf.tableView.canSlide = NO;
    };
    
}


-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat currentPostion = scrollView.contentOffset.y;
    /*
     當 底層滾動式圖滾動到指定位置時,
     停止滾動,開始滾動子視圖
     */
    if (currentPostion >= self.dragCriticalY) {
        scrollView.contentOffset = CGPointMake(0, self.dragCriticalY);
        if (self.canSlide) {
            self.canSlide = NO;
            self.tableView.canSlide = YES;
        }
        else{
            if (_lastPositionY - currentPostion > 0){
                if (self.tableView.contentOffset.y > 0) {
                    self.tableView.canSlide = YES;
                    self.canSlide = NO;
                }
                else{
                    self.tableView.canSlide = NO;
                    self.canSlide = YES;
                }
            }
        }
    }else{
        if (!self.canSlide && scrollView.contentOffset.y ==  self.dragCriticalY ) {
            scrollView.contentOffset = CGPointMake(0, self.dragCriticalY);
        }
        else{
            if (self.tableView.canSlide &&
                self.tableView.contentOffset.y != 0) {
                scrollView.contentOffset = CGPointMake(0, self.dragCriticalY);
            }
            else{
                
            }
        }
    }
    
    _lastPositionY = currentPostion;
}

- (XKBaseScrollView *)scrollView{
    if (!_scrollView) {
        _scrollView = [[XKBaseScrollView alloc]init];
        _scrollView.showsVerticalScrollIndicator = NO;
   
        _scrollView.delegate = self;
        _scrollView.backgroundColor = [UIColor redColor];
        UIView *view = [[UIView alloc]init];
        view.backgroundColor = [UIColor blueColor];
        
        [_scrollView addSubview:view];
        view.sd_layout.
        topSpaceToView(_scrollView, 0).
        leftSpaceToView(_scrollView, 0).
        rightSpaceToView(_scrollView, 0).
        heightIs(300);
        
        [_scrollView addSubview:self.tableView];
        self.tableView.sd_layout.
        topSpaceToView(view, 0).
        leftSpaceToView(_scrollView, 0).
        rightSpaceToView(_scrollView, 0).
        heightIs(self.view.bounds.size.height - (300 - self.dragCriticalY));
    }
    return _scrollView;
}
- (XKTargetTableView *)tableView{
    if(!_tableView){
        _tableView = [[XKTargetTableView alloc]initWithFrame:CGRectZero style:UITableViewStylePlain];
    }
    return _tableView;
}
@end

 

註:此 demo 需引用 SDAutoLayout


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、更改MySQL配置文件my.cnf,跳過密碼驗證。 編輯配置文件/etc/my.cnf文件,在[mysqld]下麵添加skip-grant-tables,保存退出。如圖: vim /etc/my.cnf 保存退出。 重啟mysql服務:service mysqld restart。 二、進入My ...
  • Redis 事務可以一次執行多個命令。 常用命令: multi //開起一個事務,標記一個事務塊的開始,multi即multiple exec //執行事務塊內的命令 discard //取消事務 使用步驟: 先使用 multi命令 標記事務開始 將多個命令入隊(queue,隊列) 使用 exec ...
  • # mysql主從複製邏輯: 1.從庫執行start slave 開啟主從複製。 2.從庫請求連接到主庫,並且指定binlog文件以及位置後發出請求。 3.主庫收到從庫請求後,將信息返回給從庫,除了信息日誌外,還包含新的文件名稱以及下一個更新節點。 4.從庫接收到主庫發送的信息後,會將信息更新至自身... ...
  • Android開發經常需要使用Context來啟動Activity,或者打開SharedPreferences,或者構建一個Dialog。最近老是用到getContext(),getApplicationContext(),this等,來獲取Context,故寫此文來理清思路。確定好需要Contex ...
  • 1.plist文件 即屬性列表文件,全名是Property List,這種文件的擴展名為.plist,因此,通常被叫做plist文件。它是一種用來存儲串列化後的對象的文件,用於存儲程式中經常用到且數據量小而不經常改動的數據。 可以存儲的類型:NSNumber,NSString,NSDate,NSDa ...
  • Allwinner VR9應用處理器是一款專為VR&AR市場設計的64位四核SOC。它基於ARMCortex A53,並具有高水平的系統集成和更多功能強大的視頻回放功能和更低的功耗相結合。此外,全志VR9解決方案有一個專用的ATW硬體加速模塊,以減少延遲。它還支持空間音頻。所有這些設計考慮將給最終用 ...
  • Hi3798MV310是用於IPTV/OTT機頂盒市場的支持4KP60 解碼的超高清高性能SOC晶元。集成4核64位高性能Cortex A53處理器和多核高性能 2D/3D加速引擎;支持H.265/AVS2 4Kx2K@P60 10bit 超高清視頻解碼,高性能的 H.265 高清視頻編碼,HDR視 ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/144 xlog的優點 在開發過程中,避免不了要使用日誌組件,用來記錄程式執行過程中一些關鍵節點的日誌,出了問題,我們可以根據日誌信息,快速定位問題。 對了本文 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...