2013-08-30 2 views
5

Robię to i jestem ciekawy, czy to najlepszy sposób, czy głupia!Najlepszy sposób na centrowanie niektórych zdjęć za pomocą iOS autolayout

Mam kilka 40-pikselowych obrazów, z których każdy przypomina kafelek Scrabble. Moja aplikacja chce wyświetlić niektóre i wyśrodkować je na ekranie. Tylko, że nie wie, ile tam będzie! Może wynosić od 3 do 10.

Myślę, że najlepiej jest, jeśli policzę ile, wielokrotność przez 40, więc wiem, ile pikseli będzie szerokości, a potem udawaj, że to 280 pikseli - będę utwórz interfejs UIView o szerokości 280 pikseli, umieść wszystkie kafelki w tym miejscu, a następnie użyj funkcji Autolayout, aby wyśrodkować to UIView na urządzeniu.

W ten sposób, jeśli użytkownik obraca urządzenie, nie ma problemu!

Czy to najlepszy sposób? Również będę musiał pozwolić użytkownikowi przeciągnąć płytki z tego UIView i do innego miejsca na ekranie. Czy to będzie możliwe?

Odpowiedz

4

Trzy podejścia do mnie:

  1. Myślę, że twoje rozwiązanie użycia widoku kontenera jest całkowicie w porządku. Ale nie musisz kłopotać się z określaniem rozmiaru zdjęć. Możesz po prostu zdefiniować relację między kontenerem a widokami obrazu, a to zmieni rozmiar kontenera, aby dostosować się do nieodłącznego rozmiaru widoków obrazu (lub jeśli wyraźnie zdefiniujesz rozmiar widoków obrazu, to też jest w porządku). A następnie można wyśrodkować pojemnik (i nie dać mu żadnych wyraźnych ograniczeń szerokość/wysokość):

    // create container 
    
    UIView *containerView = [[UIView alloc] init]; 
    containerView.backgroundColor = [UIColor clearColor]; 
    containerView.translatesAutoresizingMaskIntoConstraints = NO; 
    [self.view addSubview:containerView]; 
    
    // create image views 
    
    UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1.png"]]; 
    imageView1.translatesAutoresizingMaskIntoConstraints = NO; 
    [containerView addSubview:imageView1]; 
    
    UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"2.png"]]; 
    imageView2.translatesAutoresizingMaskIntoConstraints = NO; 
    [containerView addSubview:imageView2]; 
    
    NSDictionary *views = NSDictionaryOfVariableBindings(containerView, imageView1, imageView2); 
    
    // define the container in relation to the two image views 
    
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView1]-[imageView2]|" options:0 metrics:nil views:views]]; 
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[imageView1]-|" options:0 metrics:nil views:views]]; 
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[imageView2]-|" options:0 metrics:nil views:views]]; 
    
    // center the container 
    
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView 
                     attribute:NSLayoutAttributeCenterX 
                     relatedBy:NSLayoutRelationEqual 
                     toItem:containerView.superview 
                     attribute:NSLayoutAttributeCenterX 
                    multiplier:1.0 
                     constant:0]]; 
    
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView 
                     attribute:NSLayoutAttributeCenterY 
                     relatedBy:NSLayoutRelationEqual 
                     toItem:containerView.superview 
                     attribute:NSLayoutAttributeCenterY 
                    multiplier:1.0 
                     constant:0]]; 
    
  2. Innym powszechnym rozwiązaniem z ograniczeń jest stworzenie dwóch dodatkowych UIView obiektów (czasami nazywane „Widoki spacer”) dla który określisz kolor tła [UIColor clearColor] i umieścisz go po lewej i prawej stronie widoków obrazów, i zdefiniujesz je, aby przejść do marginesów superwizja, i zdefiniujesz odpowiedni widok, aby był taki sam, jak szerokość lewego widoku . Chociaż jestem pewien, że budujesz swoje ograniczenia, gdy idziesz dalej, jeśli zamierzamy napisać język w formacie wizualnym (VFL) dla dwóch obrazów, które mają być wyśrodkowane na ekranie, może to wyglądać następująco:

    @"H:|[leftView][imageView1]-[imageView2][rightView(==leftView)]|" 
    
  3. Alternatywnie, można wyeliminować potrzebę widzenia pojemnika lub dwa dystansowe poglądy na lewo i prawo o tworzeniu NSLayoutAttributeCenterX ograniczenia korzystania constraintWithItem i określając multiplier dla różnych poglądów obrazów tak, że są one rozmieszczone tak, jak chcesz . Chociaż technika ta eliminuje potrzebę stosowania tych dwóch widoków przerywnika, uważam też, że jest trochę mniej intuicyjna.

    Ale może to wyglądać tak:

    [imageViewArray enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { 
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view 
                        attribute:NSLayoutAttributeCenterX 
                        relatedBy:NSLayoutRelationEqual 
                        toItem:view.superview 
                        attribute:NSLayoutAttributeCenterX 
                       multiplier:2.0 * (idx + 1)/([imageViewArray count] + 1) 
                        constant:0]; 
        [view.superview addConstraint:constraint]; 
    }]; 
    

    To wprawdzie zatrudnia nieco inny rozstaw widoków obrazu, ale w niektórych sytuacjach jest w porządku.

