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

NSView的viewDidMoveToSuperview未调用,如何设置NSTextContainer尺寸?

问题:NSView子类的viewDidMoveToSuperview未触发及NSTextContainer尺寸设置位置优化

我开发了一个基于Storyboard的App,在NSViewController中设置了NSView子类RMTextView。为使NSTextContainer宽度与视图宽度匹配,尝试在func viewDidMoveToSuperview()中设置,但该函数及func viewWillMove(toSuperview newSuperview: NSView?)均未被调用。请问这些函数何时不会触发?目前我可在func viewDidMoveToWindow()中设置textContainer尺寸,但有没有更合适的位置?

补充:RMTextView是Storyboard中ViewController场景的子视图(层级:ViewController->View->RMTextView)

相关代码:

class ViewController: NSViewController {
    @IBOutlet var textView: RMTextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
}

class RMTextView: NSView, CALayerDelegate {
    public var textLayoutManager: NSTextLayoutManager!
    public var textContentStorage: NSTextContentStorage!
    public var textContainer: NSTextContainer!
    internal var fragmentLayerMap: NSMapTable<NSTextLayoutFragment, CALayer>!

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    func commonInit() {
        wantsLayer = true
        contentLayer = CALayer()
        layer?.addSublayer(contentLayer)

        fragmentLayerMap = .weakToWeakObjects()

        textContentStorage = NSTextContentStorage()
        textContainer = NSTextContainer(containerSize: NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
        textLayoutManager = NSTextLayoutManager()
        textLayoutManager.textContainer = textContainer
        textContentStorage.addTextLayoutManager(textLayoutManager)

        textLayoutManager.textViewportLayoutController.delegate = self

        setSelectedRange(NSTextRange(location: textContentStorage.documentRange.location))
    }

    override func viewDidMoveToSuperview() {
        // This is never called
        print("viewDidMoveToSuperview")
    }

    override func viewDidMoveToWindow() {
        // Is there a better place to set the textContainer size to match the view bounds
        if superview != nil {
            textContainer.size = CGSize(width: self.bounds.width, height: .greatestFiniteMagnitude)
        }
    }
}

回答

一、viewDidMoveToSuperview/viewWillMove(toSuperview:)不触发的场景

  • Storyboard/XIB加载视图时:这类场景下视图的层级是一次性构建完成的,视图在初始化阶段就已经被添加到父视图中,不存在"动态添加到父视图"的过程,因此这两个方法不会被触发。它们仅在代码中动态添加/移除视图到父视图时才会调用。
  • 视图从未被添加到父视图:如果视图自创建后一直没有被添加到任何父视图层级中,这两个方法也不会触发。

二、设置NSTextContainer尺寸的更优方案

推荐以下几种更合适的位置,覆盖初始加载和后续尺寸变化的场景:

  1. 重写viewDidLayout()方法
    这个方法会在视图的bounds发生变化时自动调用,既能处理初始加载时的尺寸设置,也能响应窗口调整、布局更新等后续变化,是最适配的方案:
override func viewDidLayout() {
    super.viewDidLayout()
    textContainer.size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
}
  1. KVO监听bounds变化
    在commonInit中添加对bounds属性的监听,实时响应尺寸变化:
// 在commonInit方法内添加
addObserver(self, forKeyPath: "bounds", options: [.new], context: nil)

// 实现监听回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard keyPath == "bounds" else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    textContainer.size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
}

// 记得在deinit中移除监听
deinit {
    removeObserver(self, forKeyPath: "bounds")
}
  1. 在ViewController的viewDidLayout中处理
    如果RMTextView的尺寸依赖于ViewController的视图布局,也可以在ViewController的viewDidLayout方法中设置,但这种方式需要通过IBOutlet引用RMTextView,灵活性不如前两种:
override func viewDidLayout() {
    super.viewDidLayout()
    textView.textContainer.size = CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 01:39:56