如何构建一个视差样式/弹性表格视图标题,其中包含图像而没有丑陋的黑客攻击?

时间:2015-01-03 06:01:58

标签: ios objective-c cocoa-touch uitableview uiview

我已经尝试了很长一段时间来构建一个视差风格的表格视图标题,其中包含一个图像,类似于Yahoo News Digest App,或者在Maps.app中查看商家时。 (当你对表格进行橡皮筋处理时,图像高度会增加,当向下滚动时,图像的滚动速度会略微变慢)。

这是APParallaxHeader提供的示范视频:

https://www.youtube.com/watch?v=7-JMdapWXGU

我能找到的最好的教程是this tutorial,它基本上包括将图像视图添加为表视图的子视图。虽然这主要起作用,但作为子视图添加到UITableView是非常没有文档的,并且在我的测试中看起来不适用于自动布局,因此旋转不能很好地发挥作用。

我上面链接的图书馆APParallaxHeader似乎有效,但它的实施真的令人困惑,而且如果我没错,似乎也在调整?

有一种简单的方法可以做到这一点,我只是完全忽略了吗?

5 个答案:

答案 0 :(得分:4)

在更多地考虑这个问题之后,我认为复制该外观的最佳方法是使用包含后面(在z顺序意义上)并在下方(在y方向上)延伸的图像视图的滚动视图表顶视图。在我做的测试中,我给表视图一个标题(在IB中),高100点,背景颜色清晰(表格也需要清晰的背景颜色)。滚动视图和表视图都固定在控制器主视图的侧面和顶部布局指南(控制器嵌入在导航控制器中,设置为使其视图不在顶部栏下方)。表视图也固定在视图的底部,滚动视图的固定高度为200.我给滚动视图初始偏移量为50点,这样当你开始向下拉桌子时,滚动视图可以从顶部滚动更多内容到视图中,同时还在底部显示更多内容(滚动视图的偏移量以表视图偏移率的1/2移动)。一旦表视图的偏移量达到-50,我就会停止更改滚动视图的偏移量,并开始缩放。

#define ZOOMPOINT 50

@interface ViewController () <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *sv;
@property(weak,nonatomic) IBOutlet UITableView *tableView;
@property (strong,nonatomic) UIImageView *iv;
@end

@implementation ViewController

-(void)viewDidLoad {
    [super viewDidLoad];
    self.sv.minimumZoomScale = 1.0;
    self.sv.maximumZoomScale = 2.0;
    self.sv.delegate = self;
    self.iv = [UIImageView new];
    self.iv.contentMode = UIViewContentModeScaleAspectFill;
    self.iv.translatesAutoresizingMaskIntoConstraints = NO;

}


-(void)viewDidLayoutSubviews {
    [self.iv removeFromSuperview];
    [self.sv addSubview:self.iv];
    [self.sv addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[iv(==width)]|" options:0 metrics:@{@"width":@(self.tableView.frame.size.width)} views:@{@"iv":self.iv}]];
    [self.sv addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[iv(==250)]|" options:0 metrics:nil views:@{@"iv":self.iv}]];
    self.iv.image = [UIImage imageNamed:@"img.jpg"]; // the image I was using was 500 x 500
    self.sv.contentOffset = CGPointMake(0, ZOOMPOINT);
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    if ([scrollView isEqual:self.sv]) {
        return self.iv;
    }else{
        return nil;
    }
}


- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {

}


- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView != self.sv) {
        if (scrollView.contentOffset.y < -ZOOMPOINT) {
            [self.sv setZoomScale:(scrollView.contentOffset.y + ZOOMPOINT)/-100 + 1]; // the -100 is arbitrary, change to affect the sensitivity of the zooming
        }else{
             self.sv.contentOffset = CGPointMake(0, ZOOMPOINT + scrollView.contentOffset.y/2.0);
        }
    }
}



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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    cell.textLabel.text = [NSString stringWithFormat:@"Cell %ld", (long)indexPath.row];
    return cell;
}

我已在此处上传了此项目的副本http://jmp.sh/LRKF0nM

答案 1 :(得分:1)

我以为我会抛弃另一个没有使用单独滚动视图的想法。我认为它的扩展方式会更好一些。因此,在这种尝试中,我只是将图像视图添加为主视图的子视图,并将其放置为1/2以下图像视图位于标题顶部之上(视图外),如标题下方(最初由表行隐藏)。当拉下桌子时,视图以下拉速率的一半向下移动(通过调整约束),因此图像的顶部和底部一起进入视图,然后从那里,我通过使用进行扩展变换。

#import "ViewController.h"
#define ZOOMPOINT -60

