You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 4:如何让ViewController加载不同UIView以减少控制器数量

问题:如何让单个UIViewController加载不同自定义UIView?

我平时开发多视图应用习惯一个ViewController对应一个视图,但现在做大型应用不想创建15+个ViewController,想让单个ViewController加载不同的UIView。比如菜单栏选“Settings”时,让selectionController加载Settings View。目前已经能导航到对应控制器且导航栏显示正确,但不知道怎么让控制器加载指定UIView。

相关代码如下:

选中选项时的调用函数

//Whatever option selected will affect which view is inherited
let option = self.menuOptions[indexPath.row]
if option.name != "Home" {
    self.homeController?.displayController(menuOption: menuOption, view: self.settingsView)
}

Home Controller代码

class HomeController: UIViewController {
    lazy var menu: Menu = {
        let menuLauncher = Menu()
        menuLauncher.homeController = self
        return menuLauncher
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Home"
        view.backgroundColor = Color.blue
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(displayMenu))
    }
    
    func displayController(menuOption: MenuOption, view: UIView) {
        let selectionController = UIViewController()
        selectionController.navigationItem.title = menuOption.name
        selectionController.view.backgroundColor = Color.blue
        //Why dosen't this present the view I have assigned to it??
        selectionController.view.addSubview(HomeView(frame: view.frame))
        navigationController?.navigationBar.tintColor = UIColor.white
        navigationController?.pushViewController(selectionController, animated: true)
    }
    
    @objc func displayMenu() {
        menu.displayMenu()
    }
}

现在这段代码没法正常加载指定UIView,求解决办法?


解决方案

你的问题主要出在几个细节上,我来一步步帮你修正:

1. 修复View的初始化与约束问题

你现在直接用HomeView(frame: view.frame)初始化,而且没有设置自动布局约束,这会导致自定义View的位置、大小不符合预期,甚至完全看不到。正确的做法是让自定义View自动填满控制器的view:

func displayController(menuOption: MenuOption, view: UIView) {
    let selectionController = UIViewController()
    selectionController.navigationItem.title = menuOption.name
    selectionController.view.backgroundColor = .blue
    
    // 直接使用传入的自定义View实例,不要重新创建HomeView!
    let customView = view
    customView.translatesAutoresizingMaskIntoConstraints = false
    selectionController.view.addSubview(customView)
    
    // 添加约束让自定义View填满控制器的view
    NSLayoutConstraint.activate([
        customView.topAnchor.constraint(equalTo: selectionController.view.topAnchor),
        customView.leadingAnchor.constraint(equalTo: selectionController.view.leadingAnchor),
        customView.trailingAnchor.constraint(equalTo: selectionController.view.trailingAnchor),
        customView.bottomAnchor.constraint(equalTo: selectionController.view.bottomAnchor)
    ])
    
    navigationController?.navigationBar.tintColor = .white
    navigationController?.pushViewController(selectionController, animated: true)
}

2. 避免无效的View实例创建

你原来的代码里,明明已经传入了self.settingsView,却又重新创建了HomeView,这就导致你传进来的SettingsView根本没被用到,完全是画蛇添足,直接使用传入的view参数即可。

3. 优化方案:封装通用的ContentViewController

如果要多次复用这种“加载不同自定义View”的逻辑,最好封装一个专门的控制器类,让代码更整洁易维护:

class ContentViewController: UIViewController {
    private let contentView: UIView
    
    init(contentView: UIView, title: String) {
        self.contentView = contentView
        super.init(nibName: nil, bundle: nil)
        self.navigationItem.title = title
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .blue
        
        contentView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(contentView)
        
        NSLayoutConstraint.activate([
            contentView.topAnchor.constraint(equalTo: view.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

之后在displayController里可以这样调用:

func displayController(menuOption: MenuOption, view: UIView) {
    let contentVC = ContentViewController(contentView: view, title: menuOption.name)
    navigationController?.navigationBar.tintColor = .white
    navigationController?.pushViewController(contentVC, animated: true)
}

4. 检查自定义View的初始化逻辑

确保你的SettingsView(或其他自定义View)初始化正确,比如如果是用XIB创建的,要正确加载:

class SettingsView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        // 加载XIB的逻辑
        let nib = UINib(nibName: "SettingsView", bundle: nil)
        if let nibView = nib.instantiate(withOwner: self).first as? UIView {
            nibView.frame = bounds
            nibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            addSubview(nibView)
        }
        // 纯代码创建的话,在这里添加子控件和约束
    }
}

这样修改后,你的自定义View应该就能正常显示在控制器中了。

内容的提问来源于stack exchange,提问作者Chandler Long

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:21:38