4

iOS用の最新のExpediaアプリでは、非常に興味深い効果があり、頭を包み込もうとしています。無限にスクロールするサブビューの2つの列があり、2つのスクロールビューで実行できることがわかっています。興味深い部分は、全体的なスクロールビューにリネンの背景があり、静止したままで、各サブビューセル間のギャップに表示されることです。本当にクールな部分は、サブビューが所定の位置にとどまる異なる背景を持っていることです。下のスクリーンショットでは、街のスカイラインの画像です。サブビューがスクロールすると、都市の画像はサブビューセルの後ろにのみ表示されます。ある種のマスキングトリックのように見えますが、効果がどのように行われるのかよくわかりません。どうすれば同じ結果を得ることができますか?

基本的に、小さなウィンドウとして機能し、リネンを表示しないサブビューの背後に静的な背景を表示するにはどうすればよいですか。リネンはセルの周りにのみ表示する必要があります。

アプリをダウンロードして機内モードに切り替え、自分で試すことができます。

これがスクリーンショットです:ここに画像の説明を入力してください

セルがスクロールしたが、都市は同じままであることを示す別の例を次に示します。

ここに画像の説明を入力してください

4

2 に答える 2

1

エレガントな解決策を見つけたいのですが、今のところ、目に見えるサブビューのオフセットを追跡し、それらの外観を構成することによってそれを行います。

サンプルプロジェクトで結果を確認してください。

今後の参考のために、以下のコードを添付します。

ViewController.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

ViewController.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を使用)を使用して、フラットカラーを使用してリネンにいくつかのボックスを描画します。次に、そのボックスの色を透明にマッピングして、切り抜きを作成します。これを行う方法は他にもありますが、それが私が行う方法です。

アプリで、メインビューに2つ以上の画像ビューを追加します。配置は実行時に決定されるため、配置について心配する必要はありません。これらの画像ビューを設定して、「スクロール」したい画像を含めることができます。次に、カットアウト付きのリネンUIImageViewを追加して、上部に配置し、画面サイズ全体を占めるようにします。上部のUIImageViewの背景が透明に設定されていることを確認してください。

アプリが起動したら、「下」の画像ビューを上から下にレイアウトし、[UIView beginAnimation]を開始して、「y」の位置を変更して下の画像ビューを上にスクロールします。このアニメーションには、上部の画像ビューが完全に画面から外れたときに呼び出されるコールバックが必要です。次に、完了したコールバックで、現在の状態をレイアウトし、アニメーションを再開します。これが私が使用したコードの本質です(ただし、スクロールは右から左であり、下から上ではなく、画像はすべて同じサイズでした)。

    - (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 に答える