2010-04-17 10 views
6

Nota: Questo può essere un duplicato di Subview Doesnt AutoSize When Added to Root View ControllerRegolazione automatica delle dimensioni UIView dopo aver aggiunto alla finestra


Ho un app iPad che passa tra diversi punti di vista nella sua finestra principale. Il codice vista commutazione assomiglia a questo:

- (void)switchToViewController:(UIViewController*)viewController { 
    if (currentViewController != viewController) { 
     [currentViewController.view removeFromSuperview]; 
     currentViewController = viewController; 
     [window addSubview:viewController.view]; 
    } 
} 

Il problema è che, quando la nuova vista (a UISplitView) appare in orientamento orizzontale, non è dimensionato per riempire l'intera finestra. C'è un grande spazio nero vuoto sulla destra. Sembra che la vista sia larga solo 768 pixel, piuttosto che la larghezza di 1024 pixel della finestra orizzontale.

Se ruoto il dispositivo in verticale e quindi di nuovo in orizzontale, la vista si dimensiona correttamente.

Se il dispositivo è in orientamento verticale, tutto funziona correttamente. Anche UISplitView viene ridimensionato correttamente se è la prima vista che mostro. Il problema si verifica solo se si passa ad esso dopo che è stata mostrata un'altra vista, in orizzontale.

Quindi, c'è un modo per forzare iPhone OS a ridimensionare la vista dopo che è stata aggiunta alla finestra?

Ho provato a chiamare sizeToFit e setNeedsLayout. Ho anche provato a impostare la vista bounds nella finestra bounds e ho provato a impostare frame in modo che corrispondesse alla cornice della vista precedente.

risposta

3

questo funziona, ma sembra un po 'hacky:

- (void)switchToViewController:(UIViewController *)viewController { 
    if (viewController != currentViewController) { 
     UIInterfaceOrientation orientation = currentViewController.interfaceOrientation; 
     [currentViewController.view removeFromSuperview]; 

     currentViewController = viewController; 
     UIView *view = viewController.view; 

     // Set appropriate view frame (it won't be autosized by addSubview:) 
     CGRect appFrame = [[UIScreen mainScreen] applicationFrame]; 
     if (UIInterfaceOrientationIsLandscape(orientation)) { 
      // Need to flip the X-Y coordinates for landscape 
      view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width); 
     } 
     else { 
      view.frame = appFrame; 
     } 

     [window addSubview:view]; 
    } 
} 
+0

Non ci sono problemi qui, perché non si calcola il 20px per la barra di navigazione? – basti

1

La finestra può includere altri elementi dell'interfaccia utente oltre alla visualizzazione. La differenza di 20 pixel nell'esempio è l'altezza della barra di stato.

[[UIApplication sharedApplication] statusBarFrame].height; 

Né la finestra né lo schermo ruotano. Ottenere i loro fotogrammi e usarli per una visualizzazione ruotata funzionerà solo se hai cambiato altezza e larghezza.

Se si utilizza un UIViewController, prova a tornare SI da questo metodo:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait 
+0

Le cose non sono solo 20 pixel al largo; sono 320 pixel spenti. Sto restituendo SÌ da shouldAutorotateToInterfaceOrientation :, e tutto mostra l'orientamento corretto. È solo la dimensione della vista che è sbagliata. –

+0

I 20 pixel di cui stavo parlando erano in 1004 vs 1024 e 748 vs 768. Stai lasciando spazio alla barra di stato codificando il valore 20 nella tua larghezza o altezza. – drawnonward

1

Ho avuto lo stesso problema, ma io fissato con queste linee di codice:

- (void)changeRow:(NSNotification *)notification { 
[window addSubview:new.view]; 
[old.view removeFromSuperview]; 
[new.view removeFromSuperview]; 
[window addSubview:new.view]; 

}

È necessario aggiungere la nuova vista, quindi rimuovere il vecchio e il nuovo e quindi aggiungere la nuova vista. Non so perché, ma funziona.

9

Questo è assolutamente possibile! :-)

È possibile controllare il mio repo qui: https://github.com/hfossli/AGWindowView

Si tratterà automaticamente con qualsiasi rotazione e framechanges in modo da non devono preoccuparsi di questo.

Se vi piace di preoccuparsi che allora si può solo tagliare e incollare le parti più importanti

# 1 Aggiungi vista alla finestra

[[UIApplication sharedApplication] keyWindow] addSubview:aView]; 

