Swift自定义UIView在EasyTipView内无法正常显示的问题求助
问题描述
我是iOS Swift开发新手,尝试用EasyTipView(一款Swift提示框库)展示自定义UIView,但该自定义视图无法被正确包含在EasyTipView内。同时我用视图控制器管理该自定义视图的生命周期,希望得到解决方向的指点。为简化问题,已移除自定义视图中的其他文本和按钮字段,相关代码如下:
自定义视图代码
import Foundation import UIKit public class ToolTipTemplate: UIView { private let templateAttributes: TemplateAttributes private var toolTipView: EasyTipView? = nil required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } init(templateAttributes: TemplateAttributes) { self.templateAttributes = templateAttributes super.init(frame: .zero) layoutView() } func layoutView() { let content: Tooltip = Tooltip() content.configureValues(text: toolTipTemplateAttributes.byline) addSubview(content) content.useAutolayout() NSLayoutConstraint.activate([ content.centerYAnchor.constraint(equalTo: centerYAnchor), content.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), content.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), content.widthAnchor.constraint(equalTo: widthAnchor), content.heightAnchor.constraint(equalTo: heightAnchor) ]) } override public func didMoveToSuperview() { if superview != nil { // 业务逻辑代码 } } } extension ToolTipTemplate { open class Tooltip: UIView { private let messageLabel = UILabel() private let contentStack = UIStackView() private var messageString: String? { didSet { guard oldValue != messageString else { return } guard let messageString = messageString, messageString.isEmpty == false else { messageLabel.isHidden = true return } messageLabel.attributedText = NSAttributedString( string: messageString, style: .secondary, alignment: .center, foregroundColor: .color(.primary), lineBreakMode: .byWordWrapping ) messageLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) messageLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical) messageLabel.numberOfLines = 0 messageLabel.isHidden = false messageLabel.isUserInteractionEnabled = false } } public init() { super.init(frame: CGRect.zero) setUpContent() } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func configureValues(text: String? = nil) { messageString = text setNeedsUpdateConstraints() } override public func updateConstraints() { setupConstraints() super.updateConstraints() } func setUpContent() { useAutolayout() messageLabel.useAutolayout() // 配置内容栈 contentStack.axis = .horizontal contentStack.spacing = 0.0 contentStack.alignment = .center contentStack.distribution = .fill contentStack.useAutolayout() contentStack.addArrangedSubviews(messageLabel) contentStack.setCustomSpacing(.spacer(.small), after: messageLabel) messageLabel.isHidden = true } func setupConstraints() { removeAllSubviews() // 设置视图 addSubview(contentStack) messageLabel.textAlignment = .natural NSLayoutConstraint.activate([ // 内容视图约束 contentStack.centerYAnchor.constraint(equalTo: centerYAnchor), messageLabel.topAnchor.constraint(equalTo: contentStack.topAnchor), messageLabel.leadingAnchor.constraint(equalTo: contentStack.leadingAnchor), messageLabel.trailingAnchor.constraint(equalTo: contentStack.trailingAnchor), contentStack.leadingAnchor.constraint(equalTo: leadingAnchor), contentStack.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } } }
视图控制器代码
import Foundation public class ToolTipViewController : UIViewController { private let tooltipMessageView: TooltipMessageView private let forView: UIView private let withinSuperview: UIView private var toolTipRef: EasyTipView? = nil init(tooltipMessageView: TooltipMessageView, forView: UIView, withinSuperview: UIView) { self.tooltipMessageView = tooltipMessageView self.forView = forView self.withinSuperview = withinSuperview super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public override func viewDidLoad() { super.viewDidLoad() showTooltip() view = toolTipRef } func showTooltip() { guard let toolTip = tooltipMessageView.getView() as? ToolTipTemplate else { return } toolTipRef = EasyTipView.standard(contentView: toolTip, arrowPosition: .bottom, boundsSize: withinSuperview.bounds.size, dismissOnTap: false, delegate: self) toolTipRef?.show(forView: forView, withinSuperview: withinSuperview) { [weak self] (finished) in guard finished == true else { return } // 业务逻辑代码 } } } extension ToolTipViewController: EasyTipViewDelegate { public func easyTipViewDidDismiss(_ tipView: EasyTipView) { // 业务逻辑代码 toolTipRef = nil } }
解决方向
约束修复
ToolTipTemplate中content视图的约束使用了safeAreaLayoutGuide,但EasyTipView作为父视图不存在安全区域,应改为直接绑定到自身的锚点:content.leadingAnchor.constraint(equalTo: leadingAnchor), content.trailingAnchor.constraint(equalTo: trailingAnchor),Tooltip类的setupConstraints方法中每次更新约束都会调用removeAllSubviews(),这会导致子视图被反复移除重建,破坏布局,直接删除该行代码即可。- 确保自定义视图能通过自动布局计算出明确尺寸:给
messageLabel设置合理的内容压缩/拉伸优先级,或给ToolTipTemplate添加固定宽高约束(如果需要)。
EasyTipView使用修正
- 删除
ToolTipViewController的viewDidLoad中view = toolTipRef这行代码。EasyTipView通过show方法已经完成视图添加和显示,无需将其设为控制器根视图,否则会导致层级混乱。 - 初始化EasyTipView时可移除
boundsSize参数,让库自动根据内容视图尺寸计算提示框大小,避免手动传入的尺寸与实际内容不匹配。
- 删除
生命周期管理优化
- 若需用视图控制器管理自定义视图生命周期,应将自定义视图添加到控制器的
view上,再把控制器的view作为EasyTipView的contentView,确保控制器生命周期方法正常触发:// 在showTooltip方法中修改 guard let toolTip = tooltipMessageView.getView() as? ToolTipTemplate else { return } view.addSubview(toolTip) // 让toolTip充满控制器view toolTip.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ toolTip.topAnchor.constraint(equalTo: view.topAnchor), toolTip.leadingAnchor.constraint(equalTo: view.leadingAnchor), toolTip.trailingAnchor.constraint(equalTo: view.trailingAnchor), toolTip.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) toolTipRef = EasyTipView.standard(contentView: self.view, arrowPosition: .bottom, dismissOnTap: false, delegate: self)
- 若需用视图控制器管理自定义视图生命周期,应将自定义视图添加到控制器的
内容的提问来源于stack exchange,提问作者Sabya
相关产品推荐
相关产品推荐

