chat setting mode
This commit is contained in:
parent
17f5eba096
commit
ef162eea2c
|
|
@ -6,9 +6,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
|
||||||
class ChatSettingBaseCell: UITableViewCell {
|
class ChatSettingBaseCell: UITableViewCell {
|
||||||
|
|
||||||
|
var containerHeightConstraint: Constraint?
|
||||||
|
|
||||||
lazy var containerView: UIView = {
|
lazy var containerView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
view.backgroundColor = UIColor(hex: "#F6F6F6")
|
view.backgroundColor = UIColor(hex: "#F6F6F6")
|
||||||
|
|
@ -33,7 +36,11 @@ class ChatSettingBaseCell: UITableViewCell {
|
||||||
containerView.snp.makeConstraints { make in
|
containerView.snp.makeConstraints { make in
|
||||||
make.left.right.equalToSuperview().inset(20)
|
make.left.right.equalToSuperview().inset(20)
|
||||||
make.top.bottom.equalToSuperview().inset(2.5)
|
make.top.bottom.equalToSuperview().inset(2.5)
|
||||||
make.height.equalTo(45.0)
|
containerHeightConstraint = make.height.equalTo(45.0).constraint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateContainerHeight(_ height: CGFloat) {
|
||||||
|
containerHeightConstraint?.update(offset: height)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
|
||||||
// icon title 声音头像 箭头
|
// icon title 声音头像 箭头
|
||||||
struct ImageRow: RowModel {
|
struct ImageRow: RowModel {
|
||||||
|
|
@ -14,8 +15,20 @@ struct ImageRow: RowModel {
|
||||||
let showAvatar: Bool
|
let showAvatar: Bool
|
||||||
let showArrow: Bool
|
let showArrow: Bool
|
||||||
let showSwitch: Bool
|
let showSwitch: Bool
|
||||||
|
let subItems: [ImageRow]? // 子项列表
|
||||||
|
var isExpanded: Bool = false // 展开状态
|
||||||
|
|
||||||
var cellReuseID: String { "ChatSwipeCell" }
|
var cellReuseID: String { "ChatSwipeCell" }
|
||||||
func cellHeight(tableWidth: CGFloat) -> CGFloat { 50 }
|
func cellHeight(tableWidth: CGFloat) -> CGFloat { 50 }
|
||||||
|
|
||||||
|
init(icon: String, title: String, showAvatar: Bool = false, showArrow: Bool = false, showSwitch: Bool = false, subItems: [ImageRow]? = nil) {
|
||||||
|
self.icon = icon
|
||||||
|
self.title = title
|
||||||
|
self.showAvatar = showAvatar
|
||||||
|
self.showArrow = showArrow
|
||||||
|
self.showSwitch = showSwitch
|
||||||
|
self.subItems = subItems
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatSwipeCell: ChatSettingBaseCell, CellConfigurable {
|
class ChatSwipeCell: ChatSettingBaseCell, CellConfigurable {
|
||||||
|
|
@ -61,13 +74,28 @@ class ChatSwipeCell: ChatSettingBaseCell, CellConfigurable {
|
||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private var currentRow: ImageRow?
|
||||||
|
|
||||||
func configure(with row: RowModel) {
|
func configure(with row: RowModel) {
|
||||||
guard let row = row as? ImageRow else { return }
|
guard let row = row as? ImageRow else { return }
|
||||||
|
currentRow = row
|
||||||
iconImgView.image = UIImage(named: row.icon)
|
iconImgView.image = UIImage(named: row.icon)
|
||||||
titleLab.text = row.title
|
titleLab.text = row.title
|
||||||
avatarView.isHidden = !row.showAvatar
|
avatarView.isHidden = !row.showAvatar
|
||||||
arrowImgView.isHidden = !row.showArrow
|
arrowImgView.isHidden = !row.showArrow
|
||||||
switchControl.isHidden = !row.showSwitch
|
switchControl.isHidden = !row.showSwitch
|
||||||
|
|
||||||
|
// 更新箭头状态
|
||||||
|
updateArrowState(isExpanded: row.isExpanded)
|
||||||
|
|
||||||
|
// 更新container高度(固定为50)
|
||||||
|
updateContainerHeight(50)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateArrowState(isExpanded: Bool) {
|
||||||
|
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||||
|
self?.arrowImgView.transform = isExpanded ? CGAffineTransform(rotationAngle: .pi / 2.0) : .identity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
////
|
||||||
|
//// SubModeCell.swift
|
||||||
|
//// Visual_Novel_iOS
|
||||||
|
////
|
||||||
|
//// Created by mh on 2025/10/29.
|
||||||
|
////
|
||||||
|
//
|
||||||
|
//import UIKit
|
||||||
|
//
|
||||||
|
//// SubItemsRow - 用于标识子项容器cell
|
||||||
|
//struct SubItemsRow: RowModel {
|
||||||
|
// let subItems: [ImageRow]
|
||||||
|
// var cellReuseID: String { "SubItemsContainerCell" }
|
||||||
|
// func cellHeight(tableWidth: CGFloat) -> CGFloat {
|
||||||
|
// let maxDisplayCount = 4
|
||||||
|
// let displayCount = min(subItems.count, maxDisplayCount)
|
||||||
|
// return CGFloat(displayCount) * 40 // 每行40高度
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SubItemsContainerCell - 子项容器cell
|
||||||
|
//class SubModeCell: UITableViewCell, CellConfigurable {
|
||||||
|
//
|
||||||
|
// private var subItems: [ImageRow] = []
|
||||||
|
// private var tableViewHeightConstraint: Constraint?
|
||||||
|
//
|
||||||
|
// lazy var tableView: UITableView = {
|
||||||
|
// let tableView = UITableView(frame: .zero, style: .plain)
|
||||||
|
// tableView.separatorStyle = .none
|
||||||
|
// tableView.delegate = self
|
||||||
|
// tableView.dataSource = self
|
||||||
|
// tableView.backgroundColor = UIColor(hex: "#F5F5FF")
|
||||||
|
// tableView.showsVerticalScrollIndicator = true
|
||||||
|
// tableView.isScrollEnabled = true
|
||||||
|
// tableView.register(SubItemCell.self, forCellReuseIdentifier: "SubItemCell")
|
||||||
|
// tableView.estimatedRowHeight = 40
|
||||||
|
// tableView.rowHeight = 40
|
||||||
|
// tableView.contentInset = .zero
|
||||||
|
// tableView.scrollIndicatorInsets = .zero
|
||||||
|
// tableView.bounces = false
|
||||||
|
// tableView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] // 左下、右下
|
||||||
|
// tableView.layer.cornerRadius = 15
|
||||||
|
// return tableView
|
||||||
|
// }()
|
||||||
|
//
|
||||||
|
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
// super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
// setupViews()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// required init?(coder: NSCoder) {
|
||||||
|
// fatalError("init(coder:) has not been implemented")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func setupViews() {
|
||||||
|
// backgroundColor = .clear
|
||||||
|
// selectionStyle = .none
|
||||||
|
//
|
||||||
|
// contentView.addSubview(tableView)
|
||||||
|
//
|
||||||
|
// tableView.snp.makeConstraints { make in
|
||||||
|
// make.left.right.equalToSuperview().inset(20)
|
||||||
|
// make.top.bottom.equalToSuperview().inset(2.5)
|
||||||
|
// tableViewHeightConstraint = make.height.equalTo(0).constraint
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func configure(with row: RowModel) {
|
||||||
|
// guard let subItemsRow = row as? SubItemsRow else { return }
|
||||||
|
// subItems = subItemsRow.subItems
|
||||||
|
//
|
||||||
|
// tableView.reloadData()
|
||||||
|
// updateTableViewHeight()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private func updateTableViewHeight() {
|
||||||
|
// let maxDisplayCount = 4
|
||||||
|
// let displayCount = min(subItems.count, maxDisplayCount)
|
||||||
|
// let height = CGFloat(displayCount) * 40
|
||||||
|
//
|
||||||
|
// tableViewHeightConstraint?.update(offset: height)
|
||||||
|
// layoutIfNeeded()
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//extension SubItemsContainerCell: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
// return subItems.count
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
// let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemCell", for: indexPath) as! SubItemCell
|
||||||
|
// let subItem = subItems[indexPath.row]
|
||||||
|
// cell.configure(with: subItem)
|
||||||
|
// return cell
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SubItemCell - 子项cell
|
||||||
|
//class SubItemCell: UITableViewCell {
|
||||||
|
// private let titleLabel = UILabel()
|
||||||
|
// private let iconImageView = UIImageView()
|
||||||
|
//
|
||||||
|
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
// super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
// setupViews()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// required init?(coder: NSCoder) {
|
||||||
|
// fatalError("init(coder:) has not been implemented")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func setupViews() {
|
||||||
|
// backgroundColor = .clear
|
||||||
|
// selectionStyle = .none
|
||||||
|
//
|
||||||
|
// contentView.addSubview(iconImageView)
|
||||||
|
// contentView.addSubview(titleLabel)
|
||||||
|
//
|
||||||
|
// iconImageView.snp.makeConstraints { make in
|
||||||
|
// make.left.equalToSuperview().offset(40)
|
||||||
|
// make.centerY.equalToSuperview()
|
||||||
|
// make.width.height.equalTo(18)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// titleLabel.snp.makeConstraints { make in
|
||||||
|
// make.left.equalTo(iconImageView.snp.right).offset(8)
|
||||||
|
// make.centerY.equalToSuperview()
|
||||||
|
// make.right.equalToSuperview().inset(15)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// titleLabel.font = UIFont.systemFont(ofSize: 13)
|
||||||
|
// titleLabel.textColor = UIColor(hex: "#999999")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func configure(with item: ImageRow) {
|
||||||
|
// iconImageView.image = UIImage(named: item.icon)
|
||||||
|
// titleLabel.text = item.title
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
@ -6,21 +6,50 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
|
||||||
class ChatSettingSwipeView: CLContainer {
|
class ChatSettingSwipeView: CLContainer {
|
||||||
|
|
||||||
var closeAction: (()->Void)?
|
var closeAction: (()->Void)?
|
||||||
|
|
||||||
var sectionTitle: [String] = ["Switch Model", "Sound", "Maximum number of response tokens Maximum number of response tokens", "Appearance", "Background", "Historical Archives"]
|
var sectionTitle: [String] = ["Switch Model", "Sound", "Maximum number of response tokens Maximum number of response tokens", "Appearance", "Background", "Historical Archives"]
|
||||||
var rows: [[RowModel]] = [
|
var rows: [[RowModel]] = []
|
||||||
[ImageRow(icon: "role_exchange_mode", title: "XL-0826-32K", showAvatar: false, showArrow: true, showSwitch: false), ImageRow(icon: "role_text_mode", title: "Short Text Mode", showAvatar: false, showArrow: true, showSwitch: false)],
|
|
||||||
[ImageRow(icon: "role_voice", title: "Voice actor", showAvatar: true, showArrow: true, showSwitch: false), ImageRow(icon: "role_talk", title: "Play dialogue only", showAvatar: false, showArrow: false, showSwitch: true)],
|
|
||||||
[TokenRow(count: "2500")],
|
|
||||||
[FontRow(count: "20", icon: "role_font", title: "Font size"), ImageRow(icon: "role_chat_mode", title: "Chat Mode", showAvatar: false, showArrow: true, showSwitch: false), ImageRow(icon: "role_chat_buttle", title: "Chat buttle", showAvatar: false, showArrow: true, showSwitch: false)],
|
|
||||||
[BackgroundRow(count: 50)],
|
|
||||||
[HistoryRow(time: "", icon: "", title: "", itemCount: 30)]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
// 展开状态管理:section -> rowIndex -> isExpanded
|
||||||
|
private var expandedStates: [Int: [Int: Bool]] = [:]
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
// 初始化rows数据,包含子项(至少10条数据)
|
||||||
|
let modelRow = ImageRow(icon: "role_exchange_mode", title: "XL-0826-32K", showAvatar: false, showArrow: true, showSwitch: false, subItems: [
|
||||||
|
ImageRow(icon: "role_exchange_mode", title: "Sub Item 1", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
ImageRow(icon: "role_exchange_mode", title: "Sub Item 2", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
ImageRow(icon: "role_exchange_mode", title: "Sub Item 3", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
ImageRow(icon: "role_exchange_mode", title: "Sub Item 4", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
ImageRow(icon: "role_exchange_mode", title: "Sub Item 5", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 6", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 7", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 8", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 9", showAvatar: false, showArrow: false, showSwitch: false),
|
||||||
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 10", showAvatar: false, showArrow: false, showSwitch: false)
|
||||||
|
])
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
[modelRow, ImageRow(icon: "role_text_mode", title: "Short Text Mode", showAvatar: false, showArrow: false, showSwitch: true)],
|
||||||
|
[ImageRow(icon: "role_voice", title: "Voice actor", showAvatar: true, showArrow: true, showSwitch: false), ImageRow(icon: "role_talk", title: "Play dialogue only", showAvatar: false, showArrow: false, showSwitch: true)],
|
||||||
|
[TokenRow(count: "2500")],
|
||||||
|
[FontRow(count: "20", icon: "role_font", title: "Font size"), ImageRow(icon: "role_chat_mode", title: "Chat Mode", showAvatar: false, showArrow: true, showSwitch: false), ImageRow(icon: "role_chat_buttle", title: "Chat buttle", showAvatar: false, showArrow: true, showSwitch: false)],
|
||||||
|
[BackgroundRow(count: 50)],
|
||||||
|
[HistoryRow(time: "", icon: "", title: "", itemCount: 30)]
|
||||||
|
]
|
||||||
|
|
||||||
|
setupViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
lazy var titleLab: UILabel = {
|
lazy var titleLab: UILabel = {
|
||||||
let lab = UILabel()
|
let lab = UILabel()
|
||||||
|
|
@ -62,21 +91,11 @@ class ChatSettingSwipeView: CLContainer {
|
||||||
tableView.register(ChatFontCell.self, forCellReuseIdentifier: "ChatFontCell")
|
tableView.register(ChatFontCell.self, forCellReuseIdentifier: "ChatFontCell")
|
||||||
tableView.register(ChatBackgroundCell.self, forCellReuseIdentifier: "ChatBackgroundCell")
|
tableView.register(ChatBackgroundCell.self, forCellReuseIdentifier: "ChatBackgroundCell")
|
||||||
tableView.register(ChatHistoryCell.self, forCellReuseIdentifier: "ChatHistoryCell")
|
tableView.register(ChatHistoryCell.self, forCellReuseIdentifier: "ChatHistoryCell")
|
||||||
|
tableView.register(SubItemsContainerCell.self, forCellReuseIdentifier: "SubItemsContainerCell")
|
||||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||||
return tableView
|
return tableView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
setupViews()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupViews() {
|
func setupViews() {
|
||||||
|
|
||||||
navigationView = NavigationView()
|
navigationView = NavigationView()
|
||||||
|
|
@ -151,6 +170,48 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource {
|
||||||
// 不需要额外的contentInset调整
|
// 不需要额外的contentInset调整
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
guard let imageRow = rows[indexPath.section][indexPath.row] as? ImageRow,
|
||||||
|
imageRow.showArrow,
|
||||||
|
let subItems = imageRow.subItems,
|
||||||
|
!subItems.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换展开状态
|
||||||
|
let isExpanded = expandedStates[indexPath.section]?[indexPath.row] ?? false
|
||||||
|
expandedStates[indexPath.section, default: [:]][indexPath.row] = !isExpanded
|
||||||
|
|
||||||
|
// 插入或删除SubItemsContainerCell
|
||||||
|
if !isExpanded {
|
||||||
|
// 展开:插入SubItemsContainerCell
|
||||||
|
let subItemsRow = SubItemsRow(subItems: subItems)
|
||||||
|
rows[indexPath.section].insert(subItemsRow, at: indexPath.row + 1)
|
||||||
|
|
||||||
|
let insertIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
||||||
|
tableView.beginUpdates()
|
||||||
|
tableView.insertRows(at: [insertIndexPath], with: .fade)
|
||||||
|
tableView.endUpdates()
|
||||||
|
|
||||||
|
// 更新箭头状态
|
||||||
|
tableView.reloadRows(at: [indexPath], with: .none)
|
||||||
|
} else {
|
||||||
|
// 折叠:删除SubItemsContainerCell
|
||||||
|
if indexPath.row + 1 < rows[indexPath.section].count,
|
||||||
|
rows[indexPath.section][indexPath.row + 1] is SubItemsRow {
|
||||||
|
rows[indexPath.section].remove(at: indexPath.row + 1)
|
||||||
|
|
||||||
|
let deleteIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
||||||
|
tableView.beginUpdates()
|
||||||
|
tableView.deleteRows(at: [deleteIndexPath], with: .fade)
|
||||||
|
tableView.endUpdates()
|
||||||
|
|
||||||
|
// 更新箭头状态
|
||||||
|
tableView.reloadRows(at: [indexPath], with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
return rows.count
|
return rows.count
|
||||||
}
|
}
|
||||||
|
|
@ -161,9 +222,26 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
let row = rows[indexPath.section][indexPath.row]
|
let row = rows[indexPath.section][indexPath.row]
|
||||||
|
|
||||||
|
// 处理SubItemsRow
|
||||||
|
if let subItemsRow = row as? SubItemsRow {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemsContainerCell", for: indexPath) as! SubItemsContainerCell
|
||||||
|
cell.selectionStyle = .none
|
||||||
|
cell.configure(with: subItemsRow)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: row.cellReuseID, for: indexPath)
|
let cell = tableView.dequeueReusableCell(withIdentifier: row.cellReuseID, for: indexPath)
|
||||||
cell.selectionStyle = .none
|
cell.selectionStyle = .none
|
||||||
(cell as? CellConfigurable)?.configure(with: row)
|
|
||||||
|
// 如果是ImageRow,设置展开状态
|
||||||
|
if var imageRow = row as? ImageRow {
|
||||||
|
imageRow.isExpanded = expandedStates[indexPath.section]?[indexPath.row] ?? false
|
||||||
|
(cell as? CellConfigurable)?.configure(with: imageRow)
|
||||||
|
} else {
|
||||||
|
(cell as? CellConfigurable)?.configure(with: row)
|
||||||
|
}
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,3 +272,205 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource {
|
||||||
return CGFLOAT_MIN
|
return CGFLOAT_MIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubItemsRow - 用于标识子项容器cell
|
||||||
|
struct SubItemsRow: RowModel {
|
||||||
|
let subItems: [ImageRow]
|
||||||
|
var cellReuseID: String { "SubItemsContainerCell" }
|
||||||
|
func cellHeight(tableWidth: CGFloat) -> CGFloat {
|
||||||
|
let maxDisplayCount = 4
|
||||||
|
let displayCount = min(subItems.count, maxDisplayCount)
|
||||||
|
return CGFloat(displayCount) * 58 // 每行40高度
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubItemsContainerCell - 子项容器cell
|
||||||
|
class SubItemsContainerCell: UITableViewCell, CellConfigurable {
|
||||||
|
|
||||||
|
private var subItems: [ImageRow] = []
|
||||||
|
private var tableViewHeightConstraint: Constraint?
|
||||||
|
|
||||||
|
lazy var tableView: UITableView = {
|
||||||
|
let tableView = UITableView(frame: .zero, style: .plain)
|
||||||
|
tableView.separatorStyle = .none
|
||||||
|
tableView.delegate = self
|
||||||
|
tableView.dataSource = self
|
||||||
|
tableView.backgroundColor = UIColor(hex: "#F5F5FF")
|
||||||
|
tableView.showsVerticalScrollIndicator = true
|
||||||
|
tableView.isScrollEnabled = true
|
||||||
|
tableView.register(SubItemCell.self, forCellReuseIdentifier: "SubItemCell")
|
||||||
|
tableView.estimatedRowHeight = 58
|
||||||
|
tableView.rowHeight = 58
|
||||||
|
tableView.contentInset = .zero
|
||||||
|
tableView.scrollIndicatorInsets = .zero
|
||||||
|
tableView.showsVerticalScrollIndicator = false
|
||||||
|
tableView.bounces = false
|
||||||
|
tableView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] // 左下、右下
|
||||||
|
tableView.layer.cornerRadius = 15
|
||||||
|
return tableView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
setupViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupViews() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
selectionStyle = .none
|
||||||
|
|
||||||
|
contentView.addSubview(tableView)
|
||||||
|
|
||||||
|
tableView.snp.makeConstraints { make in
|
||||||
|
make.left.right.equalToSuperview().inset(20)
|
||||||
|
make.top.bottom.equalToSuperview().inset(2.5)
|
||||||
|
tableViewHeightConstraint = make.height.equalTo(0).constraint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(with row: RowModel) {
|
||||||
|
guard let subItemsRow = row as? SubItemsRow else { return }
|
||||||
|
subItems = subItemsRow.subItems
|
||||||
|
|
||||||
|
tableView.reloadData()
|
||||||
|
updateTableViewHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateTableViewHeight() {
|
||||||
|
let maxDisplayCount = 4
|
||||||
|
let displayCount = min(subItems.count, maxDisplayCount)
|
||||||
|
let height = CGFloat(displayCount) * 58
|
||||||
|
|
||||||
|
tableViewHeightConstraint?.update(offset: height)
|
||||||
|
layoutIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SubItemsContainerCell: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return subItems.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemCell", for: indexPath) as! SubItemCell
|
||||||
|
let subItem = subItems[indexPath.row]
|
||||||
|
cell.configure(with: subItem)
|
||||||
|
cell.lineView.isHidden = subItems.count - 1 == indexPath.row
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubItemCell - 子项cell
|
||||||
|
class SubItemCell: UITableViewCell {
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.text = "Max-0618"
|
||||||
|
lab.textColor = UIColor(hex: "#333333")
|
||||||
|
lab.font = UIFont.systemFont(ofSize: 13)
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var iconImageView: UIImageView = {
|
||||||
|
let view = UIImageView()
|
||||||
|
view.cornerRadius = 12.5
|
||||||
|
view.backgroundColor = .darkText
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var subTitleLab: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.text = "Previous-generation large model"
|
||||||
|
lab.textColor = UIColor(hex: "#9494C3")
|
||||||
|
lab.font = UIFont.systemFont(ofSize: 10)
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var tokenLab: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.textColor = UIColor(hex: "#0066FF")
|
||||||
|
lab.text = "0.3 points / 1K tokens (Recommended)"
|
||||||
|
lab.font = UIFont.systemFont(ofSize: 11)
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var statusImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView(image: UIImage(named: ""))
|
||||||
|
imgView.backgroundColor = .blue
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var lineView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = UIColor(hex: "#ECECF9")
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
setupViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupViews() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
selectionStyle = .none
|
||||||
|
|
||||||
|
contentView.addSubview(iconImageView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
contentView.addSubview(subTitleLab)
|
||||||
|
contentView.addSubview(tokenLab)
|
||||||
|
contentView.addSubview(statusImgView)
|
||||||
|
contentView.addSubview(lineView)
|
||||||
|
|
||||||
|
iconImageView.snp.makeConstraints { make in
|
||||||
|
make.left.top.equalToSuperview().offset(10)
|
||||||
|
make.width.height.equalTo(25)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(iconImageView.snp.right).offset(8)
|
||||||
|
make.top.equalTo(iconImageView.snp.top).offset(-2)
|
||||||
|
make.right.equalTo(statusImgView.snp.left).inset(-10)
|
||||||
|
}
|
||||||
|
|
||||||
|
subTitleLab.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(titleLabel.snp.left)
|
||||||
|
make.top.equalTo(titleLabel.snp.bottom)
|
||||||
|
make.right.equalTo(titleLabel.snp.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
lineView.snp.makeConstraints { make in
|
||||||
|
make.left.right.equalToSuperview().inset(10)
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenLab.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(titleLabel.snp.left)
|
||||||
|
make.right.equalTo(titleLabel.snp.right)
|
||||||
|
make.bottom.equalToSuperview().inset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
statusImgView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.right.equalToSuperview().inset(10)
|
||||||
|
make.width.height.equalTo(13)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(with item: ImageRow) {
|
||||||
|
iconImageView.image = UIImage(named: item.icon)
|
||||||
|
titleLabel.text = item.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue