4

在最新的 iOS 版 Expedia 应用程序中,它们有一个非常有趣的效果,我正试图绕过它。有两列无限滚动的子视图,我知道可以用 2 个滚动视图来完成。有趣的是,整个滚动视图似乎有一个亚麻背景,它保持静态,并且可以在每个子视图单元格之间的间隙中看到。真正酷的部分是子视图具有不同的背景并保持不变。在下面的屏幕截图中,它是城市天际线图像。当子视图滚动时,只能在子视图单元格后面看到城市图像。这似乎是某种掩蔽技巧,但我不太清楚效果是如何完成的。我怎样才能达到相同的结果?

本质上,您如何在充当小窗口而不显示亚麻布的子视图后面显示静态背景。亚麻布应该只显示在细胞周围。

您可以下载该应用程序,点击飞行模式并亲自尝试。

这是一个屏幕截图:在此处输入图像描述

这是另一个显示单元格滚动但城市保持不变的情况:

在此处输入图像描述

4

2 回答 2

1

我想找到一个优雅的解决方案,现在我将通过跟踪可见的子视图偏移并配置它们的外观来做到这一点。

请在示例项目中查看结果。

为了将来的参考,我将附上以下代码:

视图控制器.m

//
//  OSViewController.m
//  ScrollMasks
//
//  Created by #%$^Q& on 11/30/12.
//  Copyright (c) 2012 Demo. All rights reserved.
//

#import "OSViewController.h"

@interface OSViewController ()

// subviews
@property (strong) IBOutlet UIScrollView * scrollView;

// all the subviews
@property (strong) NSArray * maskedSubviews;
// subviews visible at scrollview, we'll update only them
@property (strong) NSArray * visibleMaskedSubviews;

// updates the views from visibleMaskedSubviews
-(void) updateVisibleSubviews;
// updates the visibleMaskedSubviews array with the given scrollView offset
-(void) updateVisibleSubviewsArrayForOffset:(CGPoint) offset;
@end

@implementation OSViewController

-(void) unused {}

#pragma mark - view

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

- (void)viewDidLoad
{
    [super viewDidLoad];

    /*
     See -updateVisibleSubviews comment for the class comments
     */
    UIView * newMaskedView = nil;
    NSMutableArray * newMaskedSubviews = [NSMutableArray array];

    const CGSize scrollViewSize = self.scrollView.bounds.size;
    const int totalSubviews = 10;
    const float offset = 20.;
    const float height = 100.;

    UIImage * maskImage = [UIImage imageNamed:@"PeeringFrog.jpg"];

    /*

     // Uncomment to compare

     UIImageView * iv = [[UIImageView alloc] initWithFrame:self.scrollView.bounds];
     iv.image = maskImage;
     [self.view insertSubview:iv atIndex:0];
     */

    // add scrollview subviews
    for (int i = 0; i < totalSubviews; i++) {

        CGRect newViewFrame = CGRectMake(offset, offset*(i+1) + height*i, scrollViewSize.width - offset*2, height);
        newMaskedView = [UIView new];
        newMaskedView.frame = newViewFrame;
        newMaskedView.clipsToBounds = YES;
        newMaskedView.backgroundColor = [UIColor redColor];
        newMaskedView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;

        UIImageView * maskImageView = [UIImageView new];
        maskImageView.frame = CGRectMake(0, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
        maskImageView.image = maskImage;
        [newMaskedView addSubview:maskImageView];

        [self.scrollView addSubview:newMaskedView];
        [newMaskedSubviews addObject:newMaskedView];
    }

    self.scrollView.contentSize = CGSizeMake(scrollViewSize.width, (height+offset)*totalSubviews + offset*2);

    self.maskedSubviews = [NSArray arrayWithArray:newMaskedSubviews];
    [self updateVisibleSubviewsArrayForOffset:self.scrollView.contentOffset];
}

-(void) updateVisibleSubviews {
    [self updateVisibleSubviewsArrayForOffset:self.scrollView.contentOffset];

    for (UIView * view in self.visibleMaskedSubviews) {

        /*
        TODO:
         view must be UIView subclass with the imageView initializer and imageView frame update method
        */

        CGPoint viewOffset = [self.view convertPoint:CGPointZero fromView:view];
        UIImageView * subview = [[view subviews] objectAtIndex:0];
        CGRect subviewFrame = subview.frame;
        subviewFrame = CGRectMake(-viewOffset.x, -viewOffset.y, subviewFrame.size.width, subviewFrame.size.height);
        subview.frame = subviewFrame;
    }
}


#pragma mark - scrollview delegate & utilities
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
    [self updateVisibleSubviews];
}

-(void) updateVisibleSubviewsArrayForOffset:(CGPoint) offset {

    NSMutableArray * newVisibleMaskedSubviews = [NSMutableArray array];
    for (UIView * view in self.maskedSubviews) {
        CGRect intersectionRect = CGRectIntersection(view.frame, self.scrollView.bounds);
        if (NO == CGRectIsNull(intersectionRect)) {
            [newVisibleMaskedSubviews addObject:view];
        }
    }

    self.visibleMaskedSubviews = [NSArray arrayWithArray:newVisibleMaskedSubviews];
}

#pragma mark - memory
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

视图控制器.h

//
//  OSViewController.h
//  ScrollMasks
//
//  Created by #%$^Q& on 11/30/12.
//  Copyright (c) 2012 Demo. All rights reserved.
//

/*
 PeeringFrog image is taken (and resized) from Apple sample project "PhotoScroller"
 */

#import <UIKit/UIKit.h>

@interface OSViewController : UIViewController <UIScrollViewDelegate>

@end
于 2012-11-30T17:39:56.677 回答
0

几年前我做过类似的事情。起初我尝试使用 CGImageMaskCreate 东西,但发现创建一个具有透明“切口”的图像然后使用动画效果滚动它下面的图片要容易得多。

对于您的情况,我会找到屏幕大小的亚麻图像。然后我会使用图像编辑器(我使用 GIMP)在亚麻布上使用纯色绘制一些框。然后我将该框颜色映射为透明以制作切口。还有其他方法可以做到这一点,但我就是这样做的。

在您的应用程序中,将两个或更多图像视图添加到主视图。不要担心位置,因为这将在运行时确定。您需要将这些图像视图设置为包含您希望在其下“滚动”的图像。然后添加带有切口的亚麻布 UIImageView 使其位于顶部并占据整个屏幕尺寸。确保顶部 UIImageView 的背景设置为透明。

当应用程序启动时,从上到下布局您的“下方”图像视图,然后启动一个 [UIView beginAnimation],通过修改“y”位置向上滚动您的下方图像视图。这个动画应该有一个 done 回调,当顶部图像视图完全离开屏幕时会调用该回调。然后,在 done 回调中,布局当前状态并再次启动动画。这是我使用的代码的主要内容(但请注意,我的滚动是从右到左,而不是从下到上,而且我的图像大小都相同。)

    - (void)doAnimationSet
    {

        [iv1 setFrame:CGRectMake(0, 0, imageWidth, imageHeight)];
        [iv2 setFrame:CGRectMake(imageWidth, 0, imageWidth, imageHeight)];
        [iv3 setFrame:CGRectMake(imageWidth*2, 0, imageWidth, imageHeight)];

        [self loadNextImageSet];

        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:10];
        [UIView setAnimationCurve:UIViewAnimationCurveLinear];

        [iv1 setFrame:CGRectMake(-imageWidth, 0, imageWidth, imageHeight)];
        [iv2 setFrame:CGRectMake(0, 0, imageWidth, imageHeight)];
        [iv3 setFrame:CGRectMake(imageWidth, 0, imageWidth, imageHeight)];

        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(doneAnimation:finished:context:)];

        [UIView commitAnimations];

    }

    - (void)doneAnimation:(NSString *)aid finished:(BOOL)fin context:(void *)context
    {
        [self doAnimationSet];
    }

这应该会给你你正在寻找的效果。祝你好运 :)

于 2012-11-30T17:34:05.847 回答