UITableViewCell 中的 UIScrollview 和维护滚动视图页面

Posted

技术标签:

【中文标题】UITableViewCell 中的 UIScrollview 和维护滚动视图页面【英文标题】:UIScrollview within UITableViewCell and maintaining scrollview page 【发布时间】:2013-08-14 04:58:47 【问题描述】:

我在 UITableViewCell 中有一个 UIScrollView,用于滚动单元格内的图像。然而,当一个单元格被重用时,滚动位置/内容被重新加载,因此当单元格再次进入视图时,它不记得它所在的滚动位置(页面)。

在 UITableView 单元格中拥有滚动视图并使其在返回视图时保持位置的最佳方式是什么。例如,AirBnB 应用程序 (https://itunes.apple.com/us/app/airbnb/id401626263?mt=8) 似乎已经实现了这一点。

【问题讨论】:

更新了我的答案,只需下载并检查即可。 【参考方案1】:

您需要在属性中跟踪滚动视图的内容偏移量。在下面的示例中,我使用可变字典执行此操作。在 cellForRowAtIndexPath: 中,我给滚动视图一个标签并将控制器设置为委托。在滚动视图委托方法scrollViewDidEndDecelerating:中,滚动视图的内容偏移设置为与滚动视图标签对应的键的对象。在 cellForRowAtIndexPath: 中,我检查 indexPath.row(转换为 NSNumber)是否是字典的键之一,如果是,则恢复该滚动视图的正确偏移量。我将 1 添加到标签的原因是因为表格视图有自己的滚动视图,其标签为 0,所以我不想使用 0 作为单元格滚动视图之一的标签。

所以在 cellForRowAtIndexPath 中,你需要这样的东西:

cell.scrollView.tag = indexPath.row + 1;
cell.scrollView.delegate = self;

if ([self.paths.allKeys containsObject:@(indexPath.row + 1)]) 
    cell.scrollView.contentOffset = CGPointMake([self.paths[@(indexPath.row + 1)] floatValue],0);
else
    cell.scrollView.contentOffset = CGPointZero;

return cell;

在委托方法中:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
    if (scrollView.tag != 0)
        [self.paths setObject:@(scrollView.contentOffset.x) forKey:@(scrollView.tag)];

paths 是我在 viewDidLoad 中创建的一个属性 (NSMutableDictionary)。

【讨论】:

@rdelmar 我也在努力做到这一点。类似于 AP(美联社)应用程序。有什么方法可以聊天或者我可以得到更多的背景信息吗?将不胜感激。 @WilliamRiley,你还需要什么上下文? 我发现有时scrollViewDidEndDecelerating 并不总是根据你的滚动方式(如果你轻弹或不轻弹)触发,所以我改用scrollViewDidScroll。这样更靠谱。【参考方案2】:

使用我的 tableView 海关单元格

homeScrollCell.h

#import <UIKit/UIKit.h>
#import "MyManager.h"

@interface homeScrollCell : UITableViewCell<UIScrollViewDelegate>

    MyManager *manager;
    UIScrollView *__scrollView;

-(void)setPage:(int)page;
@property int currentPage;
@end

homeScrollCell.m

#import "homeScrollCell.h"

@implementation homeScrollCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) 

        manager=[MyManager sharedManager];
        __scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width,self.bounds.size.height)];
        [__scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
        NSInteger viewcount= 3;

        for(int i = 0; i< viewcount; i++)
        
            CGFloat x = i * self.bounds.size.width;

            UIView *view = [[UIView alloc] initWithFrame:CGRectMake(x, 0,self.bounds.size.width,self.bounds.size.height)];
             [view setAutoresizingMask:UIViewAutoresizingFlexibleHeight];

            UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(50, 20, 200, 50)];
            [label setBackgroundColor:[UIColor redColor]];
            label.text=[NSString stringWithFormat:@"Hi, I am label %i",i];
            [view addSubview:label];
             view.backgroundColor = [UIColor greenColor];

            [__scrollView addSubview:view];
        

        [__scrollView setBackgroundColor:[UIColor redColor]];
        __scrollView.contentSize = CGSizeMake(self.bounds.size.width *viewcount, 100);
        __scrollView.pagingEnabled = YES;
        __scrollView.bounces = NO;
        __scrollView.delegate=self;
        [self addSubview:__scrollView];

        // Initialization code
    
    return self;


