1

ユーザーの周りの場所を示す位置情報アプリを構築しています。「リスト」と「マップ」の2つのボタンを持つセグメントコントロールがあり
ますNearbyPlacesViewController

ユーザーが「リスト」を押すと、彼の周りの場所のリストを含むテーブル ビューが表示さ
れます。ユーザーが「マップ」を押すと、ビューが反転し、場所がピンとして表示された mapView がユーザーに表示されます。

「リスト」では、UISearchBar と UISearchDisplayController も使用して tableView を検索します
「マップ」では、mapView の横にいくつかのサブビューもあります

現在、すべてのビュー (UITableView, MKMapView, UISearchBarおよびその他...)
とデリゲート メソッド ( UITableViewDelegate, UITableViewDataSource, MKMapViewDelegate, UISearchDisplayDelegate、およびその他..)
を に保持していNearbyPlacesViewControllerます。

ユーザーが「マップ」ボタンを押すと、「リスト」ビュー(tableView、検索バーなど)に関連するすべてのビューを非表示にし、「マップ」ビュー(mapView、mapView、いくつかの他のサブビュー...)、その後UIView transitionWithView、それらの間でアニメーションを反転するために使用します。

すべてが機能しますが、少し乱雑に見えます。その結果、多くのコードとデリゲート メソッドを含む大きな NearbyPlacesViewController が作成されます。

別のviewControllersでそれを行う方が良いですか?
もしそうなら、どうすればいいですか?ListViewController と MapViewController を作成して、NearbyViewController に配置する必要がありますか?
それらの間でモデルを共有するにはどうすればよいですか?

4

6 に答える 6

3

View Controller コンテインメントがどのように見えるか (iOS 5) を理解するために、これは 1 つの方法です。これは、コンテナー ビュー コントローラー (ビューには 2 つの子ビュー コントローラーを切り替えるためのセグメント化されたコントロールがあります)、2 つのランダムな子ビュー コントローラー、およびデータを格納するモデル クラス (によってアクセスできます) の 4 つのクラスで構成されます。 2 つの子ビュー コントローラー)。

最初に、セグメント化されたコントロールを使用してコンテナー ビュー コントローラーを作成します (子ビュー コントローラーのビューが配置されるフレームを基本的に定義する 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

私のモデルには、オブジェクトの配列と文字列の 2 つのデータ要素しかありませんが、当然、やりたいことは何でもできます。ヘッダーだけを表示します (実装の詳細は簡単で面白くないため)。

//  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

これにより、2 つのビューに個別のビュー コントローラーを使用する方法、それらを切り替える方法、モデルを両方からアクセスできるようにする方法について理解していただければ幸いです。これはかなり単純な例ですが、機能的です。私が提案するいくつかの最適化がありますが、うまくいけば、これが正しい方向に進むのに十分です.

于 2012-06-25T04:12:37.653 に答える
3

いくつかのオプションが私に飛びつきます:

  1. を使用するUISegmentedControlと、2 つの画面に 2 つのUIViewオブジェクトを作成でき (インターフェイス ビルダーで作成するか、プログラムで作成できます)、非表示/表示またはaddSubview/removeFromSuperviewの間でジャンプすることができます。すべてのデータが単一のビュー コントローラーによって管理されるため、これは非常に簡単です。ただし、2 つのサブビューが非常に複雑になると、この 1 つのビュー コントローラーが非常に複雑になる可能性があります。しかし、うまくいくはずです。

  2. 個別のビュー コントローラーが必要な場合は、おそらく iOS 5 のビュー コントローラー コンテインメント ( UIViewController リファレンスのコンテナー ビュー コントローラーの実装を参照するか、 WWDC 2011 セッション 102を参照) を追求しますが、これはもう少し複雑です。子コントローラーに渡されるか参照されるコンテナー ビュー コントローラーのプロパティとしてデータを格納できます。

  3. セグメント化されたコントロール UI に慣れていない場合はUITabBarController、このシナリオに最適な を使用できます (これは事実上、前のオプションの順列ですが、コンテナー ビュー コントローラー (この場合はタブ バー コントローラー) は既にあなたのために書かれています)。2 つのビューのそれぞれに 1 つのビュー コントローラーを配置し、データをUITabBarControllerカスタム クラス、シングルトン、永続的なストレージ (ユーザーの既定値、コア データ、sqlite など) などに格納します。

于 2012-06-23T17:29:16.547 に答える
2

segmentedControl を扱うとき、これは私が過去に行った方法です。別のコントローラー クラスを作成して、すべての基になるモデルを処理し、そのコードの一部を削除できる場合もありますが、それは実際には、それをどこまで実行するかによって異なります。

于 2012-06-23T17:15:20.313 に答える
1

ModelViewController を考えてみてください。それらが本当に切り替えているビューだけである場合は、1 つの UIViewController を使用するのが適切です。これは、通常、現在のコントローラーのビューを切り替えることになるため、セグメント化されたコントロールで通常行うことです。コントローラーは、セグメント化されたコントロールの IBAction を確実に処理する必要があります。

デリゲートとデータソースは、意味がある場合に別のクラスに抽出されることを意図しており、あなたの場合はそうです。使用しているさまざまなデリゲートに対して個別のクラスを検討します。これにより、Apple が意図した設計原則に近づけながら、大幅にクリーンアップできます。UITableView デリゲートとデータソースを独自のクラスにまとめることができますが、それ以外は、異なるデリゲートごとに個別のクラスを作成すると、大幅にクリーンアップされます。

于 2012-06-23T17:19:33.797 に答える
1

私の意見では、3 つのビュー コントローラーを用意し、それらの間にモーダル セグエを作成するのが最善の方法です。(ストーリーボードを使用していると思います。)親View Controllerに2つの子(ListとMap)があります。

(1) ストーリーボード (ParentViewController、ListViewController、MapViewController を含む) で、各ボタンから子 VC への Modal Segue を作成します。それらに識別子を与え (showList と showMap がうまく機能します)、Transition:Flip Horizo​​ntal を選択します。

プロトコルとデリゲートを設定します: (1 つの方法を説明します。その後、同じ手順を繰り返します。)

(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 でデリゲートとして設定します。

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

別のビュー コントローラーは、おそらくより適切な方法です。私はそれらを好みます。また、最初のビュー コントローラーを他のビュー コントローラーの親にして、取得したさまざまなビューをレイアウトし、子ビュー コントローラーを必要に応じて親のビューにサブビューとして追加できることも意味します。また、いくつかのカスタム アニメーションを追加したり、後で中継したりすることも容易になります。

于 2012-06-23T17:27:15.673 に答える