2010-09-15 32 views
4

J'essaie actuellement de mettre en œuvre un sélecteur de photos, tout comme l'application photo, mais avec une source d'image personnalisée. Pour la partie "défilement des photos", j'ai utilisé l'exemple de code Apple PhotoScroller et l'ai adapté. L'une des principales différences est qu'il est maintenant intégré dans un contrôleur de navigation (qui est le propre contrôleur de navigation de photoPicker, pas celui de l'application), avec une barre de navigation. J'ai l'état et la barre de navigation translucides et j'ai réglé setsFullScreenLayout = YES sur tout le contrôleur de vue utilisé dans le photoPicker. Cela semble fonctionner presque bien. La vue "Vue d'ensemble" (celle qui affiche les vignettes de toutes les photos de l'album) est en effet en plein écran, et je dois la décaler manuellement pour que les vignettes soient d'abord affichées sous la barre de navigation. Pour la partie de défilement cependant il y a un pépin. Pour ceux qui ne connaissent pas l'exemple de code de photoScroller, il fonctionne avec un UIViewController personnalisé (PhotoViewController) avec un attribut UIScrollView (pagingScrollView) et un ensemble de UIScrollView personnalisé (ImageScrollView) avec un UIView et un attribut NSInteger. Les instances ImageScrollView sont ensuite ajoutées/supprimées en tant que sous-vues du PhotoScroller.IPhone - Plein écran UIScrollView commence à droite mais se repositionne sous la barre de navigation

est Ci-dessous un code connexe:

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 
} 

Mon problème est que la pagingScrollView est décalée vers une origine de (0, -64) pixels (hauteur de la barre de bar + nav état je suppose) quand je première charge l'instance PhotoViewController. Il en résulte une interface foirée dans laquelle ImageScrollView apparaît sous la barre de navigation (origine (0, 0)) et peut ensuite être déplacé de haut en bas même si sa hauteur est inférieure à l'écran. Avec quelques logs et points d'arrêt, j'ai pu déterminer que les limites de pagingScrollView sont correctes au début du processus de chargement. Ils changent lorsque je redimensionne l'image de ImageScrollView pour l'adapter à l'écran. Cela provoque viewForZoomingInScrollView, puis les méthodes scrollViewDidScroll à appeler. Le pagingScrollView est en cours de décalage pendant ces appels.

J'ai essayé le décalage d'manuellement, mais quand je le fais dans le processPages ScrollView ne peut rebondir plus haut et en bas ...

Toute aide sera appréciée!

Vive

PB

Répondre

-2

Three20 a une mise en œuvre très agréable de celui-ci. Si vous avez besoin d'une fonctionnalité sophistiquée, il suffit d'adapter le code à votre propre nécessité.

Tout le dur labeur est déjà fait. Bien que si vous voulez toujours implémenter votre propre version, jetez au moins un coup d'oeil au code de Three20 et vérifiez comment ils font ce que vous essayez d'accomplir.

Comment ça ressemble: http://farm4.static.flickr.com/3432/3378117315_3bf905bdb6_o.jpg

code source: http://github.com/facebook/three20

+0

Thx, je vais essayer cela dès que possible –

+0

Superbe bibliothèque, merci de me l'avoir montrée! –

+0

Trois20 n'est pas vraiment la solution pour cela si vous voulez seulement accomplir cet écran. Ce n'est pas aussi personnalisable que si vous l'aviez fait vous-même, et vous devez inclure une énorme bibliothèque juste pour ça ... –

6

Dans votre cas particulier (à savoir l'PhotoViewController étant poussé sur une pile UINavigationController) - barres de navigation ajouter une vue de défilement de contentInset-PhotoViewController. Tout cela est mieux expliqué here et here. C'est-à-dire que la vue PhotoViewController ne s'adapte pas parfaitement aux limites de votre fenêtre et peut donc être déplacée dans toutes les directions.

J'ai trouvé 2 solutions possibles à votre problème.

1 - Vous devez ajuster manuellement le contentInset de votre pagingScrollView pour compenser la hauteur de la barre de navigation et de la barre d'état. Ajouter à ce 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); 
} 

OU

2 - Depuis défiler que des vues ont une contentInset, envelopper le pagingScrollView dans une plaine UIView:

- (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; 

    // ... 
} 

Hope this helps!

+0

Thx, c'était plus ou moins ce que j'essayais de faire mais je ne connaissais pas le bon chemin. Cela dit, je ne l'ai pas testé, je suis passé à l'application Three20, qui fait exactement ce dont j'avais besoin, et plus :) –

0

Les principales choses à régler sont les suivantes dans votre contrôleur de vue. Utilisez les indicateurs autoresize sur vos vues de contenu. L'activation des indicateurs suivants étendra la taille de votre contrôleur de vue à la taille totale de l'écran. J'effectue ce qui suit dans 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. 
} 

plus de détails peuvent être trouvés ici dans les documents de pomme:

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

si vous avez fait des choses pour définir les limites du cadre sur le point de vue du contenu, je supprimerais tout ce code, car le doit au-dessus prends soin de toi pour toi.

0

Il vous suffit de définir l'option Ajuster les incrustations de vue de défilement du contrôleur de vue sur false à partir du storyboard.