目标c – 使用一个或多个ViewController在视图之间翻转?

我正在构建一个位置应用程序,向用户显示他周围的一些地方。
我有一个NearbyPlacesViewController与一个段控件有两个按钮“列表”和“地图”

如果用户按下“列表” – 我会向他显示一个表格视图,其中包含他周围的地方列表
如果用户按下“地图” – 视图翻转,我向用户显示一个mapView,其中的位置作为引脚。

在“列表”中我还使用UISearchBar和UISearchDisplayController来搜索tableView
在“map”中,我还在mapView旁边有一些其他子视图

目前我保留所有的视图( UITableView, MKMapView, UISearchBar等……)
和委托方法( UITableViewDelegate, UITableViewDataSource, MKMapViewDelegate, UISearchDisplayDelegate等等)
NearbyPlacesViewController

当用户按下“地图”按钮时,我隐藏了与“列表”视图(tableView,搜索栏…)相关的所有视图,并且我取消隐藏了与“地图”视图相关的所有视图(mapView,其他一些子视图……),然后我使用UIView transitionWithView在它们之间进行翻转动画。

一切正常,但似乎有点凌乱,结果是一个很大的NearbyPlacesViewController,有很多代码和委托方法。

用单独的viewControllers做它更好吗?
如果是这样,我该怎么做? 创建ListViewController和MapViewController并将它们放在NearbyViewController中?
我如何在它们之间共享模型?

只是让您了解控制器包含的视图(iOS 5),这是一种方法。 它由四个类组成,容器视图控制器(其视图将具有用于在两个子视图控制器之间切换的分段控件),两个随机子视图控制器,以及我们将存储数据的模型类(可以通过两个子视图控制器)。

首先使用分段控件创建一个容器视图控制器(我还添加了一个UIView,它基本上定义了子视图控制器视图的放置框架,只是为了更容易找出放置该视图的位置):

 // ContainerViewController.h #import  @interface ContainerViewController : UIViewController @property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl; @property (weak, nonatomic) IBOutlet UIView *childView; - (IBAction)changeChild:(id)sender; @end 

然后你实现它:

 // ContainerViewController.m #import "ContainerViewController.h" #import "FirstContainedViewController.h" #import "SecondContainedViewController.h" #import "MyModel.h" @interface ContainerViewController () { FirstContainedViewController *_controller0; SecondContainedViewController *_controller1; MyModel *_model; UIViewController __weak *_currentChildController; // let's keep track of the current } @end @implementation ContainerViewController @synthesize segmentedControl = _segmentedControl; @synthesize childView = _childView; - (void)dealloc { // let's release our child controllers _controller0 = nil; _controller1 = nil; // and release the model, too _model = nil; } // this is my own method to // 1. add the child view controller to the view controller hierarchy; // 2. do the appropriate notification (even though I don't use it, Apple says you should do this, so I will); and // 3. set the frame size to the appropriate size - (void)addChildToThisContainerViewController:(UIViewController *)childController { [self addChildViewController:childController]; [childController didMoveToParentViewController:self]; childController.view.frame = CGRectMake(0.0, 0.0, self.childView.frame.size.width, self.childView.frame.size.height); } - (void)viewDidLoad { [super viewDidLoad]; // let's create our model, our data _model = [[MyModel alloc] init]; // set the segmented index to point to the first one [self.segmentedControl setSelectedSegmentIndex:0]; // let's create our two controllers and provide each a pointer to our model _controller0 = [[FirstContainedViewController alloc] initWithNibName:@"FirstContainedView" bundle:nil]; _controller0.model = _model; _controller1 = [[SecondContainedViewController alloc] initWithNibName:@"SecondContainedView" bundle:nil]; _controller1.model = _model; // let's add them to the view controller hierarchy [self addChildToThisContainerViewController:_controller0]; [self addChildToThisContainerViewController:_controller1]; // let's add the currently selected controller as the "current child controller" and add it to our current view _currentChildController = [self.childViewControllers objectAtIndex:self.segmentedControl.selectedSegmentIndex]; [self.childView addSubview:_currentChildController.view]; } - (void)viewDidUnload { [self setChildView:nil]; [self setSegmentedControl:nil]; [super viewDidUnload]; // Release any retained subviews of the main view. } - (IBAction)segmentedControlValueChanged:(UISegmentedControl *)sender { UIViewController *oldChildController = _currentChildController; UIViewController *newChildController = [self.childViewControllers objectAtIndex:sender.selectedSegmentIndex]; UIViewAnimationOptions options; // let's change the animation based upon which segmented control you select ... you may change this as fits your desired UI if (sender.selectedSegmentIndex == 0) options = UIViewAnimationOptionTransitionFlipFromLeft; else options = UIViewAnimationOptionTransitionFlipFromRight; [self transitionFromViewController:oldChildController toViewController:newChildController duration:0.5 options:options animations:nil completion:nil]; _currentChildController = newChildController; } @end 

