From 1cedc988384ead1d470f107e6a0914428f6705c5 Mon Sep 17 00:00:00 2001 From: mh <729263080@qq.com> Date: Fri, 31 Oct 2025 18:53:40 +0800 Subject: [PATCH] chat setting font size --- .../Chat/Setting/Cell/ChatFontCell.swift | 263 +++++++++++++++--- 1 file changed, 222 insertions(+), 41 deletions(-) diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Setting/Cell/ChatFontCell.swift b/Visual_Novel_iOS/Src/Modules/Chat/Setting/Cell/ChatFontCell.swift index c15e815..1c7121a 100644 --- a/Visual_Novel_iOS/Src/Modules/Chat/Setting/Cell/ChatFontCell.swift +++ b/Visual_Novel_iOS/Src/Modules/Chat/Setting/Cell/ChatFontCell.swift @@ -6,6 +6,7 @@ // import UIKit +import SnapKit // font struct FontRow: RowModel { @@ -13,57 +14,129 @@ struct FontRow: RowModel { let icon: String let title: String var cellReuseID: String { "ChatFontCell" } - func cellHeight(tableWidth: CGFloat) -> CGFloat { 50 } + func cellHeight(tableWidth: CGFloat) -> CGFloat { 80 } } class ChatFontCell: ChatSettingBaseCell, CellConfigurable { + private var currentFontSize: Int = 20 + private let minFontSize: Int = 10 + private let maxFontSize: Int = 30 + lazy var iconImgView: UIImageView = { - let imgView = UIImageView(image: UIImage(named: "role_exchange_mode")) + let imgView = UIImageView(image: UIImage(named: "role_font")) return imgView }() lazy var titleLab: UILabel = { let lab = UILabel() - lab.text = "XL-0826-32K" - lab.font = UIFont.boldSystemFont(ofSize: 14) + lab.text = "Font size" + lab.font = UIFont.systemFont(ofSize: 14) lab.textColor = UIColor(hex: "#666666") return lab }() - lazy var fontSub: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "role_setting_font_sub"), for: .normal) - btn.addTarget(self, action: #selector(fontSubTap), for: .touchUpInside) - return btn - }() - - lazy var fontAdd: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "role_setting_font_add"), for: .normal) - btn.addTarget(self, action: #selector(fontAddTap), for: .touchUpInside) - return btn - }() - - lazy var fontLab: UILabel = { + lazy var fontSizeLab: UILabel = { let lab = UILabel() lab.text = "20" lab.font = UIFont.systemFont(ofSize: 14) lab.textColor = UIColor(hex: "#999999") - lab.textAlignment = .center + lab.textAlignment = .right return lab }() + lazy var sliderContainer: UIView = { + let view = UIView() + return view + }() + + lazy var minusButton: UIButton = { + let btn = UIButton(type: .custom) + // 优先使用图片,否则显示蓝色文字 + if let image = UIImage(named: "role_setting_font_sub") { + btn.setImage(image, for: .normal) + } else { + btn.setTitle("A-", for: .normal) + btn.setTitleColor(UIColor(hex: "#0066FF"), for: .normal) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium) + } + btn.addTarget(self, action: #selector(decreaseFontSize), for: .touchUpInside) + return btn + }() + + lazy var plusButton: UIButton = { + let btn = UIButton(type: .custom) + // 优先使用图片,否则显示蓝色文字 + if let image = UIImage(named: "role_setting_font_add") { + btn.setImage(image, for: .normal) + } else { + btn.setTitle("A+", for: .normal) + btn.setTitleColor(UIColor(hex: "#0066FF"), for: .normal) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium) + } + btn.addTarget(self, action: #selector(increaseFontSize), for: .touchUpInside) + return btn + }() + + lazy var slider: UISlider = { + let slider = UISlider() + slider.minimumValue = Float(minFontSize) + slider.maximumValue = Float(maxFontSize) + slider.value = Float(currentFontSize) + + // 设置灰色 track(根据图片描述) + slider.minimumTrackTintColor = UIColor(hex: "#E0E0E0") + slider.maximumTrackTintColor = UIColor(hex: "#E0E0E0") + + // 自定义白色圆形 thumb + let thumbSize: CGFloat = 24 + UIGraphicsBeginImageContextWithOptions(CGSize(width: thumbSize, height: thumbSize), false, 0.0) + if let context = UIGraphicsGetCurrentContext() { + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(x: 0, y: 0, width: thumbSize, height: thumbSize)) + // 添加阴影效果使thumb更突出 + context.setShadow(offset: CGSize(width: 0, height: 1), blur: 2, color: UIColor.black.withAlphaComponent(0.2).cgColor) + } + let thumbImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + slider.setThumbImage(thumbImage, for: .normal) + slider.setThumbImage(thumbImage, for: .highlighted) + + // 添加所有相关的事件监听,确保拖动流畅 + slider.addTarget(self, action: #selector(sliderValueChanged(_:)), for: .valueChanged) + slider.addTarget(self, action: #selector(sliderTouchDown(_:)), for: .touchDown) + slider.addTarget(self, action: #selector(sliderTouchUp(_:)), for: [.touchUpInside, .touchUpOutside]) + + return slider + }() + + private lazy var tickMarksContainer: UIView = { + let view = UIView() + view.backgroundColor = .clear + return view + }() + + private var tickMarks: [UIView] = [] + func configure(with row: RowModel) { guard let row = row as? FontRow else { return } titleLab.text = row.title iconImgView.image = UIImage(named: row.icon) + + // 更新容器高度 + updateContainerHeight(80) + + if let size = Int(row.count) { + currentFontSize = max(minFontSize, min(maxFontSize, size)) + updateFontSize(currentFontSize, updateSlider: true) + } } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - configureViews() + setupTickMarks() } required init?(coder: NSCoder) { @@ -71,48 +144,156 @@ class ChatFontCell: ChatSettingBaseCell, CellConfigurable { } func configureViews() { - containerView.addSubview(iconImgView) containerView.addSubview(titleLab) - containerView.addSubview(fontSub) - containerView.addSubview(fontAdd) - containerView.addSubview(fontLab) + containerView.addSubview(fontSizeLab) + containerView.addSubview(sliderContainer) + + // 添加顺序很重要:先添加 slider,再添加 tick marks 覆盖在上面 + sliderContainer.addSubview(minusButton) + sliderContainer.addSubview(plusButton) + sliderContainer.addSubview(slider) + sliderContainer.addSubview(tickMarksContainer) + + // 确保 tick marks 在最上层,但不拦截触摸事件 + sliderContainer.bringSubviewToFront(tickMarksContainer) iconImgView.snp.makeConstraints { make in - make.centerY.equalToSuperview() + make.top.equalToSuperview().offset(12) make.left.equalToSuperview().offset(12) make.width.height.equalTo(21) } titleLab.snp.makeConstraints { make in - make.centerY.equalToSuperview() + make.centerY.equalTo(iconImgView) make.left.equalTo(iconImgView.snp.right).offset(9) - make.right.equalTo(fontSub.snp.left).offset(-5) } - fontAdd.snp.makeConstraints { make in - make.centerY.equalToSuperview() + fontSizeLab.snp.makeConstraints { make in + make.centerY.equalTo(iconImgView) make.right.equalToSuperview().inset(20) - make.width.height.equalTo(40) } - fontLab.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.right.equalTo(fontAdd.snp.left).offset(-10) + sliderContainer.snp.makeConstraints { make in + make.top.equalTo(iconImgView.snp.bottom).offset(12) + make.left.right.equalToSuperview().inset(12) + make.bottom.equalToSuperview().inset(12) + make.height.equalTo(30) } - fontSub.snp.makeConstraints { make in + minusButton.snp.makeConstraints { make in + make.left.centerY.equalToSuperview() + make.width.equalTo(30) + } + + plusButton.snp.makeConstraints { make in + make.right.centerY.equalToSuperview() + make.width.equalTo(30) + } + + slider.snp.makeConstraints { make in + make.left.equalTo(minusButton.snp.right).offset(8) + make.right.equalTo(plusButton.snp.left).offset(-8) make.centerY.equalToSuperview() - make.right.equalTo(fontLab.snp.left).offset(-10) - make.width.height.equalTo(40) + make.height.equalTo(30) + } + + // tick marks 覆盖在 slider 上,但允许触摸穿透到 slider + tickMarksContainer.snp.makeConstraints { make in + make.left.right.equalTo(slider) + make.centerY.equalTo(slider) + make.height.equalTo(slider).offset(4) // 稍微高一点以包含 tick marks + } + + // 确保 tick marks 不拦截触摸事件 + tickMarksContainer.isUserInteractionEnabled = false + } + + private func setupTickMarks() { + // 创建5个小点作为tick marks(均匀分布) + let tickCount = 5 + for _ in 0.. 0 else { return } + let totalWidth = tickMarksContainer.bounds.width + // 5个点,4个间隔,均匀分布 + let spacing = totalWidth / 4.0 + let tickSize: CGFloat = 4 + let centerY = tickMarksContainer.bounds.height / 2.0 + + for (index, tick) in tickMarks.enumerated() { + let centerX = CGFloat(index) * spacing + tick.frame = CGRect( + x: centerX - tickSize / 2.0, + y: centerY - tickSize / 2.0, + width: tickSize, + height: tickSize + ) + tick.layer.cornerRadius = tickSize / 2.0 + } + } + + @objc private func decreaseFontSize() { + if currentFontSize > minFontSize { + currentFontSize -= 1 + updateFontSize(currentFontSize, updateSlider: true) + } + } + + @objc private func increaseFontSize() { + if currentFontSize < maxFontSize { + currentFontSize += 1 + updateFontSize(currentFontSize, updateSlider: true) + } + } + + @objc private func sliderValueChanged(_ slider: UISlider) { + // 连续拖动时实时更新 + let newValue = Int(round(slider.value)) + if newValue != currentFontSize { + currentFontSize = newValue + updateFontSize(currentFontSize, updateSlider: false) // 不更新slider避免循环 + } + } + + @objc private func sliderTouchDown(_ slider: UISlider) { + // 开始拖动 + } + + @objc private func sliderTouchUp(_ slider: UISlider) { + // 结束拖动,确保值对齐到整数 + let newValue = Int(round(slider.value)) + currentFontSize = newValue + updateFontSize(currentFontSize, updateSlider: true) + } + + private func updateFontSize(_ size: Int, updateSlider: Bool = true) { + fontSizeLab.text = "\(size)" + + if updateSlider { + slider.setValue(Float(size), animated: false) + } + + // 更新按钮状态(可选:根据需求决定是否禁用) + // minusButton.isEnabled = size > minFontSize + // plusButton.isEnabled = size < maxFontSize + + // 这里可以添加字体大小改变的回调 + // delegate?.fontSizeChanged(to: size) } }