在iOS5中使用UISegmentedControl切换视图控制器

38

我正在尝试一些非常简单的操作,但是不知何故它无法正常工作。我的目标只是使用 UISegmentedControl 在两个视图控制器之间进行切换,就像在应用商店应用程序的“亮点”选项卡中所看到的。

我正在使用 iOS5 和 Storyboards。

这是我的故事板布局:

enter image description here

所以我有一个根视图控制器和两个 UITableViews - 我想要在这两个 TableView 之间切换。

这是实现文件的样子

#import "SegmentedLocationViewController.h"
#import "PastEventsLocationViewController.h"
#import "FutureEventsLocationViewController.h"

@interface SegmentedLocationViewController()
@property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl;
@property (strong, nonatomic) NSArray *viewControllers;
@end


@implementation SegmentedLocationViewController

@synthesize segmentedControl = _segmentedControl;
@synthesize viewControllers = _viewControllers;

- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl*)segmentedControl
{
    NSLog(@"index: %d", segmentedControl.selectedSegmentIndex);
}

- (void)setupViewControllers
{
    PastEventsLocationViewController *pastEventsLocationViewController = [[PastEventsLocationViewController alloc] initWithStyle:UITableViewStylePlain];
    FutureEventsLocationViewController *futureEventsLocationViewController = [[FutureEventsLocationViewController alloc] initWithStyle:UITableViewStylePlain];

    self.viewControllers = [NSArray arrayWithObjects:pastEventsLocationViewController, futureEventsLocationViewController, nil];
}

- (void)setupUI
{
    [self.segmentedControl addTarget:self action:@selector(indexDidChangeForSegmentedControl:) forControlEvents:UIControlEventValueChanged];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setupViewControllers];
    [self setupUI];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

@end

我可以触发开关事件并记录当前选定的索引。但是我不知道接下来该怎么做。

也许有人可以指引我朝着某个方向前进...?


1
你有没有考虑过用不同的值重新加载tableView? - Matthias Bauch
你说得对。我现在会检查一下。也许我想得太复杂了... - MrBr
6个回答

83

这段代码非常适合您的目的,我在我的一个新应用程序中使用它。
它使用了新的UIViewController包含API,可以在您自己的UIViewControllers内部放置其他UIViewControllers,而无需手动转发像viewDidAppear:这样的东西。

- (void)viewDidLoad {
    [super viewDidLoad];
    // add viewController so you can switch them later. 
    UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
    [self addChildViewController:vc];
    vc.view.frame = self.contentView.bounds;
    [self.contentView addSubview:vc.view];
    self.currentViewController = vc;
}
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
    UIViewController *vc = [self viewControllerForSegmentIndex:sender.selectedSegmentIndex];
    [self addChildViewController:vc];
    [self transitionFromViewController:self.currentViewController toViewController:vc duration:0.5 options:UIViewAnimationOptionTransitionFlipFromBottom animations:^{
        [self.currentViewController.view removeFromSuperview];
        vc.view.frame = self.contentView.bounds;
        [self.contentView addSubview:vc.view];
    } completion:^(BOOL finished) {
        [vc didMoveToParentViewController:self];
        [self.currentViewController removeFromParentViewController];
        self.currentViewController = vc;
    }];
    self.navigationItem.title = vc.title;
}

- (UIViewController *)viewControllerForSegmentIndex:(NSInteger)index {
    UIViewController *vc;
    switch (index) {
        case 0:
            vc = [self.storyboard instantiateViewControllerWithIdentifier:@"FooViewController"];
            break;
        case 1:
            vc = [self.storyboard instantiateViewControllerWithIdentifier:@"BarViewController"];
            break;
    }
    return vc;
}

这段内容是我从Ray Wenderlich的书《iOS5 by tutorial》的第22章中获取的。很遗憾,我没有一个公开的教程链接。但是有一个名为“实现UIViewController容器”的WWDC 2011视频。

编辑:

self.typeSegmentedControl 是你的 UISegmentedControl 的出口

self.contentView 是你的容器视图的出口

self.currentViewController 只是一个我们用来存储当前正在使用的 UIViewController 的属性