-(void)setPage:(int)page

    CGFloat pageWidth = __scrollView.frame.size.width;
    float offset_X=pageWidth*page;
    [__scrollView setContentOffset:CGPointMake(offset_X, __scrollView.contentOffset.y)];
    _currentPage=page;


- (void)scrollViewDidScroll:(UIScrollView *)scrollView

    static NSInteger previousPage = 0;

    CGFloat pageWidth = scrollView.frame.size.width;
    float fractionalPage = scrollView.contentOffset.x / pageWidth;

    NSInteger page = lround(fractionalPage);

    if (previousPage != page) 
        _currentPage=page;
        [manager setpage:_currentPage ForKey:[NSString stringWithFormat:@"%i",self.tag]];

        previousPage = page;
    


- (void)setSelected:(BOOL)selected animated:(BOOL)animated

    [super setSelected:selected animated:animated];

    // Configure the view for the selected state


@end

并使用单例文件在单元格内保存记录或页面。

MyManager.h

#import <Foundation/Foundation.h>

@interface MyManager : NSObject

+ (id)sharedManager;

-(void)setpage:(int)page ForKey:(NSString*)key;
-(int)getpageForKey:(NSString*)key;

@end

MyManager.m

#import "MyManager.h"

static NSMutableDictionary *dictionary;

@implementation MyManager

#pragma mark Singleton Methods

+ (id)sharedManager 
    static MyManager *sharedMyManager = nil;
    static dispatch_once_t onceToken;


    dispatch_once(&onceToken, ^
        sharedMyManager = [[self alloc] init];
    );
    return sharedMyManager;


-(void)setpage:(int)page ForKey:(NSString*)key

    [dictionary setValue:[NSString stringWithFormat:@"%i",page] forKey:key];


-(int)getpageForKey:(NSString*)key

  return [[dictionary valueForKey:key] intValue];


- (id)init 
    if (self = [super init]) 
       dictionary=[[NSMutableDictionary alloc] init];
    
    return self;


- (void)dealloc 
    // Should never be called, but just here for clarity really.


@end

并将 cellForRow 中的这个自定义单元格用作

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    static NSString *CellIdentifier = @"Home Scroll Cell";
    homeScrollCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell==nil) 
        cell = [[homeScrollCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    

    cell.tag=indexPath.row;
    [cell setPage:[manager getpageForKey:[NSString stringWithFormat:@"%i",indexPath.row]]];

    return cell;

在 CustomCell 中设置您的页面并滚动,重新滚动后返回到先前位置将显示您在滚动之前设置的页面。 它将保持您的页面按原样移动。

Download And Run

【讨论】:

谢谢!这看起来是一个很好的解决方案。它比我现在想要的复杂一点,所以我使用了上面更简单的方法,但是对于更复杂的表,我可以看到使用单例方法。【参考方案3】:

rdelmar 的回答缺少一些东西。 scrollViewDidEndDecelerating 只有在用户滚动触发减速行为时才会被调用。

仅拖动滚动不会调用此方法,因此对于无法区分两种滚动类型的用户而言,您将获得不一致的行为。你需要用scrollViewDidEndDragging:willDecelerate来捕捉它

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
   
    if (!decelerate) 
        self.tableCellContentOffsets[@(scrollView.tag)] = @(scrollView.contentOffset.x);
    


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
   
    self.tableCellContentOffsets[@(scrollView.tag)] = @(scrollView.contentOffset.x);

【讨论】:

以上是关于UITableViewCell 中的 UIScrollview 和维护滚动视图页面的主要内容,如果未能解决你的问题,请参考以下文章

限制UIScrollView中的可滚动区域

更改 UITableViewCell 中的对象也更改了重用 UITableViewCell 的对象

确定 UITableViewCell 中的 UIButton

UITableViewCell 事件中的 UIButton 不起作用

UITableViewCell 中的 UICollectionView

UITableVIewCell 中的 UIScrollView 不会滚动