# 2 Add ascoltatore e aggiornamento vista

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; 

Ricordarsi di togliere la notifica ascolto

[[NSNotificationCenter defaultCenter] removeObserver:self]; 

# 3 Fare la matematica

- (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification 
{ 
    /* 
    This notification is most likely triggered inside an animation block, 
    therefore no animation is needed to perform this nice transition. 
    */ 
    [self rotateAccordingToStatusBarOrientationAndSupportedOrientations]; 
} 

- (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations 
{ 
    UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation; 
    CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation); 
    CGFloat statusBarHeight = [[self class] getStatusBarHeight]; 

    CGAffineTransform transform = CGAffineTransformMakeRotation(angle); 
    CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight]; 

    [self setIfNotEqualTransform:transform frame:frame]; 
} 

- (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame 
{ 
    if(!CGAffineTransformEqualToTransform(self.transform, transform)) 
    { 
     self.transform = transform; 
    } 
    if(!CGRectEqualToRect(self.frame, frame)) 
    { 
     self.frame = frame; 
    } 
} 

+ (CGFloat)getStatusBarHeight 
{ 
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 
    if(UIInterfaceOrientationIsLandscape(orientation)) 
    { 
     return [UIApplication sharedApplication].statusBarFrame.size.width; 
    } 
    else 
    { 
     return [UIApplication sharedApplication].statusBarFrame.size.height; 
    } 
} 

+ (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight 
{  
    CGRect frame = windowBounds; 
    frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0; 
    frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0; 
    frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0; 
    frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0; 
    return frame; 
} 

CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation) 
{ 
    CGFloat angle; 

    switch (orientation) 
    { 
     case UIInterfaceOrientationPortraitUpsideDown: 
      angle = M_PI; 
      break; 
     case UIInterfaceOrientationLandscapeLeft: 
      angle = -M_PI_2; 
      break; 
     case UIInterfaceOrientationLandscapeRight: 
      angle = M_PI_2; 
      break; 
     default: 
      angle = 0.0; 
      break; 
    } 

    return angle; 
} 

UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation) 
{ 
    return 1 << orientation; 
} 

Buona fortuna!

1

La risposta di Fossli è corretta per iPad. Tuttavia, ho un'applicazione universale che dovevo supportare. Pertanto sono necessari alcuni aggiustamenti.

Aggiungere il seguente alla AppDelegate.h

@property (strong, nonatomic) UIImageView *imageView; 

Aggiungere il seguente alla AppDelegate.m

@synthesize imageView; 

- (void)orientationChanged:(NSNotification *)notification 
{ 
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation; 
    if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) || 
      UIInterfaceOrientationIsPortrait(deviceOrientation))) 
    { 
     // May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere. 
     return; 
    } 

    [imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]]; 

    /* 
    iOS Image Sizes 

    iPhone/iPod Portrait 320 x 480 (640 x 960 @2x) 
    iPad   Portrait 768 x 1004 (1536 x 2008 @2x) 
        Landscape 1024 x 748 (2048 x 1496 @2x) 

    iPad window bounds in both orientations 768 x 1024 (needs manual swap in landscape) 
    iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape) 

    Note the size variations between the required default launch image sizes and 
    the size of the window bounds. 
    iPhone/iPod only requires rotations. 
    iPad needs origin or size adjustments depending on orientation. 
    */ 

    CGFloat angle = 0.0; 
    CGRect newFrame = [[self window] bounds]; 

    // How to get size of status bar 
    // Size of status bar gets all wonky on rotations so just set it manually 
    // CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size; 
    CGSize statusBarSize = CGSizeMake(20.0, 20.0); 

    if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown) 
    { 
     angle = M_PI; 
     if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) 
     { 
      newFrame.size.height -= statusBarSize.height; 
     } 
    } 
    else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft) 
    { 
     angle = - M_PI/2.0f;   
     if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) 
     { 
      newFrame.origin.x += statusBarSize.height; 
      newFrame.size.width += statusBarSize.height; 
     } 
    } 
    else if (deviceOrientation == UIInterfaceOrientationLandscapeRight) 
    { 
     angle = M_PI/2.0f; 
     if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) 
     { 
      newFrame.size.width -= statusBarSize.height; 
     } 
    } 
    else 
    { 
     angle = 0.0; 
     if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) 
     { 
      newFrame.origin.y += statusBarSize.height; 
      newFrame.size.height -= statusBarSize.height; 
     } 
    } 

    imageView.transform = CGAffineTransformMakeRotation(angle); 
    imageView.frame = newFrame; 
} 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    // Add background image to window with orientation changes so that it is visible in all views. 
    // A listener is added since subviews do not receive orientation changes. 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil]; 

    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;  
    imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]]; 
    [[self window] addSubview:imageView]; 

    return YES; 
} 