Osobiście skłaniam się ku pierwszemu podejściu, ale żadnej z tych prac.

0

Jeśli masz układ siatki, najlepszym rozwiązaniem jest użycie UICollectionView. Jest to wysoce konfigurowalna klasa, którą można skonfigurować dla prawie wszystkich wymagań układu siatki.

muszę jeszcze znaleźć lepszą wprowadzenie do tego, co można zrobić UICollectionView niż WWDC 2012 filmów:

WWDC 2012 Session 205: Introducing Collection Views by Olivier Gutknecht and Luke Hiesterman WWDC 2012 Session 219: Advanced Collection Views and Building Custom Layouts by Luke the Hiesterman

Dobrym internetowej poradnik oparty od Ray Wenderlich jest tutaj: http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12

0

Przy okazji zauważam, że zadałeś drugie pytanie na zakończenie swojego pytania, a mianowicie, jak przeciągnąć widoki obrazów z pojemnika.

Załóżmy, że dokonałeś ograniczeń zgodnie z sugestią zawartą w pytaniu, przy czym kafelki znajdują się w widoku kontenera, który wyśrodkowałeś w głównym widoku (zobacz opcję 1 mojej drugiej odpowiedzi). Można by zapewne napisać obsługi gest Rozpoznawanie które, jak rozpocząć przeciąganie, wyjąć płytkę z listy kontenera z tiles a następnie animować aktualizację ograniczeń odpowiednio:

- (void)handlePan:(UIPanGestureRecognizer *)gesture 
{ 
    static CGPoint originalCenter; 

    if (gesture.state == UIGestureRecognizerStateBegan) 
    { 
     // move the gesture.view out of its container, and up to the self.view, so that as the container 
     // resizes, this view we're dragging doesn't move in the process, too 

     originalCenter = [self.view convertPoint:gesture.view.center fromView:gesture.view.superview]; 
     [self.view addSubview:gesture.view]; 
     gesture.view.center = originalCenter; 

     // now update the constraints for the views still left in the container 

     [self removeContainerTileConstraints]; 
     [self.tiles removeObject:gesture.view]; 
     [self createContainerTileConstraints]; 
     [UIView animateWithDuration:0.5 animations:^{ 
      [self.containerView layoutIfNeeded]; 
     }]; 
    } 

    CGPoint translate = [gesture translationInView:gesture.view]; 
    gesture.view.center = CGPointMake(originalCenter.x + translate.x, originalCenter.y + translate.y); 

    if (gesture.state == UIGestureRecognizerStateEnded) 
    { 
     // do whatever you want when you drop your tile, presumably changing 
     // the superview of the tile to be whatever view you dropped it on 
     // and then adding whatever constraints you need to make sure it's 
     // placed in the right location. 
    } 
} 

To będzie wdzięcznie animować płytek (i, niewidoczny, ich widok kontenera), aby odzwierciedlić, że wyciągnąłeś płytkę z pojemnika.