@interface ViewController () <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
@property(weak,nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UIView *tableHeader;

@property (strong,nonatomic) UIImageView *iv;
@property (strong,nonatomic) NSLayoutConstraint *topCon;
@end

@implementation ViewController

-(void)viewDidLoad {
    [super viewDidLoad];
    self.iv = [UIImageView new];
    self.iv.contentMode = UIViewContentModeScaleToFill; //UIViewContentModeScaleAspectFill;
    self.iv.translatesAutoresizingMaskIntoConstraints = NO;
    self.edgesForExtendedLayout = UIRectEdgeNone;
}


-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.view addSubview:self.iv];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[iv]|" options:0 metrics:nil views:@{@"iv":self.iv}]];
    self.topCon = [NSLayoutConstraint constraintWithItem:self.iv attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:ZOOMPOINT/2.0];
    [self.iv addConstraint:[NSLayoutConstraint constraintWithItem:self.iv attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:self.tableHeader.frame.size.height - ZOOMPOINT*1.5]];
    [self.view addConstraint:self.topCon];
    [self.view layoutIfNeeded];

    self.iv.image = [UIImage imageNamed:@"img.jpg"];
    [self.view sendSubviewToBack:self.iv];
}



- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.contentOffset.y < 0 && scrollView.contentOffset.y > ZOOMPOINT) {
        self.topCon.constant = ZOOMPOINT/2.0 - scrollView.contentOffset.y/2.0;
    }else if (scrollView.contentOffset.y <= ZOOMPOINT) {
        self.iv.transform = CGAffineTransformMakeScale(1 - (scrollView.contentOffset.y - ZOOMPOINT)/200, 1 - (scrollView.contentOffset.y - ZOOMPOINT)/200);
    }

}


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



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    cell.textLabel.text = [NSString stringWithFormat:@"Cell %ld", (long)indexPath.row];
    return cell;
}

可在此处找到项目http://jmp.sh/7PXzISZ

答案 2 :(得分:0)

只是在这里重复,但如果标题的自然框架是frame,并且您已经获得了表格的滚动视图委托集,则缩放的框架将非常相似到:

// in scrollViewDidScroll:
// when the table view is scrolled beyond the header, contentOffset.y is negative
CGFloat headerAspect = frame.size.width / frame.size.height;
CGFloat offsetY = tableView.contentOffset.y;
CGFloat offsetX = offsetY * headerAspect;

// this will enlarge frame since offsets are < 0
frame = CGInsetRect(frame, offsetY, offsetX);

// slide it down to keep the top at the top of the header
frame = CGRectOffset(frame, 0, offsetY / 2.0);

将此设置为将图像视图上的contentMode设置为UIViewContentModeScaleToFill,这应该是一个不错的开始。

答案 3 :(得分:0)

好的,这是一个有助于构建和试用的答案。

我发现操作表的实际标题视图的框架太难了,所以我在行上方的表中添加了一个子视图。为了使该视图显示为常规表头,我给表提供了一个固定大小,透明色的标题视图。

主要思想就像我上面回答的那样:使用表格的内容偏移作为修改图像视图框架的参数,以及imageView的内容模式(更正为UIViewContentModeScaleAspectFill),以便在框架更改时提供缩放效果。“ p>

这是整个视图控制器。这是从故事板构建的,其中视图控制器位于导航控制器内。它只有一个表视图填充其视图,数据源和委托集。

#import "ViewController.h"

// how much of the image to show when the table is un-scrolled
#define HEADER_HEIGHT (100.0)

// the height of the image scaled down to fit in the header.  the real image can/should be taller than this
// i tested this with a 600x400 image
#define SCALED_IMAGE_HEIGHT (200.0)

// zoom image up to this offset
#define MAX_ZOOM (150.0)

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>

@property(weak,nonatomic) IBOutlet UITableView *tableView;

@end

@implementation ViewController

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

    // build the header in view will appear after other layout constraints are applied
    UIImageView *headerView = (UIImageView *)[self.tableView viewWithTag:99];
    if (!headerView) {
        headerView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"landscape.png"]];
        headerView.tag = 99;
        headerView.contentMode = UIViewContentModeScaleAspectFill;
        headerView.clipsToBounds = YES;

        headerView.frame = CGRectMake(0, HEADER_HEIGHT, self.view.bounds.size.width, SCALED_IMAGE_HEIGHT);
        [self.tableView addSubview:headerView];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    CGFloat offsetY = -self.tableView.contentOffset.y - 64;
    // minus 64 is kind of a bummer here.  this calc wants the offset to be 0
    // when no scrolling has happened.  for some reason my table view starts at -64

    CGFloat clamped = MIN(MAX(offsetY, 0), MAX_ZOOM);
    CGFloat origin = -HEADER_HEIGHT - clamped;
    CGFloat height = SCALED_IMAGE_HEIGHT + clamped;

    UIImageView *headerView = (UIImageView *)[self.tableView viewWithTag:99];

    CGRect frame = headerView.frame;
    frame.origin.y = origin;
    frame.size.height = height;
    headerView.frame = frame;
}

// this is a trick to make the view above the header visible: make the table header a clear UIView
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, HEADER_HEIGHT)];
    view.backgroundColor = [UIColor clearColor];
    return view;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return HEADER_HEIGHT;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    cell.textLabel.text = [NSString stringWithFormat:@"Cell %ld", indexPath.row];
    return cell;
}

@end

答案 4 :(得分:0)