如何在编程 UIKit 中将视图分成较小的视图?

How to separate Views into smaller ones in programmatic UIKit?

我是。试图通过将我的代码分成更小的文件来使我的代码更清晰。当我有一个 ViewController 时,它起作用了!但是,当我尝试添加子视图时,它没有。谁能看看我的代码并告诉我哪里出了问题?

代码:

ViewController(仅查看控制器,当它工作时):

import UIKit

class ViewController: UIViewController {
    private let textField = UITextField()

    override func loadView() {
        let view = UIView()
        self.view = view
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textField)
        view.addConstraint(textField.leadingAnchor.constraint(equalTo: view.leadingAnchor))
        view.addConstraint(textField.trailingAnchor.constraint(equalTo: view.trailingAnchor))
        view.addConstraint(textField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor))
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        textField.becomeFirstResponder()
        
        view.backgroundColor = .red
    }
}

ViewController(尝试添加子视图时)

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let searchViewController = SearchViewController()
        
        view.addSubview(searchViewController.view)
    }
}

搜索ViewController

import UIKit

class SearchViewController: UIViewController {
    private let textField = UITextField()

    override func loadView() {
        let view = UIView()
        self.view = view
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textField)
        view.addConstraint(textField.leadingAnchor.constraint(equalTo: view.leadingAnchor))
        view.addConstraint(textField.trailingAnchor.constraint(equalTo: view.trailingAnchor))
        view.addConstraint(textField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor))
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        textField.becomeFirstResponder()
        
        view.backgroundColor = .red
    }
}

SceneDelegate(向您展示我已成功删除故事板并使程序化 UIKit 可用的附加文件)

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let scene = (scene as? UIWindowScene) else { return }
        
        window = UIWindow(windowScene: scene)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }


}

添加子视图控制器时,您总是需要在子视图控制器 VC 上调用 addChild(_:) on the parent VC and didMove(toParent:)

let searchViewController = SearchViewController()
addChild(viewController)
searchViewController.didMove(toParent: self)
view.addSubview(searchViewController.view)

但最重要的是,您在 ViewController 视图

中根本没有限制 SearchViewController 视图
NSLayoutConstraint.activate([
    searchViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    searchViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
    searchViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    searchViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

您的最终 ViewController 实施应如下所示:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let searchViewController = SearchViewController()
        addChild(viewController)
        searchViewController.didMove(toParent: self)
        view.addSubview(searchViewController.view)
        searchViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            searchViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            searchViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
            searchViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            searchViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
    }
    
}

另一个问题是您使用旧 API 来设置 SearchViewController 内的约束。 addConstraint 的文档说:

When developing for iOS 8.0 or later, set the constraint’s isActive property to true instead of calling the addConstraint method directly. The isActive property automatically adds and removes the constraint from the correct view.

您可以使用 NSLayoutConstraint 上的 activate(_:) 静态函数立即激活它们。

view.addSubview(textField)
NSLayoutConstraint.activate([
    textField.leadingAnchor
        .constraint(equalTo: view.leadingAnchor, constant: 0),
    textField.topAnchor
        .constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
    textField.trailingAnchor
        .constraint(equalTo: view.trailingAnchor, constant: 0),
])

资源: