chat setting 完善
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_chat.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_chat@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_chat@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_chat.imageset/role_chat_buttle_chat@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_chat.imageset/role_chat_buttle_chat@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_lock.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_lock@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_lock@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_lock.imageset/role_chat_buttle_lock@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 423 B |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_lock.imageset/role_chat_buttle_lock@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 894 B |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_stone.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_stone@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_stone@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_stone.imageset/role_chat_buttle_stone@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1023 B |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_stone.imageset/role_chat_buttle_stone@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_vip.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_vip@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_buttle_vip@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_vip.imageset/role_chat_buttle_vip@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_buttle_vip.imageset/role_chat_buttle_vip@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -0,0 +1,144 @@
|
||||||
|
//
|
||||||
|
// ChatButtleCollectionCell.swift
|
||||||
|
// Visual_Novel_iOS
|
||||||
|
//
|
||||||
|
// Created by mh on 2025/10/30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
lazy var statusImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView()
|
||||||
|
imgView.backgroundColor = .blue
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var stoneImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView(image: UIImage(named: "role_chat_buttle_stone"))
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var stoneLa: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.font = UIFont.boldSystemFont(ofSize: 12)
|
||||||
|
lab.textColor = UIColor(hex: "#D7E7FF")
|
||||||
|
lab.text = "12"
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var lockImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView(image: UIImage(named: "role_chat_buttle_lock"))
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var hiImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView(image: UIImage(named: "role_chat_buttle_chat"))
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var hiLab: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.text = "hi"
|
||||||
|
lab.textColor = UIColor(hex: "#333333")
|
||||||
|
lab.font = UIFont.systemFont(ofSize: 14)
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var vipImgView: UIImageView = {
|
||||||
|
let imgView = UIImageView(image: UIImage(named: "role_chat_buttle_vip"))
|
||||||
|
return imgView
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var nameLab: UILabel = {
|
||||||
|
let lab = UILabel()
|
||||||
|
lab.font = UIFont.boldSystemFont(ofSize: 12)
|
||||||
|
lab.text = "Default"
|
||||||
|
lab.textColor = .white
|
||||||
|
return lab
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var getBtn: UIButton = {
|
||||||
|
let btn = UIButton()
|
||||||
|
btn.backgroundColor = UIColor(hex: "#8A48FF")
|
||||||
|
btn.setTitle("Get", for: .normal)
|
||||||
|
btn.cornerRadius = 12.5
|
||||||
|
btn.addTarget(self, action: #selector(getBtnTap), for: .touchUpInside)
|
||||||
|
return btn
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var stoneStackView: UIStackView = {
|
||||||
|
let stackView = UIStackView(arrangedSubviews: [stoneImgView, stoneLa])
|
||||||
|
stackView.axis = .horizontal
|
||||||
|
stackView.spacing = 5
|
||||||
|
stackView.alignment = .center
|
||||||
|
stackView.distribution = .fill
|
||||||
|
return stackView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setupViews()
|
||||||
|
setupData()
|
||||||
|
setupEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func getBtnTap() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupViews() {
|
||||||
|
contentView.addSubview(vipImgView)
|
||||||
|
contentView.addSubview(statusImgView)
|
||||||
|
contentView.addSubview(stoneStackView)
|
||||||
|
contentView.addSubview(lockImgView)
|
||||||
|
contentView.addSubview(hiImgView)
|
||||||
|
hiImgView.addSubview(hiLab)
|
||||||
|
contentView.addSubview(nameLab)
|
||||||
|
contentView.addSubview(getBtn)
|
||||||
|
|
||||||
|
vipImgView.snp.makeConstraints { make in
|
||||||
|
make.top.left.equalToSuperview().inset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
stoneStackView.snp.makeConstraints { make in
|
||||||
|
make.left.top.equalToSuperview().inset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
statusImgView.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().inset(5)
|
||||||
|
make.centerY.equalTo(stoneStackView.snp.centerY)
|
||||||
|
make.width.height.equalTo(13)
|
||||||
|
}
|
||||||
|
|
||||||
|
hiImgView.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalTo(statusImgView.snp.bottom).offset(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
hiLab.snp.makeConstraints { make in
|
||||||
|
make.centerX.centerX.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
lockImgView.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().inset(5)
|
||||||
|
make.centerY.equalTo(stoneStackView.snp.centerY)
|
||||||
|
}
|
||||||
|
|
||||||
|
nameLab.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.left.equalToSuperview().inset(5)
|
||||||
|
make.top.equalTo(hiImgView.snp.bottom).offset(15)
|
||||||
|
make.bottom.equalToSuperview().inset(10).priority(.low)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupData() {}
|
||||||
|
|
||||||
|
func setupEvent() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
//
|
||||||
|
// SubItemsContainerCell.swift
|
||||||
|
// Visual_Novel_iOS
|
||||||
|
//
|
||||||
|
// Created by mh on 2025/10/30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
|
||||||
|
// switch model中点击展开的item
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +35,10 @@ class ChatSettingSwipeView: CLContainer {
|
||||||
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 10", showAvatar: false, showArrow: false, showSwitch: false)
|
// ImageRow(icon: "role_exchange_mode", title: "Sub Item 10", showAvatar: false, showArrow: false, showSwitch: false)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
let buttleRow = ImageRow(icon: "role_chat_buttle", title: "Chat buttle", showAvatar: false, showArrow: true, showSwitch: false, subItems: [
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
rows = [
|
rows = [
|
||||||
[modelRow, ImageRow(icon: "role_text_mode", title: "Short Text Mode", showAvatar: false, showArrow: false, showSwitch: true)],
|
[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)],
|
[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)],
|
||||||
|
|
@ -272,205 +276,3 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||