我的模型只有两个数据元素,一个对象数组和一个字符串,但显然你可以做任何你想做的事情。 我只是显示标题(因为实现细节是微不足道和无趣的):

 // MyModel.h #import  @interface MyModel : NSObject @property (nonatomic, strong) NSMutableArray *listOfItems; @property (nonatomic, strong) NSString *displayText; - (MyModel *)init; @end 

子视图控制器同样是微不足道的:

 // FirstContainedViewController.h #import  @class MyModel; @interface FirstContainedViewController : UIViewController  @property (nonatomic, weak) MyModel *model; @end 

实现可能看起来像(这是一个简单的例子,但显示了如何从共享模型中检索信息):

 // FirstContainedViewController.m #import "FirstContainedViewController.h" #import "MyModel.h" @implementation FirstContainedViewController @synthesize model = _model; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } #pragma mark - tableview data source delegate methods - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.model.listOfItems count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"fcvc"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; cell.textLabel.text = [self.model.listOfItems objectAtIndex:indexPath.row]; return cell; } @end 

希望这能让您了解如何为两个视图使用单独的视图控制器,如何在它们之间切换,使两者都可以访问模型。 这是一个相当简单的例子,但它很实用。 我可能会建议一些优化,但希望这足以让你朝着正确的方向前进。

有几种选择让我大吃一惊:

  1. 如果您使用UISegmentedControl ,您可以为两个屏幕创建两个UIView对象(您可以在界面构建器中创建它们或以编程方式构建它们)然后您可以在它们之间跳转时隐藏/显示或添加子removeFromSuperview / removeFromSuperview 。 由于所有数据都将由您的单个视图控制器管理,因此这非常简单。 但是,如果这两个子视图变得非常复杂,那么这个单一视图控制器可能会变得非常毛茸茸。 但应该工作正常。

  2. 如果你想要单独的视图控制器,你可能会追求iOS 5的视图控制器包含(请参阅UIViewController参考中的实现容器视图控制器 ,或参见WWDC 2011会话102 ),尽管这有点复杂。 您可以将数据存储为容器视图控制器的属性,这些属性将传递给子控制器或由子控制器引用。

  3. 如果您没有使用分段控件UI,您可以使用非常适合此场景的UITabBarController (它实际上是前一个选项的排列,但容器视图控制器,在这种情况下是标签栏控制器,是已经为你写的)。 一个视图控制器,用于两个视图中的每一个,并将数据存储在UITabBarController自定义类,单例,一些持久存储(如用户默认值,核心数据,sqlite)等中。

在处理segmentedControl时,这就是我过去的做法。 您可能能够创建一个单独的控制器类来处理所有内容的底层模型并清理掉一些代码,但这实际上只取决于您想要采用它的程度。

想想ModelViewController。 如果它们真的只是你要切换的视图,那么使用一个UIViewController是合适的,这就是我通常用分段控件做的事情,因为它通常归结为切换当前控制器的视图。 控制器肯定应该处理分段控制的IBAction。

代表和数据源应该在有意义的时候被提取到一个单独的类中,在你的情况下它就是这样。 我会为你正在使用的各个代表考虑单独的课程,这将大大清理它,同时也坚持苹果的设计原则。 您可以将UITableView委托和数据源保存在它们自己的类中,但除此之外,为每个不同的委托创建一个单独的类将显着清理它。

单独的视图控制器可能是一个更简洁的方法。 我更喜欢他们。 也意味着您可以将初始视图控制器作为其他人的父级,并布置您已获得的各种视图,并将子视图控制器作为子视图添加到父级视图中以及何时需要。 还可以更轻松地添加一些自定义动画或在以后转发。

在我看来,你最好的选择是拥有三个视图控制器并在它们之间创建模态分段。 (我假设你正在使用故事板。)你的父视图控制器有两个孩子(列表和地图)。

(1)在你的故事板(具有ParentViewController,ListViewController和MapViewController)中,从每个按钮创建一个Modal Segue到子VC。 给它们标识符(showList和showMap可以正常工作)并选择Transition:Flip Horizo​​ntal。

设置协议和代理:(我将告诉你如何做一个,然后重复它。)

(2a)在ListViewController.h中添加以上@interface:

 @class ListViewController; @protocol ListViewControllerDelegate - (void)listViewControllerDidFinish:(ListViewController *)controller; @end 

(2b)将委托添加为属性:

 @property (weak, nonatomic) id  delegate; 

(3a)在ListViewController.m中合成:

 @synthesize delegate; 

(3b)并在IBAction按钮方法中委托翻转:

 - (IBAction)flipBack:(id)sender { [self.delegate ListViewControllerDidFinish:self]; } 

(4)在ParentViewController.h中添加最顶部的#import "ListViewController.h"并在@interface 的末尾添加

(5a)在ParentViewController.m中添加符合协议的方法:

 - (void)listViewControllerFinish:(ListViewController *)controller { [self dismissModalViewControllerAnimated:YES]; } 

(5b)然后将其设置为prepareForSegue中的委托:

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"showList"]) { [[segue destinationViewController] setDelegate:self]; } }