如何在UILabel中嵌入可点击UIButton?iOS开发技术求助
实现包含可点击按钮样式的文本容器
直接采用自定义容器视图+UILabel+UIButton的组合方案,彻底解决你遇到的样式限制和点击定位问题——这个方案既能完美实现按钮的圆角、边框、内边距样式,又能利用UIButton原生点击事件保证交互可靠性。
核心实现思路
- 解析原始文本,拆分出普通文本片段和
[]包裹的按钮文本片段 - 用
UIStackView作为容器自动处理排版(包括换行、控件间距) - 对普通文本片段创建
UILabel,对按钮文本片段创建带样式的UIButton - 给按钮绑定原生点击事件,处理交互逻辑
完整代码实现
自定义容器视图类
import UIKit class RQCommentaryContainerView: UIView { // 按钮点击回调,向外传递点击的按钮文本 var onButtonTap: ((String) -> Void)? private let stackView: UIStackView = { let stack = UIStackView() stack.axis = .horizontal stack.alignment = .center stack.spacing = 8 // 文本与按钮、按钮与按钮之间的间距 stack.isLayoutMarginsRelativeArrangement = true stack.layoutMargins = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) // 容器内边距 stack.translatesAutoresizingMaskIntoConstraints = false stack.distribution = .fill return stack }() override init(frame: CGRect) { super.init(frame: frame) setupBaseView() } required init?(coder: NSCoder) { super.init(coder: coder) setupBaseView() } private func setupBaseView() { addSubview(stackView) NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: topAnchor), stackView.leadingAnchor.constraint(equalTo: leadingAnchor), stackView.trailingAnchor.constraint(equalTo: trailingAnchor), stackView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } /// 渲染带按钮的文本内容 func renderCommentary(text: String) { // 清空已有控件 stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } // 正则匹配[]包裹的按钮文本 let pattern = "\\[([^\\[]*)\\]" guard let regex = try? NSRegularExpression(pattern: pattern) else { return } let textRange = NSRange(text.startIndex..., in: text) let matches = regex.matches(in: text, range: textRange) var currentTextPosition = 0 for match in matches { // 添加匹配区间之前的普通文本 let prefixRange = NSRange(location: currentTextPosition, length: match.range.location - currentTextPosition) if prefixRange.length > 0 { let prefixText = (text as NSString).substring(with: prefixRange) let label = createTextLabel(text: prefixText) stackView.addArrangedSubview(label) } // 添加按钮(提取[]内部的文本) let buttonText = (text as NSString).substring(with: match.range(at: 1)) let button = createCommentaryButton(text: buttonText) stackView.addArrangedSubview(button) currentTextPosition = match.range.location + match.range.length } // 添加剩余的尾部普通文本 if currentTextPosition < text.count { let suffixText = (text as NSString).substring(from: currentTextPosition) let label = createTextLabel(text: suffixText) stackView.addArrangedSubview(label) } } /// 创建普通文本Label private func createTextLabel(text: String) -> UILabel { let label = UILabel() label.text = text label.font = UIFont.fontfor(.englishNormal, weight: .regular, style: .body) // 替换为你的自定义字体 label.textColor = .black label.numberOfLines = 0 return label } /// 创建带样式的按钮 private func createCommentaryButton(text: String) -> UIButton { let button = UIButton(type: .system) button.setTitle(text, for: .normal) button.setTitleColor(UIColor.RQColors.switchColor, for: .normal) button.titleLabel?.font = UIFont.fontfor(.englishNormal, weight: .bold, style: .body) // 替换为你的自定义字体 // 设置按钮视觉样式 button.layer.cornerRadius = 4 button.layer.borderWidth = 1 button.layer.borderColor = UIColor.RQColors.switchColor.cgColor button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8) // 按钮内边距 // 绑定点击事件 button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) return button } @objc private func buttonTapped(_ sender: UIButton) { guard let text = sender.titleLabel?.text else { return } onButtonTap?(text) } }
使用示例
// 在ViewController中集成 let commentaryView = RQCommentaryContainerView() commentaryView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(commentaryView) // 设置布局约束 NSLayoutConstraint.activate([ commentaryView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), commentaryView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), commentaryView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) // 渲染示例文本 commentaryView.renderCommentary(text: "这是[第一个可点击按钮],还有[第二个可点击按钮]的演示内容") // 处理按钮点击 commentaryView.onButtonTap = { buttonText in print("点击了按钮:\(buttonText)") }
方案优势对比
- 样式自由度高:直接通过UIButton的layer属性设置圆角、边框,contentEdgeInsets控制内边距,完全满足按钮的视觉需求,不会像富文本那样受限于文本属性
- 点击交互可靠:利用UIButton原生点击事件,不存在自定义字体导致的定位偏差问题,多行文本场景下也能精准响应
- 排版灵活可控:通过UIStackView自动处理换行、间距,也可根据需求调整stackView的alignment、spacing等属性适配不同布局
- 扩展性强:可轻松修改按钮样式、文本样式,或添加按钮的高亮、禁用等状态逻辑
内容的提问来源于stack exchange,提问作者Madu
相关产品推荐
相关产品推荐