Po prostu dla kontekstu, pokażę ci, jak utworzyłem pojemnik i płytki, które będą używane z powyższym uchwytem do rozpoznawania gestów. Załóżmy, że masz NSMutableArray, o nazwie tiles, z płytek w stylu Scrabble, które znajdowały się w twoim kontenerze. Można następnie utworzyć pojemnik, dachówek i dołączyć rozpoznawania gestów do każdej płytki tak:

// create the container 

UIView *containerView = [[UIView alloc] init]; 
containerView.backgroundColor = [UIColor lightGrayColor]; 
containerView.translatesAutoresizingMaskIntoConstraints = NO; 
[self.view addSubview:containerView]; 
self.containerView = containerView; // save this for future reference 

// center the container (change this to place it whereever you want it) 

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView 
                 attribute:NSLayoutAttributeCenterX 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:containerView.superview 
                 attribute:NSLayoutAttributeCenterX 
                multiplier:1.0 
                 constant:0]]; 

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView 
                 attribute:NSLayoutAttributeCenterY 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:containerView.superview 
                 attribute:NSLayoutAttributeCenterY 
                multiplier:1.0 
                 constant:0]]; 

// create the tiles (in my case, three random images), populating an array of `tiles` that 
// will specify which tiles the container will have constraints added 

self.tiles = [NSMutableArray array]; 

NSArray *imageNames = @[@"1.png", @"2.png", @"3.png"]; 
for (NSString *imageName in imageNames) 
{ 
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]]; 
    imageView.translatesAutoresizingMaskIntoConstraints = NO; 
    [containerView addSubview:imageView]; 

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; 
    [imageView addGestureRecognizer:pan]; 
    imageView.userInteractionEnabled = YES; 

    [self.tiles addObject:imageView]; 
} 

// add the tile constraints 

[self createContainerTileConstraints]; 

A ty oczywiście potrzebują tych metod użytkowych:

- (void)removeContainerTileConstraints 
{ 
    NSMutableArray *constraintsToRemove = [NSMutableArray array]; 

    // build an array of constraints associated with the tiles 

    for (NSLayoutConstraint *constraint in self.containerView.constraints) 
    { 
     if ([self.tiles indexOfObject:constraint.firstItem] != NSNotFound || 
      [self.tiles indexOfObject:constraint.secondItem] != NSNotFound) 
     { 
      [constraintsToRemove addObject:constraint]; 
     } 
    } 

    // now remove them 

    [self.containerView removeConstraints:constraintsToRemove]; 
} 

- (void)createContainerTileConstraints 
{ 
    [self.tiles enumerateObjectsUsingBlock:^(UIView *tile, NSUInteger idx, BOOL *stop) { 
     // set leading constraint 

     if (idx == 0) 
     { 
      // if first tile, set the leading constraint to its superview 

      [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile 
                     attribute:NSLayoutAttributeLeading 
                     relatedBy:NSLayoutRelationEqual 
                      toItem:tile.superview 
                     attribute:NSLayoutAttributeLeading 
                     multiplier:1.0 
                     constant:0.0]]; 
     } 
     else 
     { 
      // if not first tile, set the leading constraint to the prior tile 

      [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile 
                     attribute:NSLayoutAttributeLeading 
                     relatedBy:NSLayoutRelationEqual 
                      toItem:self.tiles[idx - 1] 
                     attribute:NSLayoutAttributeTrailing 
                     multiplier:1.0 
                     constant:10.0]]; 
     } 

     // set vertical constraints 

     NSDictionary *views = NSDictionaryOfVariableBindings(tile); 

     [tile.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[tile]|" options:0 metrics:nil views:views]]; 
    }]; 

    // set the last tile's trailing constraint to its superview 

    UIView *tile = [self.tiles lastObject]; 
    [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile 
                   attribute:NSLayoutAttributeTrailing 
                   relatedBy:NSLayoutRelationEqual 
                    toItem:tile.superview 
                   attribute:NSLayoutAttributeTrailing 
                   multiplier:1.0 
                   constant:0.0]]; 

}