1

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

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

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

目前我 在. UITableView, MKMapView, UISearchBar_
_UITableViewDelegate, UITableViewDataSource, MKMapViewDelegate, UISearchDisplayDelegate
NearbyPlacesViewController

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

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

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

4

6 回答 6

3

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

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

//  ContainerViewController.h

#import <UIKit/UIKit.h>

@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 <Foundation/Foundation.h>

@interface MyModel : NSObject

@property (nonatomic, strong) NSMutableArray *listOfItems;
@property (nonatomic, strong) NSString *displayText;

- (MyModel *)init;

@end

子视图控制器同样简单:

//  FirstContainedViewController.h

#import <UIKit/UIKit.h>

@class MyModel;

@interface FirstContainedViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

@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

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

于 2012-06-25T04:12:37.653 回答
3

我有几个选择:

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

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

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

于 2012-06-23T17:29:16.547 回答
2

在处理 segmentedControl 时,这就是我过去的做法。您也许可以创建一个单独的控制器类来处理所有内容的底层模型并清除其中的一些代码,但这实际上取决于您想要走多远。

于 2012-06-23T17:15:20.313 回答
1

在我看来,最好的选择是拥有三个View Controller 并在它们之间创建 Modal Segue。(我假设您正在使用情节提要。)您的父视图控制器将有两个孩子(列表和地图)。

(1) 在您的故事板(具有 ParentViewController、ListViewController 和 MapViewController)中,创建从每个按钮到子 VC 的 Modal Segue。给他们标识符(showList 和 showMap 会很好用)并选择 Transition:Flip Horizo​​ntal。

设置协议和委托:(我会告诉你如何做,然后重复一遍。)

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

@class ListViewController;

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

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

@property (weak, nonatomic) id <ListViewControllerDelegate> delegate;

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

@synthesize delegate;

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

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

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

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

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

(5b) 然后在prepareForSegue中设置为delegate:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showList"]) {
        [[segue destinationViewController] setDelegate:self];
    }
}
于 2012-06-23T20:01:11.337 回答
1

想想模型视图控制器。如果它们真的只是您要在它们之间切换的视图,那么使用一个 UIViewController 是合适的,这就是我通常对分段控件所做的事情,因为它通常归结为切换当前控制器的视图。控制器绝对应该处理分段控制的 IBAction。

代表和数据源应该在有意义时被提取到一个单独的类中,在您的情况下它确实如此。我会考虑为您正在使用的各种代表单独的类,这将显着清理它,同时也坚持接近 Apple 的设计原则。您可以将 UITableView 委托和数据源放在它们自己的类中,但除此之外,为每个不同的委托创建一个单独的类将大大清理它。

于 2012-06-23T17:19:33.797 回答
1

单独的视图控制器可能是一种更简洁的方法。我更喜欢他们。也意味着您可以使初始视图控制器成为其他视图控制器的父视图,并布置您拥有的各种视图,并在需要时将您的子视图控制器作为子视图添加到父视图中。还可以更轻松地添加一些自定义动画或在以后进行中继。

于 2012-06-23T17:27:15.673 回答