Effet de plafond iOS

Dans le projet, lorsque la liste défile vers le haut, il faut parfois placer un certain contrôle en haut, ce qui est notre effet de plafond commun.

1. Effet de plafond UITableView

UITableViewIl a son propre effet de plafond. Nous définissons le contrôle qui doit être en haut sur SectionHeaderView, de sorte que lors du défilement, le contrôle soit automatiquement en haut.

- (UITableView *)tableView {
    
    
    if (!_tableView) {
    
    
        _tableView = [[UKNestedTableView alloc] init];
        
        _tableView.bounces = NO;
        _tableView.showsVerticalScrollIndicator = NO;

        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CellId"];
    }
    return _tableView;
}

#pragma mark - UITableViewDataSource -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
    
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    
    if (section == 0) {
    
    
        return 1;
    }
    return 20;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    if (indexPath.section == 0) {
    
    
        return 150;
    }
    return 60;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    
    
    if (section == 1) {
    
    
        UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 50)];
        headerView.backgroundColor = [UIColor blueColor];
        return headerView;
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    
    
    if (section == 1) {
    
    
        return 50;
    }
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId" forIndexPath:indexPath];
    
    if (indexPath.section == 0) {
    
    
        cell.backgroundColor = [UIColor yellowColor];
        cell.textLabel.text = @"section 0";
    } else {
    
    
        if (indexPath.row % 2 == 0) {
    
    
            cell.backgroundColor = [UIColor grayColor];
        } else {
    
    
            cell.backgroundColor = [UIColor whiteColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"item - %ld", indexPath.row];
    }
    
    return cell;
}

PersonnaliserUKNestedTableView

@implementation UKNestedTableView

- (instancetype)init {
    
    
    self = [super initWithFrame:CGRectZero style:UITableViewStylePlain];

    if (self) {
    
    
        self.backgroundColor = [UIColor whiteColor];
        self.separatorColor = [UIColor clearColor];
        
        self.separatorStyle = UITableViewCellSeparatorStyleNone;
                
        if (@available(iOS 11.0, *)) {
    
    
            self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        }

        self.estimatedRowHeight = 0.000;
        self.estimatedSectionHeaderHeight = 0.000;
        self.estimatedSectionFooterHeight = 0.000;
        
        if (@available(iOS 13.0,*)) {
    
    
            self.automaticallyAdjustsScrollIndicatorInsets = NO;
        }

        if (@available(iOS 15.0,*)) {
    
     // 去除表格头留白
            self.sectionHeaderTopPadding = YES;
         }
    }
    return self;
}

@end

L'effet est le suivant

insérez la description de l'image ici

2. Effet plafond avec TabView

UITableViewL'effet de plafond peut répondre à certaines des exigences, mais dans les applications pratiques, ce sont souvent des pages à onglet qui doivent être placées en haut, correspondant à plusieurs listes.

Nous utilisons UKTabView comme contrôle supérieur et correspondons à plusieurs contenus.

- (UKTabView *)tabView {
    
    
    if (!_tabView) {
    
    
        _tabView = [[UKTabView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 50)];
        [_tabView setIndicatorWidth:80 height:2 radius:1 color:[UIColor blueColor]];
        
        UKCustomTabItemView *tabItemView1 = [[UKCustomTabItemView alloc] init];
        [tabItemView1 setText:@"选项1"];
        [_tabView addItemView:tabItemView1];
        
        UKCustomTabItemView *tabItemView2 = [[UKCustomTabItemView alloc] init];
        [tabItemView2 setText:@"选项2"];
        [_tabView addItemView:tabItemView2];
        
        _tabView.delegate = self;
        
        [_tabView setSelection:0];
    }
    return _tabView;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    
    
    if (section == 1) {
    
    
        return self.tabView;
    }
    return nil;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId" forIndexPath:indexPath];
    if (indexPath.section == 0) {
    
    
        cell.backgroundColor = [UIColor yellowColor];
        cell.textLabel.text = @"section 0";
    } else {
    
    
        if (indexPath.row % 2 == 0) {
    
    
            if (self.selection == 0) {
    
    
                cell.backgroundColor = [UIColor grayColor];
            } else {
    
    
                cell.backgroundColor = [UIColor darkGrayColor];
            }
        } else {
    
    
            cell.backgroundColor = [UIColor whiteColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"item %ld - %ld", self.selection, indexPath.row];
    }
    
    return cell;
}

#pragma mark - UKTabViewDelegate -
- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position {
    
    
    self.selection = position;
    [self.tableView reloadData];
}

L'effet est le suivant
insérez la description de l'image ici

La méthode ci-dessus implémente simplement les fonctions de recouvrement d'onglet et de changement d'onglet, mais comme nous ne pouvons partager qu'une seule liste, les deux pages à onglet défilent.

Pour ce faire, nous devons optimiser le décalage de défilement, enregistrer d'abord le décalage à la fin du défilement, puis définir le décalage d'origine lors du changement d'onglet.

@property(nonatomic, assign) NSInteger selection;
@property(nonatomic, assign) CGFloat tab1Offset;
@property(nonatomic, assign) CGFloat tab2Offset;

// 拖动结束
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    
    
    NSLog(@"scrollViewDidEndDragging");

    [self recordOffset:scrollView];
}