我没有使用故事板制作它,但当我使用您的代码与nib文件时,它可以工作... 所以感谢您,您得到了我的投票 ;) - MrBr
3
太棒了!我刚刚在两个月后发现了 [self storyboard]。谢谢!! - Damon Aw
这可能看起来像是一个愚蠢的问题,但是当我从头开始时,我会遇到这样的问题:对象类型“ViewController *”上找不到属性“typeSegmentedControl”。我可能做了一些愚蠢的事情,是吗? - user379468
你需要为 typeSegmentedControl (UISegmentedControl) 添加 @property - Matthias Bauch
我将 vc.view.frame = self.contentView.bounds; 移到动画块之外,以避免在转换期间子视图超出 contentView 的边界,否则会导致闪烁。 - hillmark
我不得不在viewDidLoad中实现self.contentView.frame = self.view.frame;,因为在启动应用程序后我的容器大小为0。 - Sebastian Boldt

10

这是Matthias Bauch的解决方案,但我想以Swift的形式分享它! 编辑: 添加一个链接到一个Swift 2.0的演示应用程序。 https://github.com/ahmed-abdurrahman/taby-segmented-control

var currentViewController: UIViewController?
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var segmentedControl: UISegmentedControl!

@IBAction func switchHappeningTabs(sender: UISegmentedControl) {
    if let vc = viewControllerForSelectedSegmentIndex(sender.selectedSegmentIndex) {
        self.addChildViewController(vc)
        self.transitionFromViewController(self.currentViewController!, toViewController: vc, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromRight, animations: {
            self.currentViewController!.view.removeFromSuperview()
            vc.view.frame = self.contentView.bounds
            self.contentView.addSubview(vc.view)
            }, completion: { finished in
                vc.didMoveToParentViewController(self)
                self.currentViewController!.removeFromParentViewController()
                self.currentViewController = vc
            }
        )        
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    if let vc = self.viewControllerForSelectedSegmentIndex(self.segmentedControl.selectedSegmentIndex) {
        self.addChildViewController(vc)
        self.contentView.addSubview(vc.view)
        self.currentViewController = vc
    }
}

func viewControllerForSelectedSegmentIndex(index: Int) -> UIViewController? {
    var vc: UIViewController?
    switch index {
    case 0:
            vc = self.storyboard?.instantiateViewControllerWithIdentifier("FooViewController") as? UIViewController
    case 1:
            vc = self.storyboard?.instantiateViewControllerWithIdentifier("BarViewController") as? UIViewController
    default:
        return nil
    }

    return vc
}

非常感谢!这让我的一天都变得美好了 :) - AlexB
当然,我很乐意 :) - Abdurrahman

4

如果有人想在Swift中实现相同的功能:

import UIKit

class HomeController: UIViewController {

    var currentViewController:UIViewController?

    @IBOutlet weak var homeController: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let initialController:UIViewController = self.viewControllerForSegmentedIndex(0)

        self.addChildViewController(initialController)

        initialController.view.frame = self.homeController.bounds
        self.homeController.addSubview(initialController.view)
        self.currentViewController = initialController

    }

    @IBAction func segmentChanged(sender: UISegmentedControl) {

        let viewCOntroller:UIViewController = viewControllerForSegmentedIndex(sender.selectedSegmentIndex)

        self.addChildViewController(viewCOntroller)

        self.transitionFromViewController(self.currentViewController!, toViewController: viewCOntroller, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromBottom, animations: {
            self.currentViewController?.view.removeFromSuperview()
            viewCOntroller.view.frame = self.homeController.bounds
            self.homeController.addSubview(viewCOntroller.view)

            }, completion:{ finished in

                viewCOntroller.didMoveToParentViewController(self)
                self.currentViewController?.removeFromParentViewController()
                self.currentViewController = viewCOntroller

        })

    }
    func viewControllerForSegmentedIndex(index:Int) -> UIViewController {
        var viewController:UIViewController?
        switch index {
        case 0:
            viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForFirstController")
            break
        case 1:
            viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForSecondController")
            break
        case 2:
            viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForThirdController")
            break
        default:
            break
        }
        return viewController!
    }
}

故事板视图

在此输入图片描述


2

A是根视图控制器,B和C是子视图控制器。当你点击选项卡时,添加子视图控制器。

结果

结果

设计

设计

代码

import UIKit

class SegmentViewController: UIViewController {