Aggiungere il seguente alla Utility.h

+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation; 

Aggiungere quanto segue Utility.m

+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation 
{ 
    NSString *imageName = nil; 

    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) 
    { 
     if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) 
     { 
      imageName = @"Default-Landscape~ipad.png"; 
     } 
     else 
     { 
      imageName = @"Default-Portrait~ipad.png"; 
     } 
    } 
    else 
    { 
     if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) 
     { 
      imageName = @"Default-Landscape~iphone.png"; 
     } 
     else 
     { 
      imageName = @"Default.png"; 
     } 
    } 

    return imageName; 
} 
+0

Si noti inoltre che se non si visualizza la barra di stato per l'app (e probabilmente si dovrebbe), potrebbe non essere necessario eseguire alcune regolazioni utilizzando l'altezza della barra di stato. – Christopher

0

Le finestre di iOS7 hanno comportamenti diversi con Windows di iOS8/9.

La finestra della tastiera di iOS7 e tutte le finestre di iOS8/9 hanno sempre l'orientamento corretto & dimensioni. In questo modo puoi osservare gli eventi di modifica delle dimensioni e aggiornare la cornice della tua vista.

Ma altre finestre di iOS7 mantengono sempre l'orientamento e la dimensione del ritratto. È necessario aggiornare la trasformazione della vista dopo la rotazione.

È necessario osservatore UIApplicationWillChangeStatusBarOrientationNotification e dimensione aggiornamento del vostro UIView come questo:

@interface MyView : UIView 

@end 

@implementation MyView 

- (instancetype)init 
{ 
    if (self = [super init]) { 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; 
} 

- (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation 
{ 
    CGFloat width = CGRectGetWidth(self.window.bounds); 
    CGFloat height = CGRectGetHeight(self.window.bounds); 
    if (width > height) { 
     CGFloat temp = width; 
     width = height; 
     height = temp; 
    } 
    CGFloat offset = (height - width)/2; 
    CGAffineTransform transform; 
    switch (orientation) { 
     case UIInterfaceOrientationLandscapeLeft: 
      transform = CGAffineTransformMakeTranslation(-offset, offset); 
      transform = CGAffineTransformRotate(transform, -M_PI_2); 
      break; 
     case UIInterfaceOrientationLandscapeRight: 
      transform = CGAffineTransformMakeTranslation(-offset, offset); 
      transform = CGAffineTransformRotate(transform, M_PI_2); 
      break; 
     case UIInterfaceOrientationPortraitUpsideDown: 
      transform = CGAffineTransformMakeRotation(-M_PI); 
      break; 
     default: 
      transform = CGAffineTransformIdentity; 
      break; 
    } 
    self.transform = transform; 
    self.frame = CGRectMake(0, 0, width, height); 
} 

- (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation 
{ 
    CGFloat width = CGRectGetWidth(self.window.bounds); 
    CGFloat height = CGRectGetHeight(self.window.bounds); 
    if (width > height) { 
     CGFloat temp = width; 
     width = height; 
     height = temp; 
    } 
    switch (orientation) { 
     case UIInterfaceOrientationLandscapeLeft: 
     case UIInterfaceOrientationLandscapeRight: 
      self.frame = CGRectMake(0, 0, height, width); 
      break; 
     default: 
      self.frame = CGRectMake(0, 0, width, height); 
      break; 
    } 
} 

- (void)updateWithOrientation:(UIInterfaceOrientation)orientation 
{ 
    BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0; 
    BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")]; 
    if (isIos7 == YES && isKeyboardWindow == NO) { 
     [self updateTransformWithOrientation:orientation]; 
    } else { 
     [self updateFrameWithOrientation:orientation]; 
    } 
} 

- (void)changeOrientationHandler:(NSNotification *)notification 
{ 
    [UIView animateWithDuration:0.25 animations:^{ 
     UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue]; 
     [self updateWithOrientation:orientation]; 
    }]; 
} 

@end