// 滚动结束
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
    
    NSLog(@"scrollViewDidEndDecelerating");

    [self recordOffset:scrollView];
}

- (void)recordOffset:(UIScrollView *)scrollView {
    
    
    if (self.selection == 0) {
    
    
        self.tab1Offset = scrollView.contentOffset.y;
        NSLog(@"tab1Offset = %.2f", self.tab1Offset);
    } else if (self.selection == 1) {
    
    
        self.tab2Offset = scrollView.contentOffset.y;
        NSLog(@"tab2Offset = %.2f", self.tab2Offset);
    }
}

Lorsque vous changez d'onglet, définissez le décalage réel

- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position {
    
    
    self.selection = position;
    [self.tableView reloadData];

    // 有时设置tableView.contentOffset无效,需要提前刷新
    [self.tableView layoutIfNeeded];
    if (position == 0) {
    
    
        self.tableView.contentOffset = CGPointMake(0, self.tab1Offset);
    } else if (position == 1) {
    
    
        self.tableView.contentOffset = CGPointMake(0, self.tab2Offset);
    }
}

L'effet est le suivant
insérez la description de l'image ici

Bien que nous ayons enregistré le décalage d'origine, à partir de l'effet réel, TabViewil sera à la même position lors de la commutation et le scintillement est grave. Pour cela, nous devons essayer de maintenir TabViewla position.

- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position {
    
    
    self.selection = position;
    [self.tableView reloadData];

    [self.tableView layoutIfNeeded];
    if (position == 0) {
    
    
        self.tab1Offset = [self getDestOffset:self.tab1Offset originOffset:self.tab2Offset];
        self.tableView.contentOffset = CGPointMake(0, self.tab1Offset);
    } else if (position == 1) {
    
    
        self.tab2Offset = [self getDestOffset:self.tab2Offset originOffset:self.tab1Offset];
        self.tableView.contentOffset = CGPointMake(0, self.tab2Offset);
    }
}

// 如果TabView已经置顶,切换时保持置顶。
// 1、如果切换后的内容已经置顶,保持原有效果
// 2、如果切换后的内容没有置顶,修改切换后的内容为置顶
// 如果TabView没有制度,切换后保持一致
- (CGFloat)getDestOffset:(CGFloat)destOffset originOffset:(CGFloat)originOffset {
    
    
    if (originOffset >= 150) {
    
    
        if (destOffset >= 150) {
    
    
            return destOffset;
        } else {
    
    
            return 150;
        }
    } else {
    
    
        return originOffset;
    }
}

L'effet est le suivant
insérez la description de l'image ici

Bien que la solution actuelle ait résolu la plupart des exigences, il reste encore quelques défauts.

  1. Le contenu ne peut être UIScrollViewaffiché qu'avec
  2. Afin de conserver UKTableViewla même position, la position décalée du contenu n'est pas entièrement garantie.
  3. Si un contenu est court, il y aura toujours un problème de décalage. Bien que nous puissions améliorer ce problème en remplissant le contenu vide, cela ajoute beaucoup de travail.
  4. Il n'y a pas d'effet fluide lors du changement de contenu.

