5

私は現在、フォト アプリと同じようにフォト ピッカーを実装しようとしていますが、カスタム イメージ ソースを使用しています。「写真のスクロール」部分については、Apple PhotoScroller のサンプル コードを使用し、それを適応させました。主な違いの 1 つは、ナビゲーション バーを備えたナビゲーション コントローラー (アプリケーションのナビゲーション コントローラーではなく、photoPicker 独自のナビゲーション コントローラー) に組み込まれていることです。ステータス バーとナビゲーション バーを半透明にし、photoPicker で使用されるすべてのビュー コントローラーで wantsFullScreenLayout = YES を設定しました。ほぼ問題なく動作しているようです。「概要」ビュー (アルバムのすべての写真のサムネイルを表示するビュー) は確かにフルスクリーンであり、最初はサムネイルがナビゲーション バーの下に表示されるように手動でオフセットする必要があります。ただし、スクロール部分には不具合があります。そうでない人のために photoScroller のサンプル コードは、UIScrollView 属性 (pagingScrollView) を持つカスタム UIViewController (PhotoViewController) と、UIView および NSInteger インデックス属性を持つ一連のカスタム UIScrollView (ImageScrollView) で動作します。ImageScrollView インスタンスは、PhotoScroller のサブビューとして追加/削除されます。

以下は関連するコードです:

PhotoViewController.h

@interface PhotoViewController : UIViewController <UIScrollViewDelegate> {
    UIScrollView *pagingScrollView;

    NSMutableSet *recycledPages;
    NSMutableSet *visiblePages;

    IBOutlet UIToolbar *toolbar;
    IBOutlet UIBarButtonItem *previousButtonItem;
    IBOutlet UIBarButtonItem *nextButtonItem;

    id<PhotoViewDataSource> dataSource;
}

@property(nonatomic, retain) UIScrollView *pagingScrollView;
@property(nonatomic, retain) NSMutableSet *recylcledPages;
@property(nonatomic, retain) NSMutableSet *visiblePages;

@property(nonatomic, retain) UIToolbar *toolbar;
@property(nonatomic, retain) UIBarButtonItem *previousButtonItem;
@property(nonatomic, retain) UIBarButtonItem *nextButtonItem;

@property(nonatomic, retain) id<PhotoViewDataSource> dataSource;

PhotoViewController.m

- (void)loadView 
{
    self.wantsFullScreenLayout = YES;

    // Configure the scrollView
    CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
    pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
    pagingScrollView.pagingEnabled = YES;
    pagingScrollView.backgroundColor = [UIColor redColor];
    pagingScrollView.showsVerticalScrollIndicator = NO;
    pagingScrollView.showsHorizontalScrollIndicator = NO;
    pagingScrollView.contentSize = CGSizeMake(pagingScrollViewFrame.size.width * [self.dataSource imageCount],
                                              pagingScrollViewFrame.size.height);
    //pagingScrollView.contentOffset = CGPointMake(0, 0);

    pagingScrollView.delegate = self;
    self.view = pagingScrollView;

    // TODO ? Prepare to tile content
    recycledPages = [[NSMutableSet alloc] init];
    visiblePages  = [[NSMutableSet alloc] init];
    [self processPages];
}


- (void)processPages {

    // Calculate which pages are visible
    CGRect visibleBounds = pagingScrollView.bounds;
    NSLog(@"PhotoViewController - processPages : frame = %@", NSStringFromCGRect(pagingScrollView.frame));
    NSLog(@"pagingScrollView bounds = %@", NSStringFromCGRect(pagingScrollView.bounds));
    NSLog(@"and contentSize = %@", NSStringFromCGSize(pagingScrollView.contentSize));

    int firstNeededPageIndex = floorf(CGRectGetMinX(visibleBounds) / CGRectGetWidth(visibleBounds));
    int lastNeededPageIndex  = floorf((CGRectGetMaxX(visibleBounds)-1) / CGRectGetWidth(visibleBounds));
    firstNeededPageIndex = MAX(firstNeededPageIndex, 0);
    lastNeededPageIndex  = MIN(lastNeededPageIndex, [dataSource imageCount] - 1);

    if (lastNeededPageIndex >= 0) {

        // Recycle no-longer-visible pages 
        for (ImageScrollView *page in visiblePages) {
            if (page.index < firstNeededPageIndex || page.index > lastNeededPageIndex) {
                [recycledPages addObject:page];
                [page removeFromSuperview];
            }
        }
        [visiblePages minusSet:recycledPages];

        // add missing pages
        for (int index = firstNeededPageIndex; index <= lastNeededPageIndex; index++) {
            if (![self isDisplayingPageForIndex:index]) {
                ImageScrollView *page = [self dequeueRecycledPage];
                if (page == nil) {
                    page = [[[ImageScrollView alloc] init] autorelease];
                }
                [self configurePage:page forIndex:index];
                NSLog(@"PhotoViewController - processPage 2 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds));
                [pagingScrollView addSubview:page];
                NSLog(@"PhotoViewController - processPage 3 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds));
                [visiblePages addObject:page];
            }
        }
    }
}

- (ImageScrollView *)dequeueRecycledPage {
    ImageScrollView *page = [recycledPages anyObject];
    if (page) {
        [[page retain] autorelease];
        [recycledPages removeObject:page];
    }
    return page;
}

- (BOOL)isDisplayingPageForIndex:(NSUInteger)index {
    BOOL foundPage = NO;
    for (ImageScrollView *page in visiblePages) {
        if (page.index == index) {
            foundPage = YES;
            break;
        }
    }
    return foundPage;
}

- (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index {
    page.index = index;
    page.frame = [self frameForPageAtIndex:index];

    NSLog(@"PhotoViewController - configurePage : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds));

    [page displayImage:[dataSource imageForImageId:index]];

    NSLog(@"PhotoViewController - configurePage 2 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds));
}


#pragma mark -
#pragma mark ScrollView delegate methods

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self processPages];
}


#pragma mark -
#pragma mark  Frame calculations
#define PADDING  10

- (CGRect)frameForPagingScrollView {
    CGRect frame = [[UIScreen mainScreen] bounds];
    frame.origin.x -= PADDING;
    frame.size.width += (2*PADDING);
    return frame;
}

- (CGRect)frameForPageAtIndex:(NSUInteger)index {
    CGRect pagingScrollViewFrame = [self frameForPagingScrollView];

    CGRect pageFrame = pagingScrollViewFrame;
    pageFrame.size.width -= (2 * PADDING);
    pageFrame.origin.x = (pagingScrollViewFrame.size.width * index) + PADDING;
    //pageFrame.origin.x = (pagingScrollViewFrame.size.width * index) - (PADDING*index*2);
    return pageFrame;
}

ImageScrollView.h

@interface ImageScrollView : UIScrollView <UIScrollViewDelegate> {
    UIView        *imageView;
    NSUInteger     index;
}
@property (assign) NSUInteger index;

- (void)displayImage:(UIImage *)image;
//- (void)displayTiledImageNamed:(NSString *)imageName size:(CGSize)imageSize;
- (void)configureForImageSize:(CGSize)imageSize;

ImageScrollView.m

- (void)layoutSubviews 
{
    [super layoutSubviews];

    imageView.backgroundColor = [UIColor greenColor];
    self.backgroundColor = [UIColor blueColor];

    // center the image as it becomes smaller than the size of the screen

    CGSize boundsSize = self.bounds.size;
    CGRect frameToCenter = imageView.frame;

    // center horizontally
    if (frameToCenter.size.width < boundsSize.width)
        frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
    else
        frameToCenter.origin.x = 0;

    // center vertically
    if (frameToCenter.size.height < boundsSize.height)
        frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
    else
        frameToCenter.origin.y = 0;

    imageView.frame = frameToCenter;
    NSLog(@"imageView frame = %@", NSStringFromCGRect(frameToCenter));

    if ([imageView isKindOfClass:[TilingView class]]) {
        // to handle the interaction between CATiledLayer and high resolution screens, we need to manually set the
        // tiling view's contentScaleFactor to 1.0. (If we omitted this, it would be 2.0 on high resolution screens,
        // which would cause the CATiledLayer to ask us for tiles of the wrong scales.)
        imageView.contentScaleFactor = 1.0;
    }
}

#pragma mark -
#pragma mark UIScrollView delegate methods

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return imageView;
}

#pragma mark -
#pragma mark Configure scrollView to display new image (tiled or not)

- (void)displayImage:(UIImage *)image
{
    // clear the previous imageView
    [imageView removeFromSuperview];
    [imageView release];
    imageView = nil;

    // reset our zoomScale to 1.0 before doing any further calculations
    self.zoomScale = 1.0;

    // make a new UIImageView for the new image
    imageView = [[UIImageView alloc] initWithImage:image];
    [self addSubview:imageView];

    [self configureForImageSize:[image size]];
}

- (void)configureForImageSize:(CGSize)imageSize 
{
    CGSize boundsSize = [self bounds].size;

    // set up our content size and min/max zoomscale
    CGFloat xScale = boundsSize.width / imageSize.width;    // the scale needed to perfectly fit the image width-wise
    CGFloat yScale = boundsSize.height / imageSize.height;  // the scale needed to perfectly fit the image height-wise
    CGFloat minScale = MIN(xScale, yScale);                 // use minimum of these to allow the image to become fully visible

    // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
    // maximum zoom scale to 0.5.
    CGFloat maxScale = /*1.0 / */[[UIScreen mainScreen] scale];

    // don't let minScale exceed maxScale. (If the image is smaller than the screen, we don't want to force it to be zoomed.) 
    if (minScale > maxScale) {
        minScale = maxScale;
    }

    self.contentSize = imageSize;
    self.maximumZoomScale = maxScale;
    self.minimumZoomScale = minScale;
    self.zoomScale = minScale;  // start out with the content fully visible
}

私の問題は、最初に PhotoViewController インスタンスをロードしたときに、pagingScrollView が (0, -64) ピクセル (ステータス バーの高さ + ナビゲーション バーの高さ) の原点にオフセットされていることです。これにより、ImageScrollView がナビゲーション バー (原点 (0, 0)) の下に表示され、その高さが画面よりも小さい場合でも上下にスクロールできる、混乱したインターフェイスが発生します。

いくつかのログとブレークポイントを使用して、読み込みプロセスの開始時に pagingScrollView の境界が適切であることを確認できました。画面に合わせて ImageScrollView の画像を拡大縮小すると、それらが変化します。これにより、viewForZoomingInScrollView が呼び出され、次に scrollViewDidScroll メソッドが呼び出されます。これらの呼び出し中に、pagingScrollView がオフセットされています。

オフセットを手動で設定しようとしましたが、processPages で設定すると、scrollView が上下に跳ね返らなくなりました...

どんな助けでも大歓迎です!

乾杯

PB

4

4 に答える 4

6

あなたの特定のケース(つまり、スタックPhotoViewControllerにプッシュされているUINavigationController)では、ナビゲーションバーがのスクロールビューに追加contentInsetされます。これについては、こちらこちらでPhotoViewController詳しく説明しています。

つまり、PhotoViewControllerビューはウィンドウの境界にぴったりと収まらないため、あらゆる方向にスクロールする余地があります。

あなたの問題に対する2つの可能な解決策を見つけました。

1 -ナビゲーション バーとステータス バーの高さを補正するにcontentInsetは、手動で を調整する必要があります。pagingScrollViewこれを に追加PhotoViewController:

- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat topOffset = self.navigationController.navigationBar.frame.size.height + [[UIApplication sharedApplication] statusBarFrame].size.height;
    pagingScrollView.contentInset = UIEdgeInsetsMake(-topOffset, 0.0, 0.0, 0.0);
}

また

2 - スクロール ビューのみに があるため、プレーンcontentInsetでラップします。pagingScrollViewUIView

- (void)loadView {    
    [self setWantsFullScreenLayout:YES];

    CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
    photoView = [[UIView alloc] initWithFrame:pagingScrollViewFrame];
    pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];

    // ... configure the pagingScrollView

    [photoView addSubview:pagingScrollView];
    self.view = photoView;

    // ...
}

お役に立てれば!

于 2010-09-23T20:35:51.490 に答える
0

ストーリーボードからView ControllerのAdjust Scroll View insetsをfalseに設定するだけです。

于 2016-01-31T11:10:59.260 に答える
0

View Controllerで設定する必要がある主なものは次のとおりです。コンテンツ ビューで自動サイズ変更フラグを使用します。次のフラグを有効にすると、View Controller のサイズが画面全体に拡大されます。viewWillAppear で以下を実行します。

- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];

    [self setWantsFullScreenLayout:YES]; // if you want to underlap status
    self.navigationController.navigationBar.translucent = YES; // underlap nav controller
    self.toolbar.translucent = YES; // if you have a toolbar.
}

詳細については、Apple のドキュメントを参照してください。

http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html#//apple_ref/doc/uid/TP40011313-CH2

コンテンツ ビューでフレーム境界を設定するために何かを行っている場合は、上記で処理する必要があるため、そのコードをすべて削除します。

于 2012-04-13T16:53:59.553 に答える
-2

Three20 はそれをとてもうまく実装しています。派手な機能が必要な場合は、必要に応じてコードを調整するだけです。

すべての大変な作業はすでに完了しています。それでも独自のバージョンを実装したい場合は、少なくとも Three20 コードを見て、達成しようとしていることがどのように行われるかを確認してください。

どのように見えるか: http://farm4.static.flickr.com/3432/3378117315_3bf905bdb6_o.jpg

ソースコード: http://github.com/facebook/three20

于 2010-09-15T10:44:32.967 に答える