NSTextField添加到AVPlayerView.contentOverlayView后自动贴合内容的问题求助
问题
手动设置NSTextField的frame后,将其添加到AVPlayerView.contentOverlayView时,frame会被自动调整为贴合文本的尺寸,但添加到普通NSView时不会出现这个问题。这是Bug吗?有没有可行的解决办法?
代码示例
class ViewController: NSViewController { override func loadView() { view = NSView() let text = NSTextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30)) text.translatesAutoresizingMaskIntoConstraints = false text.isEditable = false text.backgroundColor = .red let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center text.attributedStringValue = NSAttributedString(string: "asdf", attributes: [.paragraphStyle: paragraph]) view.addSubview(text) // 注释以下3行可解决问题 let playerView = AVPlayerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) view.addSubview(playerView) playerView.contentOverlayView!.addSubview(text) // 取消注释以下5行也可解决问题,但文本框错误尺寸会短暂显示 // DispatchQueue.main.async { // print(text.frame) // text.frame.size.width = 200 // text.removeConstraints(text.constraints) // } } }
原因解析
这不是Bug,是AVPlayerView的contentOverlayView的默认行为。当你把translatesAutoresizingMaskIntoConstraints设为false时,contentOverlayView会自动为子视图生成约束;对于NSTextField这类自带内容尺寸计算的控件,它会添加约束让控件尺寸贴合文本内容,从而覆盖你手动设置的frame。而普通NSView不会有这种自动添加约束的逻辑。
解决办法
1. 添加明确的自定义约束(推荐)
放弃依赖frame,直接给NSTextField添加固定宽高的约束,优先级高于系统自动生成的约束,就能强制保持你需要的尺寸:
class ViewController: NSViewController { override func loadView() { view = NSView() let text = NSTextField() text.translatesAutoresizingMaskIntoConstraints = false text.isEditable = false text.backgroundColor = .red let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center text.attributedStringValue = NSAttributedString(string: "asdf", attributes: [.paragraphStyle: paragraph]) let playerView = AVPlayerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) view.addSubview(playerView) playerView.contentOverlayView!.addSubview(text) // 添加明确的宽高与位置约束 NSLayoutConstraint.activate([ text.widthAnchor.constraint(equalToConstant: 200), text.heightAnchor.constraint(equalToConstant: 30), text.centerXAnchor.constraint(equalTo: playerView.contentOverlayView!.centerXAnchor), text.centerYAnchor.constraint(equalTo: playerView.contentOverlayView!.centerYAnchor) ]) } }
2. 保持translatesAutoresizingMaskIntoConstraints为true
如果你更习惯用frame设置尺寸,不要把translatesAutoresizingMaskIntoConstraints设为false(默认值就是true)。此时系统会根据frame生成约束,contentOverlayView不会额外添加约束来修改尺寸:
class ViewController: NSViewController { override func loadView() { view = NSView() let text = NSTextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30)) // 移除text.translatesAutoresizingMaskIntoConstraints = false这一行 text.isEditable = false text.backgroundColor = .red let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center text.attributedStringValue = NSAttributedString(string: "asdf", attributes: [.paragraphStyle: paragraph]) let playerView = AVPlayerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) view.addSubview(playerView) playerView.contentOverlayView!.addSubview(text) } }
3. 延迟清除自动约束(不推荐,仅特殊场景用)
如果必须用translatesAutoresizingMaskIntoConstraints = false,可以在主线程延迟执行,清除系统自动添加的约束并重置尺寸,但会有短暂的尺寸闪烁:
class ViewController: NSViewController { override func loadView() { view = NSView() let text = NSTextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30)) text.translatesAutoresizingMaskIntoConstraints = false text.isEditable = false text.backgroundColor = .red let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center text.attributedStringValue = NSAttributedString(string: "asdf", attributes: [.paragraphStyle: paragraph]) let playerView = AVPlayerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) view.addSubview(playerView) playerView.contentOverlayView!.addSubview(text) DispatchQueue.main.async { // 重置尺寸 text.frame.size.width = 200 // 移除系统自动添加的宽度约束 text.constraints.forEach { constraint in if constraint.firstAttribute == .width && constraint.constant != 200 { text.removeConstraint(constraint) } } } } }
内容的提问来源于stack exchange,提问作者Nickkk
相关产品推荐
相关产品推荐

