22

我正在尝试找出使用UISegmentedControliPhone 应用程序的“最佳”方式。我在 stackoverflow 上阅读了一些帖子,并看到了一些人的想法,但我无法完全找出最好的方法来做到这一点。我指的帖子是:

从 UISegmentedControl 更改视图 以及 如何使用 UISegmentedControl 切换视图?

似乎选项是:

  • 在 IB 中添加每个视图并将它们放在彼此之上,然后显示/隐藏它们
  • 在 IB 中分别创建每个子视图,然后在主视图中创建一个容器以填充您需要的子视图
  • 设置一个非常高或非常宽UIView的动画,并根据所选片段向左/向右或向上/向下设置动画
  • 使用 aUITabBarController换出子视图 - 看起来很傻
  • 对于表格,重新加载表格并cellForRowAtIndex根据所选的段选项从不同的数据源或部分填充表格(我的应用程序不是这种情况)

那么哪种方法最适合子视图/非表方法?哪个最容易实现?您能否分享一些该方法的示例代码?

谢谢!

4

3 回答 3

19

我在 iPad 应用程序中也遇到过这个要求。

我想到的解决方案是为每种视图样式创建专门的视图控制器,以处理与这些视图相关的业务逻辑(即与每个段相关),并以编程方式将它们作为子视图添加/删除到“管理”控制器以响应选定的段索引更改。

为此,必须创建一个额外的 UIViewController 子类来管理 UISegmentedControl 更改,并添加/删除子视图。

下面的代码完成了所有这些工作,同时还处理了一些注意事项/附加事项:

  • viewWillAppear/viewWillDisappear/etc,不会在子视图上自动调用,需要通过“管理”控制器告知
  • viewWillAppear/viewWillDisappear/etc,当它位于导航控制器中时,不会在“管理”控制器上调用,因此导航控制器委托
  • 如果您想从段的子视图中推送到导航堆栈,则需要回调“管理”视图来执行此操作,因为子视图是在导航层次结构之外创建的,并且不会对导航控制器的引用。
  • 如果在导航控制器场景中使用,后退按钮会自动设置为段的名称。

界面:

@interface SegmentManagingViewController : UIViewController <UINavigationControllerDelegate> {
    UISegmentedControl    * segmentedControl;
    UIViewController      * activeViewController;
    NSArray               * segmentedViewControllers;
}

@property (nonatomic, retain) IBOutlet UISegmentedControl * segmentedControl;
@property (nonatomic, retain) UIViewController            * activeViewController;
@property (nonatomic, retain) NSArray                     * segmentedViewControllers;

@end

执行:

@interface SegmentManagingViewController ()
- (void)didChangeSegmentControl:(UISegmentedControl *)control;
@end

@implementation SegmentManagingViewController

@synthesize segmentedControl, activeViewController, segmentedViewControllers;

- (void)viewDidLoad {
    [super viewDidLoad];

    UIViewController * controller1 = [[MyViewController1 alloc] initWithParentViewController:self];
    UIViewController * controller2 = [[MyViewController2 alloc] initWithParentViewController:self];
    UIViewController * controller3 = [[MyViewController3 alloc] initWithParentViewController:self];

    self.segmentedViewControllers = [NSArray arrayWithObjects:controller1, controller2, controller3, nil];
    [controller1 release];
    [controller2 release];
    [controller3 release];

    self.navigationItem.titleView = self.segmentedControl =
    [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Seg 1", @"Seg 2", @"Seg 3", nil]];
    self.segmentedControl.selectedSegmentIndex = 0;
    self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

    [self.segmentedControl addTarget:self action:@selector(didChangeSegmentControl:) forControlEvents:UIControlEventValueChanged];

    [self didChangeSegmentControl:self.segmentedControl]; // kick everything off
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.activeViewController viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.activeViewController viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.activeViewController viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.activeViewController viewDidDisappear:animated];
}

#pragma mark -
#pragma mark UINavigationControllerDelegate control

// Required to ensure we call viewDidAppear/viewWillAppear on ourselves (and the active view controller)
// inside of a navigation stack, since viewDidAppear/willAppear insn't invoked automatically. Without this
// selected table views don't know when to de-highlight the selected row.

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [viewController viewDidAppear:animated];
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [viewController viewWillAppear:animated];
}

#pragma mark -
#pragma mark Segment control

- (void)didChangeSegmentControl:(UISegmentedControl *)control {
    if (self.activeViewController) {
        [self.activeViewController viewWillDisappear:NO];
        [self.activeViewController.view removeFromSuperview];
        [self.activeViewController viewDidDisappear:NO];
    }

    self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex];

    [self.activeViewController viewWillAppear:NO];
    [self.view addSubview:self.activeViewController.view];
    [self.activeViewController viewDidAppear:NO];

    NSString * segmentTitle = [control titleForSegmentAtIndex:control.selectedSegmentIndex];
    self.navigationItem.backBarButtonItem  = [[UIBarButtonItem alloc] initWithTitle:segmentTitle style:UIBarButtonItemStylePlain target:nil action:nil];
}

#pragma mark -
#pragma mark Memory management

- (void)dealloc {
    self.segmentedControl = nil;
    self.segmentedViewControllers = nil;
    self.activeViewController = nil;
    [super dealloc];
}

@end

希望这可以帮助。

于 2010-05-23T08:32:21.290 回答
11

我会选择您提到的第二个选项,在 IB 中创建子视图并将它们换入和换出主视图。这将是一个很好的使用UIViewController, unsubclassed 的机会:在您的初始设置中,使用创建一个控制器-initWithNibName:bundle:(其中第一个参数是包含单个子视图的 NIB 的名称,第二个参数是nil)并将其添加view为您的子视图必要时的主视图。这将有助于保持低内存占用:UIViewController当收到内存警告时,a 的默认行为是如果它没有超级视图,则释放它的视图。只要您从视图层次结构中删除隐藏视图,您就可以将控制器保留在内存中,而不必担心释放任何内容。

(根据评论进行编辑:)

您不需要子类UIViewController化,但您确实需要为每个视图单独的 XIB。您也不需要向 IB 中的包含视图添加任何内容。

实例变量,在处理所有这些的任何类的接口中:

 UIViewController *controllerOne;
 UIViewController *controllerTwo;

 UIViewController *currentController;

 IBOutlet UIView *theContainerView;

在您的设置中(-applicationDidFinishLaunching:或其他)

 controllerOne = [[UIViewController alloc] initWithNibName:@"MyFirstView" bundle:nil];
 controllerTwo = [[UIViewController alloc] initWithNibName:@"MySecondView" bundle:nil];

要切换到控制器:

 - (void)switchToController:(UIViewController *)newCtl
 {
      if(newCtl == currentController)
           return;
      if([currentController isViewLoaded])
           [currentController.view removeFromSuperview];

      if(newCtl != nil)
           [theContainerView addSubview:newCtl.view];

      currentController = newCtl;
 }

然后只需调用它,例如,

 [self switchToController:controllerOne];
于 2010-01-22T15:46:01.400 回答
3

Here's a great tutorial that explains this concept further: http://redartisan.com/2010/5/26/uisegmented-control-view-switching

and the github location to it: https://github.com/crafterm/SegmentedControlExample.git

于 2012-02-12T23:38:04.990 回答