Jak używać UISegmentedControl do przełączania widoków?

82

Próbuję dowiedzieć się, jak używać różnych stanów UISegmentedControl do przełączania widoków, podobnie jak Apple robi to w App Store podczas przełączania między „Najlepiej płatne” i „Najlepsze bezpłatne”.

Mark Adams
źródło

Odpowiedzi:

113

Najprostszym podejściem jest posiadanie dwóch widoków, w których można przełączać ich widoczność, aby wskazać, który widok został wybrany. Oto przykładowy kod pokazujący, jak można to zrobić, zdecydowanie nie jest to zoptymalizowany sposób obsługi widoków, ale tylko po to, aby zademonstrować, jak można użyć UISegmentControl do przełączania widocznego widoku:

- (IBAction)segmentSwitch:(id)sender {
  UISegmentedControl *segmentedControl = (UISegmentedControl *) sender;
  NSInteger selectedSegment = segmentedControl.selectedSegmentIndex;

  if (selectedSegment == 0) {
    //toggle the correct view to be visible
    [firstView setHidden:NO];
    [secondView setHidden:YES];
  }
  else{
    //toggle the correct view to be visible
    [firstView setHidden:YES];
    [secondView setHidden:NO];
  }
}


Możesz oczywiście jeszcze bardziej zmienić współczynnik w kodzie, aby ukryć / pokazać właściwy widok.

Ronnie Liew
źródło
4
„zdecydowanie nie jest to zoptymalizowany sposób obsługi widoków” - dlaczego?
Adam Waite
3
@AdamWaite, ponieważ wszystkie widoki muszą być trwale przechowywane w pamięci. Jeśli Twoje widoki są zbyt skomplikowane i / lub zawierają wiele innych elementów, wpłynie to na ogólną wydajność. Ten fragment kodu również może zostać zrefaktorowany.
Stas
@Stas Masz rację, lepiej podzielić logikę na wiele kontrolerów widoku, z których każdy jest odpowiedzialny za swoje własne działania i zachowania
tf.alves
używanie widoków kontenerów może prowadzić do problemów z paskiem nawigacji. zwłaszcza gdy używasz półprzezroczystego. z mojego doświadczenia nie jest to
godne
45

W moim przypadku moje widoki są dość złożone i nie mogę po prostu zmienić ukrytej właściwości różnych widoków, ponieważ zajmowałoby to zbyt dużo pamięci.

Wypróbowałem kilka rozwiązań i żadne z nich nie działało dla mnie ani nie działało błędnie, szczególnie z titleView paska navBar nie zawsze pokazującym segmentedControl podczas wypychania / wyskakiwania widoków.

Znalazłem ten wpis na blogu dotyczący problemu, który wyjaśnia, jak to zrobić we właściwy sposób. Wygląda na to, że podczas WWDC'2010 skorzystał z pomocy inżynierów Apple, aby opracować to rozwiązanie.

http://redartisan.com/2010/6/27/uisegmented-control-view-switching-revisited

Rozwiązanie w tym linku to bez wątpienia najlepsze rozwiązanie problemu, jakie znalazłem do tej pory. Przy niewielkiej regulacji działał również dobrze z tabBarem na dole

Marc M.
źródło
Dziękuję za wspaniałe znalezisko. Zdecydowanie ładne i eleganckie rozwiązanie dla tej metodologii.
Shiun
1
Próbowałem, aby to działało poprawnie z paskiem narzędzi na dole bez powodzenia, stackoverflow.com/questions/4748120/… Czy możesz mi pomóc?
Erik
Czy istnieje sposób na uzyskanie animacji odwracania w poziomie między widokami? Czy działa tylko bez animacji?
aneuryzm
Tak, wygląda na to, że jest to świetne rozwiązanie, ale jak dostosować to do pracy z tabBarController z navigationControllers już w środku?
Vladimir Stazhilov
2
Na szczęście wdrożenie kontrolera widoku kontenera działało bez zarzutu! Nawet segue działa zgodnie z oczekiwaniami.
jweyrich
17

Lub jeśli jest to tabela, możesz ponownie załadować tabelę i w cellForRowAtIndex wypełnić tabelę z różnych źródeł danych na podstawie wybranej opcji segmentu.

lostInTransit
źródło
7

Jeden z pomysłów polega na tym, aby widok z segmentowanymi kontrolkami miał widok kontenera, który można wypełnić różnymi widokami podrzędnymi (dodać jako jedyny widok podrzędny widoku kontenera, gdy segmenty są przełączane). Możesz nawet mieć osobne kontrolery widoku dla tych widoków podrzędnych, chociaż musisz przekazywać ważne metody, takie jak „viewWillAppear” i „viewWillDisappear”, jeśli ich potrzebujesz (i będą musieli powiedzieć, pod jakim kontrolerem nawigacyjnym się znajdują).

Generalnie działa to całkiem nieźle, ponieważ możesz rozłożyć widok główny za pomocą kontenera w IB, a podglądy wypełnią dowolną przestrzeń, na jaką pozwala im kontener (upewnij się, że maski autoresize są poprawnie skonfigurowane).

Kendall Helmstetter Gelner
źródło
2

Z odpowiedzi @Ronnie Liew tworzę to:

//
//  ViewController.m
//  ResearchSegmentedView
//
//  Created by Ta Quoc Viet on 5/1/14.
//  Copyright (c) 2014 Ta Quoc Viet. All rights reserved.
//
#define SIZE_OF_SEGMENT 56
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize theSegmentControl;
UIView *firstView;
UIView *secondView;
CGRect leftRect;
CGRect centerRect;
CGRect rightRect;
- (void)viewDidLoad
{
    [super viewDidLoad];
    leftRect = CGRectMake(-self.view.frame.size.width, SIZE_OF_SEGMENT, self.view.frame.size.width, self.view.frame.size.height-SIZE_OF_SEGMENT);
    centerRect = CGRectMake(0, SIZE_OF_SEGMENT, self.view.frame.size.width, self.view.frame.size.height-SIZE_OF_SEGMENT);
    rightRect = CGRectMake(self.view.frame.size.width, SIZE_OF_SEGMENT, self.view.frame.size.width, self.view.frame.size.height-SIZE_OF_SEGMENT);

    firstView = [[UIView alloc] initWithFrame:centerRect];
    [firstView setBackgroundColor:[UIColor orangeColor]];
    secondView = [[UIView alloc] initWithFrame:rightRect];
    [secondView setBackgroundColor:[UIColor greenColor]];
    [self.view addSubview:firstView];
    [self.view addSubview:secondView];

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)segmentSwitch:(UISegmentedControl*)sender {
    NSInteger selectedSegment = sender.selectedSegmentIndex;
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.2];
    if (selectedSegment == 0) {
        //toggle the correct view to be visible
        firstView.frame = centerRect;
        secondView.frame = rightRect;
    }
    else{
        //toggle the correct view to be visible
        firstView.frame = leftRect;
        secondView.frame = centerRect;
    }
    [UIView commitAnimations];
}
@end
Envil
źródło
2

Przypisz .H w

 UISegmentedControl *lblSegChange;

- (IBAction)segValChange:(UISegmentedControl *) sender

Zadeklaruj .M

- (IBAction)segValChange:(UISegmentedControl *) sender
{

 if(sender.selectedSegmentIndex==0)
 {
  viewcontroller1 *View=[[viewcontroller alloc]init];
  [self.navigationController pushViewController:view animated:YES];
 }
 else 
 {
  viewcontroller2 *View2=[[viewcontroller2 alloc]init];
  [self.navigationController pushViewController:view2 animated:YES];
 }
} 
Anbu.Karthik
źródło
2

Wersja Swift:

Nadrzędny kontroler widoku jest odpowiedzialny za ustawienie rozmiaru i położenia widoku każdego podrzędnego kontrolera widoku. Widok podrzędnego kontrolera widoku staje się częścią hierarchii widoków nadrzędnego kontrolera widoku.

Zdefiniuj leniwe właściwości:

private lazy var summaryViewController: SummaryViewController = {
   // Load Storyboard
   let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

   // Instantiate View Controller
   var viewController = storyboard.instantiateViewController(withIdentifier: "SummaryViewController") as! SummaryViewController

   // Add View Controller as Child View Controller
   self.add(asChildViewController: viewController)

   return viewController
}()

private lazy var sessionsViewController: SessionsViewController = {
    // Load Storyboard
    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

    // Instantiate View Controller
    var viewController = storyboard.instantiateViewController(withIdentifier: "SessionsViewController") as! SessionsViewController

    // Add View Controller as Child View Controller
    self.add(asChildViewController: viewController)

    return viewController
}()

Pokaż / ukryj kontrolery widoku potomnego:

private func add(asChildViewController viewController: UIViewController) {
    // Add Child View Controller
    addChildViewController(viewController)

    // Add Child View as Subview
    view.addSubview(viewController.view)

    // Configure Child View
    viewController.view.frame = view.bounds
    viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    // Notify Child View Controller
    viewController.didMove(toParentViewController: self)
}

private func remove(asChildViewController viewController: UIViewController) {
    // Notify Child View Controller
    viewController.willMove(toParentViewController: nil)

    // Remove Child View From Superview
    viewController.view.removeFromSuperview()

    // Notify Child View Controller
    viewController.removeFromParentViewController()
}

Zarządzaj SegmentedControl tapEvent

private func updateView() {
    if segmentedControl.selectedSegmentIndex == 0 {
        remove(asChildViewController: sessionsViewController)
        add(asChildViewController: summaryViewController)
    } else {
        remove(asChildViewController: summaryViewController)
        add(asChildViewController: sessionsViewController)
    }
}

Oczywiście możesz używać w swoich podrzędnych klasach kontrolerów widoku:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    print("Summary View Controller Will Appear")
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("Summary View Controller Will Disappear")
}

Źródła: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/

SlavisaPetkovic
źródło
1
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
Basile Perrenoud
2
@BasilePerrenoud Właśnie zaktualizowałem odpowiedź o kluczową i najważniejszą część rozwiązania.
SlavisaPetkovic
1

Szybka wersja Swift:

@IBAction func segmentControlValueChanged(_ sender: UISegmentedControl) {

    if segmentControl.selectedSegmentIndex == 0 {

        // do something
    } else {

        // do something else
    }
}
Świetlana przyszłość
źródło