iPhone/iPad屏幕方向控制处理

34

这更像是一个一般性的问题,想请人们给我提供指导。我正在学习iPad/iPhone开发,并终于接触到了多方向支持的问题。

我查阅了相当数量的文档,我的书《Beginning iPhone 3 Development》中也有一章专门讲解它。

但我的问题是,如果我要在程序中改变我的控件(甚至为每个方向使用不同的视图),人们如何维护他们的代码库?我可以想象到会有很多问题,比如混乱的代码/成千上万个“if”语句无处不在,这会让我疯狂,即使只做一点小小的UI排列改动。

有没有人有处理这个问题的经验?控制它的好方法是什么?

非常感谢, 马克

6个回答

41

在我的视图控制器中,我使用两种简单的方法来完成此操作:

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [self adjustViewsForOrientation:toInterfaceOrientation];
}

- (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {
    if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
        titleImageView.center = CGPointMake(235.0f, 42.0f);
        subtitleImageView.center = CGPointMake(355.0f, 70.0f);
        ...
    }
    else if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
        titleImageView.center = CGPointMake(160.0f, 52.0f);
        subtitleImageView.center = CGPointMake(275.0f, 80.0f);
        ...
    }
}
为了保持代码的整洁,你可以在单一的if-else条件语句中调用方法,将视图的调整、重载等操作区分开来。

1
我的willRotateToInterface方法从未被调用。你需要在IB中连接或添加监听器吗? - Mike S
这是我正在寻找的一个很好的例子。有其他的例子吗? - Yoon Lee
你正在实现什么?-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{ return YES;} - johndpope
但这不是唯一的方法 willRotateToInterfaceOrientation 如何处理方向变化。旋转完成后,您可以处理 didRotateFromInterfaceOrientation 方法。 - horkavlna
UIInterfaceOrientationUnknown怎么办?如何处理? - Mohammad Zaid Pathan
显示剩余2条评论

7
这真的取决于你正在布局什么。
如果您查看Apple设置应用程序,您会发现他们使用表视图进行布局,并为大多数行使用自定义单元格。通过这样做,您可以通过仅填充单元格的宽度来以相对便宜的方式使简单的界面旋转。即使是在像邮件这样的应用中,每一行都有编辑文本单元格也适用。表甚至可以完全透明,只有按钮或标签可见,因此它们看起来不像表格。
您可以从每个UIView的自动调整掩码中获得很多好处。如果有一个或多个项目可以具有灵活的高度,则通常可以获得在任何方向上看起来不错的接口布局。根据外观如何,有时您可以将所有内容固定在顶部。
在罕见的情况下,如果所有界面元素都适合一个正方形中,则可以在原地旋转它们。
有两种情况需要显式处理方向更改。一种是当视图从旁边移动到下面时。另一种是当您对每个方向都有不同的图像时,例如如果您始终希望全宽。
有时可以解决这两个问题。您可以使用可拉伸的图像或限制每行仅使用一个视图。或者您可能会锁定某些视图的方向。
如果必须更改视图的布局,则有一个明确的layoutSubviews方法。您应该尝试在此方法中处理所有条件布局。仅当视图边界发生更改(例如旋转或为键盘腾出空间)时,才会调用它。为需要响应旋转的每个视图层次结构创建自定义视图,并从那里布置子视图。

2

iPhone SDK采用MVC架构,理论上如果你将所有逻辑(模型)与UI(视图)分开,则只需在一个地方关注UI:即您的视图控制器。对于这些,您可以为每个方向单独设置一个视图控制器,然后只需使用一个if / else来选择要加载的视图控制器。

同样的想法也适用于iPhone / iPad支持,您可以加载另一个视图控制器来处理较大的显示屏。


是的,我曾经看到有人做过类似的事情,这是个好主意,谢谢。 - Mark


0

我不能保证这段代码的可靠性,老实说上面的willRotateToInterfaceOrientation函数效果很好。这里提供另一种方法,使用Facebook为iPhone / iPad开发的FBDialog.m文件。(尽管我认为这是针对Webview的)

以下是要点:

[[NSNotificationCenter defaultCenter] addObserver:self
  selector:@selector(deviceOrientationDidChange:)
  name:@"UIDeviceOrientationDidChangeNotification" object:nil];


- (void)deviceOrientationDidChange:(void*)object {
  UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  if ([self shouldRotateToOrientation:orientation]) {


    CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:duration];
    [self sizeToFitOrientation:YES];
    [UIView commitAnimations];
  }
}


-(CGAffineTransform)transformForOrientation {
  UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  if (orientation == UIInterfaceOrientationLandscapeLeft) {
    return CGAffineTransformMakeRotation(M_PI*1.5);
  } else if (orientation == UIInterfaceOrientationLandscapeRight) {
    return CGAffineTransformMakeRotation(M_PI/2);
  } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
    return CGAffineTransformMakeRotation(-M_PI);
  } else {
    return CGAffineTransformIdentity;
  }
}

- (void)sizeToFitOrientation:(BOOL)transform {
  if (transform) {
    self.transform = CGAffineTransformIdentity;
  }

  CGRect frame = [UIScreen mainScreen].applicationFrame;
  CGPoint center = CGPointMake(
    frame.origin.x + ceil(frame.size.width/2),
    frame.origin.y + ceil(frame.size.height/2));

  CGFloat scale_factor = 1.0f;
  if (FBIsDeviceIPad()) {
    // On the iPad the dialog's dimensions should only be 60% of the screen's
    scale_factor = 0.6f;
  }

  CGFloat width = floor(scale_factor * frame.size.width) - kPadding * 2;
  CGFloat height = floor(scale_factor * frame.size.height) - kPadding * 2;

  _orientation = [UIApplication sharedApplication].statusBarOrientation;
  if (UIInterfaceOrientationIsLandscape(_orientation)) {
    self.frame = CGRectMake(kPadding, kPadding, height, width);
  } else {
    self.frame = CGRectMake(kPadding, kPadding, width, height);
  }
  self.center = center;

  if (transform) {
    self.transform = [self transformForOrientation];
  }
}

0
在你的问题中,你写道:
“我可以想象到有很多关于意大利面代码/成千上万的‘if’检查遍布各处的问题,这会让我疯狂,因为要对UI排列进行一小改动。”
避免这种情况的一种方法是创建一个视图层次结构,从一开始就将iPhone/iPad特定更改的处理分开。您只需要设置每个设备最初加载的视图即可。然后,您像通常一样创建一个视图控制器,但还要子类化您创建的视图控制器。每个设备都有一个子类。这就是您可以放置设备特定代码(如方向处理)的地方。就像这样:
MyViewController.h            // Code that is used on both devices
    MyViewController_iPhone.h // iPhone specific code, like orientation handling
    MyViewController_iPad.h   // iPad specific code, like orientation handling

如果您对这种方法感兴趣,我建议您阅读this article。它以非常好的方式解释了这个方法。

文章提到的其中一件事是:

--引用开始--

这种模式的美妙之处在于,我们不必在代码中添加看起来像这样的垃圾:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    // The device is an iPad running iPhone 3.2 or later.
    // set up the iPad-specific view
} else {
    // The device is an iPhone or iPod touch.
    // set up the iPhone/iPod Touch view
}

---引用结束--

希望这能帮到你。祝你好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接