3. Imbrication UITableView + UICollectionView

Afin de parfaire au maximum notre effet de plafond, nous essayons d'utiliser UITableView+UICollectionViewla combinaison pour obtenir deux effets de plafond et de coulissement gauche et droite.

nous personnalisonsUKNestedScrollView

@interface UKNestedScrollView()

@property(nonatomic, strong) NSMutableArray <UITableView *> *contentViewArray;
@property(nonatomic, assign) BOOL dragging;

@end

@implementation UKNestedScrollView

- (instancetype)initWithFrame:(CGRect)frame {
    
    
    self = [super initWithFrame:frame];
    if (self) {
    
    
        [self setupInitialUI];
    }
    return self;
}

// 设置表头
- (void)setHeaderView:(UIView *)headerView {
    
    
    self.tableView.tableHeaderView = headerView;
    self.headerHeight = headerView.frame.size.height;
}

// 添加标签页和内容
- (void)addTabView:(UKTabItemView *)itemView contentView:(UITableView *)contentView {
    
    
    [self.tabView addItemView:itemView];
    
    [self.contentViewArray addObject:contentView];
    
    [self.collectionView reloadData];
}


- (void)setupInitialUI {
    
    
    // UKNestedScrollView包含一个UITableView
    [self addSubview:self.tableView];
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
        make.left.right.top.bottom.equalTo(self);
    }];
}

- (UITableView *)tableView {
    
    
    if (!_tableView) {
    
    
        _tableView = [[UKNestedTableView alloc] init];
        
        _tableView.bounces = NO;
        _tableView.showsVerticalScrollIndicator = NO;

        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CellId"];
    }
    return _tableView;
}

- (UITableView *)tableView {
    
    
    if (!_tableView) {
    
    
        _tableView = [[UKNestedTableView alloc] init];
        
        _tableView.bounces = NO;
        _tableView.showsVerticalScrollIndicator = NO;

        _tableView.delegate = self;
        _tableView.dataSource = self;
        
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"CellId"];
    }
    return _tableView;
}

// SectionHeaderView包含UKTabView和UICollectionView
- (UIView *)sectionHeaderView {
    
    
    if (!_sectionHeaderView) {
    
    
        _sectionHeaderView = [[UIView alloc] initWithFrame:self.frame];
        
        [_sectionHeaderView addSubview:self.tabView];
        
        [_sectionHeaderView addSubview:self.collectionView];
    }
    return _sectionHeaderView;
}

- (UKTabView *)tabView {
    
    
    if (!_tabView) {
    
    
        _tabView = [[UKTabView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 50)];
        [_tabView setIndicatorWidth:80 height:2 radius:1 color:[UIColor blueColor]];

        _tabView.delegate = self;
    }
    return _tabView;
}

- (UICollectionView *)collectionView {
    
    
    if (!_collectionView) {
    
    
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        layout.itemSize = CGSizeMake(self.frame.size.width, self.frame.size.height - 50);
        layout.minimumLineSpacing = CGFLOAT_MIN;
        layout.minimumInteritemSpacing = CGFLOAT_MIN;
        
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 50, self.frame.size.width, self.frame.size.height - 50) collectionViewLayout:layout];
        _collectionView.pagingEnabled = YES;
        _collectionView.bounces = NO;
        _collectionView.showsHorizontalScrollIndicator = NO;

        _collectionView.dataSource = self;
        _collectionView.delegate = self;
        
        [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    }
    return _collectionView;
}

#pragma mark - UITableViewDataSource -
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    
    
    return self.frame.size.height;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    
    
    return self.sectionHeaderView;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    return [[UITableViewCell alloc] init];
}

#pragma mark - UICollectionViewDataSource -
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    
    
    return self.contentViewArray.count;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    
    UITableView *contentView = self.contentViewArray[indexPath.row];
    [contentView removeFromSuperview];
    
    [cell.contentView addSubview:contentView];
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
        make.left.right.top.bottom.equalTo(cell.contentView);
    }];
    
    return cell;
}