    @IBOutlet weak var containerView: UIView!

    var leftViewController: LeftViewController!
    var rightViewController: RightViewController!


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        if let sb = storyboard {
            leftViewController = sb.instantiateViewControllerWithIdentifier("leftViewController") as! LeftViewController

            switchViewController(from: nil, to: leftViewController)
        } else {
            print("storyboard is nil")
        }
    }

    func switchViewController(from fromVC: UIViewController?, to toVC: UIViewController?) {
        if let from = fromVC {
            from.willMoveToParentViewController(nil)
            from.view.removeFromSuperview()
            from.removeFromParentViewController()
        } else {
            print("fromVC is nil")
        }

        if let to = toVC {
            self.addChildViewController(to)
            to.view.frame = CGRectMake(0, 0, containerView.frame.width, containerView.frame.height)
            self.containerView.insertSubview(to.view, atIndex: 0)
            to.didMoveToParentViewController(self)
        } else {
            print("toVC is nil")
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.

        removeViewController()
    }

    func removeViewController() {
        if let leftVC = leftViewController {
            if let _ = leftVC.parentViewController {
                print("leftVC is using")
            } else {
                print("set leftVC = nil")
                leftViewController = nil
            }
        }

        if let rightVC = rightViewController {
            if let _ = rightVC.parentViewController {
                print("rightVC is using")
            } else {
                print("set rightVC = nil")
                rightViewController = nil
            }
        }
    }


    @IBAction func onSegmentValueChanged(sender: UISegmentedControl) {
        UIView.beginAnimations("xxx", context: nil)
        UIView.setAnimationDuration(0.4)
        UIView.setAnimationCurve(.EaseInOut)

        switch sender.selectedSegmentIndex {
        case 0:
            UIView.setAnimationTransition(.FlipFromRight, forView: self.containerView, cache: true)

            if let leftVC = leftViewController {
                switchViewController(from: rightViewController, to: leftVC)
            } else {
                if let sb = storyboard {
                    leftViewController = sb.instantiateViewControllerWithIdentifier("leftViewController") as! LeftViewController
                    switchViewController(from: rightViewController, to: leftViewController)
                } else {
                    print("storyboard is nil")
                }
            }
        default:
            UIView.setAnimationTransition(.FlipFromLeft, forView: self.containerView, cache: true)

            if let rightVC = rightViewController {
                switchViewController(from: leftViewController, to: rightVC)
            } else {
                if let sb = storyboard {
                    rightViewController = sb.instantiateViewControllerWithIdentifier("rightViewController") as! RightViewController
                    switchViewController(from: leftViewController, to: rightViewController)
                } else {
                    print("storyboard is nil")
                }
            }
        }

        UIView.commitAnimations()
    }
}

0
你可以将初始视图控制器嵌入导航控制器中。选择故事板中的视图控制器,然后选择“编辑”->“嵌入”->“导航控制器”。
在你的indexDidChangeForSegmentedControl:方法中,只需将相应的视图控制器推送到导航堆栈中:
- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl*)segmentedControl
{
    [self.navigationController pushViewController:[self.viewControllers objectAtIndex:[segmentedControl.selectedIndex]] animated:YES];

}

但是当您使用故事板时,您的方法并没有太多意义。 我不知道您是否可以使用segues将单个分段控件按钮连接到视图控制器。只需尝试一下。


0

最终我使用了导航控制器来实现类似的功能。

import UIKit

class BaseViewController: UIViewController {

  var currentSegmentIndex = 0

  @IBOutlet weak var segmentedControl: UISegmentedControl!

  override func viewDidLoad() {
    super.viewDidLoad()

    segmentedControl.selectedSegmentIndex = currentSegmentIndex
  }

  @IBAction func segmentedControlChanged(_ sender: Any) {
    let idx = segmentedControl.selectedSegmentIndex
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    switch idx {
    case 0:
      let vc = storyboard.instantiateViewController(
        withIdentifier: "Foo") as! FooViewController
      vc.currentSegmentIndex = 0
      navigationController?.viewControllers = [vc]
      break
    case 1:
      let vc = storyboard.instantiateViewController(
        withIdentifier: "Bar") as! BarViewController
      vc.currentSegmentIndex = 1
      navigationController?.viewControllers = [vc]
      break
    default:
      break
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接