UISegmentedControl 在关闭视图后消失

UISegmented Control Disappears After Dismissing View

在使用底部的 TabBar、顶部的 NavBar 和 Segmented Control 开发此应用时:

我有一个问题,当我点击返回时,带有 UITableView 的视图 A(分段一)在 select 一个单元格并显示一个包含更多详细信息的新视图时,顶部的分段控件将消失,视图 A 中的 TableView 将被向上推。

这并不总是会发生 - 有时会经过多次尝试,有时只会发生一次。我还没有发现与导致它的原因有任何关联。

我发现如果我从分段控件中 select 查看 B,然后返回到视图 A,然后单击其中一个 table 单元格以进入详细信息屏幕,然后单击返回,100% 的时间顶部导航栏随分段控件一起消失。

TabBarItemOneViewController

let segmentOneVC: SegmentOneViewController
let segmentTwoVC: SegmentTwoViewController

var currentViewController: UIViewController

let viewControllerYLoc = 60 // statusBarHeight + topBarHeight
let viewWidth = Helper.getViewWidth()
let tabBarHeight = 40

func pressedSegItem(segControl: UISegmentedControl){

    let viewControllerHeight = Int(self.view.frame.height)

    let viewFrame = CGRect(x: 0, y: viewControllerYLoc, width: Int(viewWidth), height: viewControllerHeight)

    let selectedIndex = segControl.selectedSegmentIndex
    previouslySelectedMyLoadsIndex = selectedIndex

    self.currentViewController.removeFromParentViewController()

    if(selectedIndex == 0){
        currentViewController = segmentOneVC
    }
    else if(selectedIndex == 1){
        currentViewController = segmentTwoVC
    }

    self.view.addSubview(self.currentViewController.view)
    self.currentViewController.didMove(toParentViewController: self)
}

public init() {

    segmentOneVC = SegmentOneViewController(nibName: nil, bundle: nil)
    segmentTwoVC = SegmentTwoViewController(nibName: nil, bundle: nil)

    if(previouslySelectedIndex == 0){
        currentViewController = segmentOneVC
    }
    else{
        currentViewController = segmentTwoVC
    }

    super.init(nibName: nil, bundle: nil)

    self.calculateItems()

    self.addSegmentedControl()

    let viewControllerHeight = (Int(self.view.frame.height) - viewControllerYLoc) - tabBarHeight

    let viewFrame = CGRect(x: 0, y: viewControllerYLoc, width: Int(viewWidth), height: viewControllerHeight)

    self.currentViewController.view.frame = viewFrame
    self.addChildViewController(segmentOneVC)
    self.addChildViewController(segmentTwoVC)
    self.view.addSubview(self.currentViewController.view)
    self.currentViewController.didMove(toParentViewController: self)
}

SegmentOneViewController(注意:SegmentTwoViewController 相同)

let cellReuseIdentifier = "ItemDetailTableViewCell"

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let row = indexPath.row

    let dataItem = self.dataArray[row]

    let itemDetailVC = ItemDetailViewController()
    itemDetailVC.dataItem = dataItem
    self.present(itemDetailVC, animated: true, completion: nil)
}

func addTableView(){
    self.tableView = UITableView()

    tableView.register(ItemDetailTableViewCell.self, forCellReuseIdentifier: self.cellReuseIdentifier)

    tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    tableView.frame = CGRect(x: 0, y: 0, width: Int(viewWidth), height: (Int(self.view.frame.height) - bottomOfTopNavBar) - heightOfTabBar)

    self.view.addSubview(tableView)
}

override func viewDidAppear(_ animated: Bool){
    super.viewDidAppear(animated)

    loadData()

    tableView.dataSource = self
    tableView.delegate = self
}

override func viewDidLoad() {
    super.viewDidLoad()

    addTableView()
}

ItemDetailViewController

// Connected to a back button in a top Navigation Bar
func goBack(){
    self.dismiss(animated: false, completion: nil)
}