#pragma mark - UIScrollViewDelegate -
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    
    
    if (scrollView == self.collectionView) {
    
    
        self.dragging = YES;
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    
    if (scrollView == self.collectionView) {
    
    
        if (self.dragging) {
    
    
            CGFloat width = scrollView.contentOffset.x;
            NSInteger page = width/self.frame.size.width + 0.5;
            
            [self.tabView setSelection:page offsetRatio:(width/self.frame.size.width - page)];
        }
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
    
    if (scrollView == self.collectionView) {
    
    
        CGFloat width = scrollView.contentOffset.x;
        NSInteger page = width/self.frame.size.width + 0.5;

        [self.tabView setSelection:page];
        self.dragging = NO;
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    
    
    if (scrollView == self.collectionView && !decelerate) {
    
    
        CGFloat width = scrollView.contentOffset.x;
        NSInteger page = width/self.frame.size.width + 0.5;

        [self.tabView setSelection:page];
        self.dragging = NO;
    }
}
 
#pragma mark - UKTabViewDelegate -
- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position {
    
    
    [self collectionViewScrollToPosition:position];
}

Afin de permettre la réception UICollectionViewdes gestes à l'intérieur UITableView, il est nécessaire UKNestedTableViewd'ajouter

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    
    
    return YES;
}

montrer comme ci-dessous
insérez la description de l'image ici

Nous pouvons voir que lorsque la liste glisse, les deux listes glissent et le contenu à l'intérieur glisse plus rapidement. Ceci est principalement dû à l'exception que les deux listes glissent, donc la liste intérieure est en fait la somme des deux distances de glissement, donc quand nous devons faire glisser la liste extérieure, la liste intérieure ne peut pas glisser.

if (scrollView == self.tableView) {
    
    
    self.offset = self.tableView.contentOffset.y;
    // changed表示外面列表在滑动
    self.changed = YES;
} else {
    
    
    NSInteger position = 0;
    for (UIScrollView *contentView in self.contentViewArray) {
    
    
        if (contentView == scrollView) {
    
    
            // 如果外面列表滑动,禁止里面列表滑动事件
            if (self.changed) {
    
    
                scrollView.contentOffset = CGPointMake(0, [self.offsetArray[position] floatValue]);
                self.changed = NO;
            } else {
    
    
                // 记录当前页面偏移量,方便后面禁止事件
                self.offsetArray[position] = [NSNumber numberWithFloat:scrollView.contentOffset.y];
            }           
            break;
        }
        position++;
    }
}

L'effet est le suivant
insérez la description de l'image ici

L'effet actuel a essentiellement répondu à nos besoins. Il a un effet de plafond, peut glisser à gauche et à droite, peut enregistrer le décalage de la liste et le contenu glisse plus facilement.

Enfin, nous avons d'abord essayé de dérouler le contenu du contrôle, peut-être que cela sera utile plus tard

if (scrollView == self.tableView) {
    
    
    self.originOffset = self.offset;
    self.offset = self.tableView.contentOffset.y;
    self.changed = YES;
} else {
    
    
    NSInteger position = 0;
    for (UIScrollView *contentView in self.contentViewArray) {
    
    
        if (contentView == scrollView) {
    
                    
            CGFloat scrollViewOffset = scrollView.contentOffset.y - [self.offsetArray[position] floatValue];

            if (scrollViewOffset > 0) {
    
    
                if (self.changed) {
    
    
                    scrollView.contentOffset = CGPointMake(0, [self.offsetArray[position] floatValue]);
                    self.changed = NO;
                } else {
    
    
                    self.offsetArray[position] = [NSNumber numberWithFloat:scrollView.contentOffset.y];
                }
            } else if (scrollViewOffset < 0) {
    
    
                if (self.changed) {
    
    
                    self.offset = self.originOffset;

                    self.tableView.delegate = nil;
                    self.tableView.contentOffset = CGPointMake(0, self.offset);
                    self.tableView.delegate = self;

                    self.changed = NO;
                }
                self.offsetArray[position] = [NSNumber numberWithFloat:scrollView.contentOffset.y];
            }
            break;
        }
        position++;
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/chennai1101/article/details/130131507
conseillé
Classement