2010-01-22 7 views
23

Ich versuche, die "beste" Möglichkeit herauszufinden, eine UISegmentedControl für eine iPhone-Anwendung zu verwenden. Ich habe ein paar Posts hier auf stackoverflow gelesen und die Ideen einiger Leute gesehen, aber ich kann nicht den besten Weg finden, dies zu tun. Die Beiträge, die ich mich beziehe, sind:UISegmentedControl Best Practice

Changing Views from UISegmentedControl und How do I use a UISegmentedControl to switch views?

Es scheint, dass die Optionen sind:

  • jeder der Ansichten in IB hinzufügen und sie lag an der Spitze der Dann zeigen/verbergen Sie sie.
  • Erstellen Sie jede der Unteransichten separat in IB, dann erstellen Sie einen Container in der Hauptansicht, um mit der Unteransicht zu füllen, die Sie benötigen
  • Richten Sie einen wirklich groß oder sehr breit UIView und animieren sie nach links/rechts oder oben/je nach unten auf das ausgewählte Segment
  • ein UITabBarController Verwenden Sie die Subviews auszulagern - scheint dumm
  • Für Tabellen, die Tabelle neu zu laden und in cellForRowAtIndex und füllen Sie die Tabelle aus unterschiedlichen Datenquellen oder Abschnitten auf der Basis des Segments Option ausgewählt (nicht der Fall für meine app)

So welcher Ansatz ist am besten für subview/nicht-Tabelle Ansätze? Welche ist am einfachsten zu implementieren? Können Sie dem Ansatz Beispielcode geben?

Danke!

Antwort

11

Ich würde mit der zweiten Option gehen, die Sie erwähnen, die Unteransichten in IB erstellen und sie in eine Hauptansicht und aus ihr heraus wechseln. Dies wäre eine gute Gelegenheit sein, UIViewController zu verwenden, unsubclassed: in der Ersteinrichtung, erstellen Sie einen Controller -initWithNibName:bundle: mit (wo der erste Parameter ist der Name des NIB mit dem einzelnen subview, und der zweite Parameter ist nil) und fügen Sie seine view als eine Unteransicht Ihrer Hauptansicht nach Bedarf. Dies hilft, den Speicherbedarf gering zu halten: Das Standardverhalten von UIViewController beim Empfang einer Speicherwarnung besteht darin, die Ansicht zu löschen, wenn keine Superview vorhanden ist. Solange Sie versteckte Ansichten aus der Ansichtshierarchie entfernen, können Sie die Controller im Speicher behalten und sich nicht darum kümmern, etwas zu veröffentlichen.

(als Antwort bearbeitet Kommentar :)

Sie brauchen nicht UIViewController zu Unterklasse, aber Sie tun für jede Ansicht separaten XIBs benötigen. Sie müssen der übergeordneten Sicht in IB auch nichts hinzufügen.

Instanzvariablen, in der Schnittstelle von was auch immer Klasse all dies ist der Umgang:

UIViewController *controllerOne; 
UIViewController *controllerTwo; 

UIViewController *currentController; 

IBOutlet UIView *theContainerView; 

In Ihrem Setup (-applicationDidFinishLaunching: oder was auch immer)

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

an einen Controller wechseln:

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

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

     currentController = newCtl; 
} 

Dann rufen Sie das einfach mit, zB,

[self switchToController:controllerOne]; 
+0

Nur um zu klären ... Sagen Sie eine separate UIViewController .h, .m zu schaffen, und .xib-Datei in IB für jede Unteransicht, instanziieren Sie jeweils einen von ihnen in der viewDidLoad der "containing" -Ansicht, fügen Sie eine UIView in IB zur enthaltenden Ansicht hinzu, und tun Sie was ?? –

19

Ich bin auf diese Anforderung auch in einer iPad-Anwendung gestoßen.

Die Lösung kam ich zu war für jeden Stil Sichtfach Ansicht-Controller zu erstellen, um diese Ansichten (zu jedem Segment, dh. In Bezug) und programmatisch hinzufügen/entfernen sie als Subviews ein Zusammenhang Business-Logik zu handhaben Steuerung des Reglers als Reaktion auf das ausgewählte Segment Indexänderungen.

Um dies zu tun, muss man eine zusätzliche UIViewController-Unterklasse erstellen, die UISegmentedControl-Änderungen verwaltet und die Untersichten hinzufügt/entfernt.

Der folgende Code tut dies alles, auch ein paar Einschränkungen/Extras die Pflege:

  • viewWillAppear/viewWillDisappear/etc, sind nicht auf die Subviews genannt automatisch und müssen über die erzählt werden ‚Verwaltung‘ Controller
  • viewWillAppear/viewWillDisappear/etc, sind nicht auf ‚Verwaltung‘ genannt Controller, wenn es innerhalb einer Navigationssteuerung, daher der Navigationscontroller delegieren
  • Wenn Sie auf einen Navigationsstapel schieben möchten aus einer 01 herausIn der Unteransicht des Segments müssen Sie die Ansicht ' ' wieder aufrufen, da die Unteransicht außerhalb der Navigationshierarchie erstellt wurde und keine Referenz auf die Navigation hat.
  • Bei Verwendung innerhalb eines Navigationssteuerungs-Szenarios wird die Zurück-Schaltfläche automatisch auf den Namen des Segments gesetzt.

Schnittstelle:

@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 

Umsetzung:

@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 

Hoffnung, das hilft.

+1

Abgestimmt! Es gibt einen kleinen Speicherverlust in der -didChangeSegmentedControl: Methode. Die letzte Anweisung sollte sein: self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle: segmentTitle Stil: UIBarButtonItemStylePlain target: nil action: nil] autorelease]; Ein ähnliches Speicherleck ist in viewDidLoad: self.navigationItem.titleView = self.segmentiertControl = [[[UISegmentedControl Alloc] InitWithItems: [NSArray ArrayWithObjects: @ "Seg 1", @ "Seg 2", @ "Seg 3", Nil ]] Autorelease]; – Mustafa

+0

Hinzufügen eines Links zu einem weiteren Update, das ich bezüglich dieses Musters gepostet habe - Verwendung eines UINavigationControllers anstelle eines 'Container'-View-Controllers. http: // redartisan.com/2010/6/27/uisegmented-control-view-switching-revisited – crafterm

+0

Aus irgendeinem Grund, wenn ich dies auf einem iPad verwenden, wird der Detailbereich nicht korrekt skaliert, es ist, als ob die Ansichten in voller iPad-Porträt gezeichnet werden. – Convolution