漂亮的图形顺便说一句...该插图使您更容易理解您的问题。

此外,我是一个 Obj-C 人,仍在学习 Swift 的细微差别,所以如果我的语法或其他方面不正确,请告诉我。我在使用容器 VC 方面也相对缺乏经验。

我的回复分为两部分。

第一部分是我尝试解决你的问题。

第二部分是我的替代建议,供您考虑。

第一部分

这是我对您代码中 order/sequence 执行的理解...

Parent 使用分段控件查看

  1. public init () :在 parent 视图控制器实例化时,两个 child VCs(segmentOneVC 和 segmentTwoVC) 被实例化,并根据先前的选择分配为 currentViewController。然后将分段控件添加到 TabBarItemOneViewController.

  2. 用户点击分段控件。

  3. 根据用户输入,SegmentOneViewControllerSegmentTwoViewController 视图将作为 subview 添加到 TabBarItemOneViewController.view。 (请注意,初始化 VC 时也会这样做。)

Child 查看

  1. override func viewDidLoad() :视图加载后,调用函数 addTableView

  2. func addTableView() :在这个自定义函数中,您实例化您的 table 视图并将其放置在 SegmentOneViewController 中,我假设它本身是一个 UIViewController.

  3. override func viewDidAppear(_ animated: Bool) :您调用自定义函数 loadData 并设置您的 table 视图数据源和委托。

后退按钮

  1. 用户点击后退按钮。

  2. Child VC 被关闭,TabBarItemOneViewController 成为屏幕上的活动视图。

让我们看看按下后退按钮时视图控制器生命周期中没有发生什么...列表中的第 1 项。

这可以解释不一致。

试试这个...运行 应用程序,点击选项卡控件将您带到 TabBarItemOneViewController。不要点击分段控件。在 table 视图中点击一行。点击 child VC 中的后退按钮。我猜你的分段控件仍然存在。

现在试试这个...运行 应用程序,点击选项卡控件将您带到 TabBarItemOneViewController。点击分段控件。在 table 视图中点击一行。点击 child VC 中的后退按钮。我猜你的分段控件已经不存在了。

为什么?因为我假设的自定义函数 pressedSegItem 有一个分配给分段控件的目标操作,将覆盖您的 init,这是您将分段控件添加到选项卡栏视图控制器的地方。

因此,举例来说,尝试将代码放置在 TabBarItemOneViewController VC 的 viewWillAppear 的覆盖函数中而不是实例化您的分段控件。

所以有几个概念需要考虑...

  • 延迟加载以节省内存分配 - 仅当用户在应用程序中明确请求该功能时才实例化您需要的 objects;
  • UIViewController生命周期中每个函数的执行顺序;和
  • 哪些函数只执行一次,哪些在您的视图成为第一响应者/活动视图时执行。

部分阅读推荐:

  • SO question titled :Looking to understand the iOS UIViewController lifecycle" 提供了很多有用的信息,但请理解,由于 iOS 中 viewDidUnload 的弃用,部分信息不正确 6.

  • 这就是为什么你应该总是去 Apple documentation for UIViewController 参考最新的 API 参考。

第二部分

通过提供此替代方案,我并不是在暗示您的方法不正确,而是建议您考虑一个替代方案。

我一直使用标签栏控制器来更改视图和分段控件来过滤数据集或更改当前视图的外观。

考虑使用 UISegmentedControl 在一个 table 视图中管理或调整数据集。这将减轻对多个视图控制器的需求以及管理它们的杂耍行为。

例如,在为表格视图编写数据源和委托方法/函数时,您可以包含以下代码以确保 table 视图加载并相应响应:

let selectedIndex = segControl.selectedSegmentIndex
if(selectedIndex == 0) {
    rowHeight = 20 'for example
} else {
    rowHeight = 30 'for example
}

然后您需要重新加载 table 视图才能使更改生效。