chat setting chat buttle点击效果实现
This commit is contained in:
parent
3543d9c9e8
commit
c4a34fd419
|
|
@ -6,8 +6,285 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
|
||||||
class ChatButtleCollectionCell: UICollectionViewCell {
|
struct SubButtleRow: RowModel {
|
||||||
|
let subItems: [ChatButtleRow]
|
||||||
|
var cellReuseID: String { "ChatButtleCollectionCell" }
|
||||||
|
func cellHeight(tableWidth: CGFloat) -> CGFloat {
|
||||||
|
let maxDisplayCount = 4
|
||||||
|
let displayCount = min(subItems.count, maxDisplayCount)
|
||||||
|
return CGFloat(displayCount) * 58 // 每行40高度
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 数据模型
|
||||||
|
struct ChatButtleItem {
|
||||||
|
let id: String
|
||||||
|
let name: String
|
||||||
|
let price: Int?
|
||||||
|
enum Kind { case `default`, vip, premium }
|
||||||
|
enum Status { case selected, available, locked, gettable }
|
||||||
|
let kind: Kind
|
||||||
|
let status: Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - RowModel
|
||||||
|
struct ChatButtleRow: RowModel {
|
||||||
|
let id: String
|
||||||
|
let name: String
|
||||||
|
let price: Int?
|
||||||
|
enum Kind { case `default`, vip, premium }
|
||||||
|
enum Status { case selected, available, locked, gettable }
|
||||||
|
let kind: Kind
|
||||||
|
let status: Status
|
||||||
|
|
||||||
|
let items: [ChatButtleRow] = []
|
||||||
|
var cellReuseID: String { "ChatButtleCollectionCell" }
|
||||||
|
func cellHeight(tableWidth: CGFloat) -> CGFloat {
|
||||||
|
let columns: CGFloat = 3
|
||||||
|
let spacing: CGFloat = 8
|
||||||
|
let horizontalPadding: CGFloat = 40 // container 左右各 20
|
||||||
|
let availableWidth = tableWidth - horizontalPadding
|
||||||
|
_ = (availableWidth - spacing * (columns - 1)) / columns // width 未用到,但保留用于后续等比计算
|
||||||
|
let itemHeight: CGFloat = 120
|
||||||
|
let rows = ceil(CGFloat(items.count) / columns)
|
||||||
|
let displayRows = min(rows, 4.5)
|
||||||
|
let total = displayRows * itemHeight + (displayRows - 1) * spacing + 5
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 容器 Cell
|
||||||
|
class ChatButtleCollectionCell: UITableViewCell, CellConfigurable {
|
||||||
|
private var items: [ChatButtleRow] = []
|
||||||
|
private var selectedId: String?
|
||||||
|
private let widthItem: CGFloat = ((UIScreen.width * 0.84 - 60.0) / 3.0 - 1.0)
|
||||||
|
private var heightConstraint: Constraint?
|
||||||
|
|
||||||
|
private lazy var collectionView: UICollectionView = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.minimumInteritemSpacing = 10
|
||||||
|
layout.minimumLineSpacing = 10
|
||||||
|
layout.itemSize = CGSizeMake(widthItem, widthItem * 110.0 / 87.0)
|
||||||
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
|
||||||
|
layout.scrollDirection = .vertical
|
||||||
|
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
cv.delegate = self
|
||||||
|
cv.dataSource = self
|
||||||
|
cv.showsVerticalScrollIndicator = false
|
||||||
|
cv.register(ChatButtleCard.self, forCellWithReuseIdentifier: "ChatButtleCard")
|
||||||
|
cv.layer.cornerRadius = 15
|
||||||
|
cv.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||||
|
return cv
|
||||||
|
}()
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupViews() {
|
||||||
|
backgroundColor = .clear
|
||||||
|
selectionStyle = .none
|
||||||
|
contentView.addSubview(collectionView)
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.right.equalToSuperview()
|
||||||
|
make.top.bottom.equalToSuperview().inset(2.5)
|
||||||
|
heightConstraint = make.height.equalTo(0).constraint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(with row: RowModel) {
|
||||||
|
guard let r = row as? SubButtleRow else { return }
|
||||||
|
items = r.subItems
|
||||||
|
if let first = items.first(where: { $0.status == .selected || $0.status == .available }) {
|
||||||
|
selectedId = first.id
|
||||||
|
}
|
||||||
|
collectionView.reloadData()
|
||||||
|
updateHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateHeight() {
|
||||||
|
let columns: CGFloat = 3
|
||||||
|
let spacing: CGFloat = 10
|
||||||
|
let availableWidth = UIScreen.width * 0.84 - 60.0
|
||||||
|
_ = (availableWidth - spacing * (columns - 1)) / columns
|
||||||
|
let itemHeight: CGFloat = widthItem * 110.0 / 87.0
|
||||||
|
let rows = ceil(CGFloat(items.count) / columns)
|
||||||
|
let displayRows = min(rows, 4.5)
|
||||||
|
let total = displayRows * itemHeight + (displayRows - 1) * spacing + 5
|
||||||
|
heightConstraint?.update(offset: total)
|
||||||
|
layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
updateHeight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CollectionView
|
||||||
|
extension ChatButtleCollectionCell: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { items.count }
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ChatButtleCard", for: indexPath) as! ChatButtleCard
|
||||||
|
let item = items[indexPath.item]
|
||||||
|
cell.configure(with: item, selected: item.id == selectedId)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let item = items[indexPath.item]
|
||||||
|
guard item.status == .available || item.status == .gettable else { return }
|
||||||
|
selectedId = item.id
|
||||||
|
collectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||||
|
let columns: CGFloat = 3
|
||||||
|
let spacing: CGFloat = 10
|
||||||
|
let availableWidth = UIScreen.width * 0.84 - 60.0
|
||||||
|
let itemWidth = (availableWidth - spacing * (columns - 1)) / columns
|
||||||
|
return CGSize(width: itemWidth, height: widthItem * 110.0 / 87.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 卡片
|
||||||
|
//class ChatButtleCard: UICollectionViewCell {
|
||||||
|
// private lazy var container: UIView = {
|
||||||
|
// let v = UIView()
|
||||||
|
// v.backgroundColor = UIColor(hex: "#1A1A2E")
|
||||||
|
// v.layer.cornerRadius = 12
|
||||||
|
// return v
|
||||||
|
// }()
|
||||||
|
// private lazy var bubble: UIView = {
|
||||||
|
// let v = UIView()
|
||||||
|
// v.backgroundColor = .white
|
||||||
|
// v.layer.cornerRadius = 20
|
||||||
|
// return v
|
||||||
|
// }()
|
||||||
|
// private lazy var hiLabel: UILabel = {
|
||||||
|
// let l = UILabel()
|
||||||
|
// l.text = "hi"
|
||||||
|
// l.textColor = .black
|
||||||
|
// l.font = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||||||
|
// return l
|
||||||
|
// }()
|
||||||
|
// private lazy var title: UILabel = {
|
||||||
|
// let l = UILabel()
|
||||||
|
// l.textColor = .white
|
||||||
|
// l.font = UIFont.systemFont(ofSize: 12, weight: .medium)
|
||||||
|
// l.textAlignment = .center
|
||||||
|
// return l
|
||||||
|
// }()
|
||||||
|
// private lazy var vipBadge: UILabel = {
|
||||||
|
// let l = UILabel()
|
||||||
|
// l.text = "VIP"
|
||||||
|
// l.textColor = .black
|
||||||
|
// l.font = UIFont.systemFont(ofSize: 10, weight: .bold)
|
||||||
|
// l.backgroundColor = UIColor(hex: "#FFD700")
|
||||||
|
// l.textAlignment = .center
|
||||||
|
// l.layer.cornerRadius = 8
|
||||||
|
// l.clipsToBounds = true
|
||||||
|
// l.isHidden = true
|
||||||
|
// return l
|
||||||
|
// }()
|
||||||
|
// private lazy var lockIcon: UIImageView = {
|
||||||
|
// let v = UIImageView(image: UIImage(systemName: "lock.fill"))
|
||||||
|
// v.tintColor = UIColor(hex: "#999999")
|
||||||
|
// v.isHidden = true
|
||||||
|
// return v
|
||||||
|
// }()
|
||||||
|
// private lazy var getButton: UIButton = {
|
||||||
|
// let b = UIButton(type: .custom)
|
||||||
|
// b.setTitle("Get", for: .normal)
|
||||||
|
// b.setTitleColor(.white, for: .normal)
|
||||||
|
// b.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .bold)
|
||||||
|
// b.backgroundColor = UIColor(hex: "#8B5CF6")
|
||||||
|
// b.layer.cornerRadius = 8
|
||||||
|
// b.isHidden = true
|
||||||
|
// return b
|
||||||
|
// }()
|
||||||
|
// private lazy var highlight: UIView = {
|
||||||
|
// let v = UIView()
|
||||||
|
// v.layer.borderWidth = 2
|
||||||
|
// v.layer.borderColor = UIColor(hex: "#8B5CF6").cgColor
|
||||||
|
// v.layer.cornerRadius = 12
|
||||||
|
// v.isHidden = true
|
||||||
|
// return v
|
||||||
|
// }()
|
||||||
|
//
|
||||||
|
// override init(frame: CGRect) {
|
||||||
|
// super.init(frame: frame)
|
||||||
|
// setup()
|
||||||
|
// }
|
||||||
|
// required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||||
|
//
|
||||||
|
// private func setup() {
|
||||||
|
// contentView.addSubview(container)
|
||||||
|
// container.addSubview(bubble)
|
||||||
|
// container.addSubview(hiLabel)
|
||||||
|
// container.addSubview(title)
|
||||||
|
// container.addSubview(vipBadge)
|
||||||
|
// container.addSubview(lockIcon)
|
||||||
|
// container.addSubview(getButton)
|
||||||
|
// container.addSubview(highlight)
|
||||||
|
//
|
||||||
|
// container.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||||||
|
// bubble.snp.makeConstraints { make in
|
||||||
|
// make.centerX.equalToSuperview()
|
||||||
|
// make.centerY.equalToSuperview().offset(-10)
|
||||||
|
// make.width.height.equalTo(40)
|
||||||
|
// }
|
||||||
|
// hiLabel.snp.makeConstraints { make in make.center.equalTo(bubble) }
|
||||||
|
// title.snp.makeConstraints { make in
|
||||||
|
// make.centerX.equalToSuperview()
|
||||||
|
// make.bottom.equalToSuperview().offset(-12)
|
||||||
|
// make.left.right.equalToSuperview().inset(6)
|
||||||
|
// }
|
||||||
|
// vipBadge.snp.makeConstraints { make in
|
||||||
|
// make.top.left.equalToSuperview().offset(8)
|
||||||
|
// make.width.equalTo(24)
|
||||||
|
// make.height.equalTo(16)
|
||||||
|
// }
|
||||||
|
// lockIcon.snp.makeConstraints { make in
|
||||||
|
// make.top.right.equalToSuperview().inset(8)
|
||||||
|
// make.width.height.equalTo(12)
|
||||||
|
// }
|
||||||
|
// getButton.snp.makeConstraints { make in
|
||||||
|
// make.centerX.equalToSuperview()
|
||||||
|
// make.bottom.equalToSuperview().offset(-8)
|
||||||
|
// make.width.equalTo(60)
|
||||||
|
// make.height.equalTo(24)
|
||||||
|
// }
|
||||||
|
// highlight.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func configure(with item: ChatButtleItem, selected: Bool) {
|
||||||
|
// title.text = item.name
|
||||||
|
// vipBadge.isHidden = item.kind != .vip
|
||||||
|
// lockIcon.isHidden = item.status != .locked
|
||||||
|
// getButton.isHidden = item.status != .gettable
|
||||||
|
// highlight.isHidden = !selected && item.status != .gettable
|
||||||
|
// if item.kind == .premium { title.textColor = UIColor(hex: "#0066FF") } else if item.kind == .vip { title.textColor = UIColor(hex: "#FF8C00") } else { title.textColor = .white }
|
||||||
|
// title.isHidden = item.status == .gettable
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ChatButtleCollectionCell.swift
|
||||||
|
// Visual_Novel_iOS
|
||||||
|
//
|
||||||
|
// Created by mh on 2025/10/30.
|
||||||
|
//
|
||||||
|
|
||||||
|
class ChatButtleCard: UICollectionViewCell {
|
||||||
|
|
||||||
lazy var statusImgView: UIImageView = {
|
lazy var statusImgView: UIImageView = {
|
||||||
let imgView = UIImageView()
|
let imgView = UIImageView()
|
||||||
|
|
@ -43,6 +320,7 @@ class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
lab.text = "hi"
|
lab.text = "hi"
|
||||||
lab.textColor = UIColor(hex: "#333333")
|
lab.textColor = UIColor(hex: "#333333")
|
||||||
lab.font = UIFont.systemFont(ofSize: 14)
|
lab.font = UIFont.systemFont(ofSize: 14)
|
||||||
|
lab.textAlignment = .center
|
||||||
return lab
|
return lab
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -56,6 +334,7 @@ class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
lab.font = UIFont.boldSystemFont(ofSize: 12)
|
lab.font = UIFont.boldSystemFont(ofSize: 12)
|
||||||
lab.text = "Default"
|
lab.text = "Default"
|
||||||
lab.textColor = .white
|
lab.textColor = .white
|
||||||
|
lab.textAlignment = .center
|
||||||
return lab
|
return lab
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -72,7 +351,7 @@ class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
let stackView = UIStackView(arrangedSubviews: [stoneImgView, stoneLa])
|
let stackView = UIStackView(arrangedSubviews: [stoneImgView, stoneLa])
|
||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.spacing = 5
|
stackView.spacing = 5
|
||||||
stackView.alignment = .center
|
stackView.alignment = .fill
|
||||||
stackView.distribution = .fill
|
stackView.distribution = .fill
|
||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
@ -92,7 +371,19 @@ class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configure(with item: ChatButtleRow, selected: Bool) {
|
||||||
|
nameLab.text = item.name
|
||||||
|
vipImgView.isHidden = item.kind != .vip
|
||||||
|
lockImgView.isHidden = item.status != .locked
|
||||||
|
getBtn.isHidden = item.status != .gettable
|
||||||
|
// highlight.isHidden = !selected && item.status != .gettable
|
||||||
|
if item.kind == .premium { nameLab.textColor = UIColor(hex: "#0066FF") } else if item.kind == .vip { nameLab.textColor = UIColor(hex: "#FF8C00") } else { nameLab.textColor = .white }
|
||||||
|
nameLab.isHidden = item.status == .gettable
|
||||||
|
}
|
||||||
|
|
||||||
func setupViews() {
|
func setupViews() {
|
||||||
|
self.backgroundColor = .gray
|
||||||
|
self.cornerRadius = 10
|
||||||
contentView.addSubview(vipImgView)
|
contentView.addSubview(vipImgView)
|
||||||
contentView.addSubview(statusImgView)
|
contentView.addSubview(statusImgView)
|
||||||
contentView.addSubview(stoneStackView)
|
contentView.addSubview(stoneStackView)
|
||||||
|
|
@ -122,7 +413,7 @@ class ChatButtleCollectionCell: UICollectionViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
hiLab.snp.makeConstraints { make in
|
hiLab.snp.makeConstraints { make in
|
||||||
make.centerX.centerX.equalToSuperview()
|
make.centerX.centerY.equalToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
lockImgView.snp.makeConstraints { make in
|
lockImgView.snp.makeConstraints { make in
|
||||||
|
|
|
||||||
|
|
@ -16,18 +16,20 @@ struct ImageRow: RowModel {
|
||||||
let showArrow: Bool
|
let showArrow: Bool
|
||||||
let showSwitch: Bool
|
let showSwitch: Bool
|
||||||
let subItems: [ImageRow]? // 子项列表
|
let subItems: [ImageRow]? // 子项列表
|
||||||
|
let buttleItems: [ChatButtleRow]?
|
||||||
var isExpanded: Bool = false // 展开状态
|
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) {
|
init(icon: String, title: String, showAvatar: Bool = false, showArrow: Bool = false, showSwitch: Bool = false, subItems: [ImageRow]? = nil, buttleItems: [ChatButtleRow]? = nil) {
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.title = title
|
self.title = title
|
||||||
self.showAvatar = showAvatar
|
self.showAvatar = showAvatar
|
||||||
self.showArrow = showArrow
|
self.showArrow = showArrow
|
||||||
self.showSwitch = showSwitch
|
self.showSwitch = showSwitch
|
||||||
self.subItems = subItems
|
self.subItems = subItems
|
||||||
|
self.buttleItems = buttleItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +94,7 @@ class ChatSwipeCell: ChatSettingBaseCell, CellConfigurable {
|
||||||
updateContainerHeight(50)
|
updateContainerHeight(50)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateArrowState(isExpanded: Bool) {
|
func updateArrowState(isExpanded: Bool) {
|
||||||
UIView.animate(withDuration: 0.3) { [weak self] in
|
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||||
self?.arrowImgView.transform = isExpanded ? CGAffineTransform(rotationAngle: .pi / 2.0) : .identity
|
self?.arrowImgView.transform = isExpanded ? CGAffineTransform(rotationAngle: .pi / 2.0) : .identity
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,29 @@ 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: [
|
let buttleRow = ImageRow(icon: "role_chat_buttle", title: "Chat buttle", showAvatar: false, showArrow: true, showSwitch: false, buttleItems: [
|
||||||
|
ChatButtleRow(id: "1", name: "Default", price: nil, kind: .default, status: .selected),
|
||||||
|
ChatButtleRow(id: "2", name: "Name1", price: nil, kind: .vip, status: .available),
|
||||||
|
ChatButtleRow(id: "3", name: "Name2", price: nil, kind: .premium, status: .available),
|
||||||
|
// Row 2
|
||||||
|
ChatButtleRow(id: "4", name: "Name3", price: nil, kind: .vip, status: .locked),
|
||||||
|
ChatButtleRow(id: "5", name: "Name4", price: nil, kind: .vip, status: .locked),
|
||||||
|
ChatButtleRow(id: "6", name: "Name5", price: nil, kind: .vip, status: .locked),
|
||||||
|
// Row 3
|
||||||
|
ChatButtleRow(id: "7", name: "Name6", price: 20, kind: .premium, status: .locked),
|
||||||
|
ChatButtleRow(id: "8", name: "Name7", price: 20, kind: .premium, status: .locked),
|
||||||
|
ChatButtleRow(id: "9", name: "Name8", price: 20, kind: .premium, status: .gettable),
|
||||||
|
// Row 4
|
||||||
|
// ChatButtleRow(id: "10", name: "Name9", price: 20, kind: .premium, status: .locked),
|
||||||
|
// ChatButtleRow(id: "11", name: "Name10", price: 20, kind: .premium, status: .locked),
|
||||||
|
// ChatButtleRow(id: "12", name: "Name11", price: 20, kind: .premium, status: .locked)
|
||||||
])
|
])
|
||||||
|
|
||||||
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)],
|
||||||
[TokenRow(count: "2500")],
|
[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)],
|
[FontRow(count: "20", icon: "role_font", title: "Font size"), ImageRow(icon: "role_chat_mode", title: "Chat Mode", showAvatar: false, showArrow: true, showSwitch: false), buttleRow],
|
||||||
[BackgroundRow(count: 50)],
|
[BackgroundRow(count: 50)],
|
||||||
[HistoryRow(time: "", icon: "", title: "", itemCount: 30)]
|
[HistoryRow(time: "", icon: "", title: "", itemCount: 30)]
|
||||||
]
|
]
|
||||||
|
|
@ -96,6 +110,7 @@ class ChatSettingSwipeView: CLContainer {
|
||||||
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.register(SubItemsContainerCell.self, forCellReuseIdentifier: "SubItemsContainerCell")
|
||||||
|
tableView.register(ChatButtleCollectionCell.self, forCellReuseIdentifier: "ChatButtleCollectionCell")
|
||||||
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
|
||||||
}()
|
}()
|
||||||
|
|
@ -176,42 +191,70 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard let imageRow = rows[indexPath.section][indexPath.row] as? ImageRow,
|
guard let imageRow = rows[indexPath.section][indexPath.row] as? ImageRow,
|
||||||
imageRow.showArrow,
|
imageRow.showArrow else { return }
|
||||||
let subItems = imageRow.subItems,
|
// 统一处理:优先判断 Chat buttle(使用集合视图)
|
||||||
!subItems.isEmpty else {
|
if imageRow.title == "Chat buttle", let subItems = imageRow.buttleItems, !subItems.isEmpty {
|
||||||
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
|
let isExpanded = expandedStates[indexPath.section]?[indexPath.row] ?? false
|
||||||
|
expandedStates[indexPath.section, default: [:]][indexPath.row] = !isExpanded
|
||||||
|
if !isExpanded {
|
||||||
|
// 展开:插入 ChatButtleRow
|
||||||
|
let subItemsRow = SubButtleRow(subItems: subItems)
|
||||||
|
rows[indexPath.section].insert(subItemsRow, at: indexPath.row + 1)
|
||||||
|
|
||||||
|
let insertIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
||||||
|
tableView.insertRows(at: [insertIndexPath], with: .fade)
|
||||||
|
if let cell = tableView.cellForRow(at: indexPath) as? ChatSwipeCell {
|
||||||
|
cell.updateArrowState(isExpanded: true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 折叠
|
||||||
|
if indexPath.row + 1 < rows[indexPath.section].count,
|
||||||
|
rows[indexPath.section][indexPath.row + 1] is SubButtleRow {
|
||||||
|
rows[indexPath.section].remove(at: indexPath.row + 1)
|
||||||
|
|
||||||
|
let deleteIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
||||||
|
tableView.deleteRows(at: [deleteIndexPath], with: .fade)
|
||||||
|
if let cell = tableView.cellForRow(at: indexPath) as? ChatSwipeCell {
|
||||||
|
cell.updateArrowState(isExpanded: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换展开状态
|
if let subItems = imageRow.subItems, !subItems.isEmpty {
|
||||||
let isExpanded = expandedStates[indexPath.section]?[indexPath.row] ?? false
|
// 切换展开状态
|
||||||
expandedStates[indexPath.section, default: [:]][indexPath.row] = !isExpanded
|
let isExpanded = expandedStates[indexPath.section]?[indexPath.row] ?? false
|
||||||
|
expandedStates[indexPath.section, default: [:]][indexPath.row] = !isExpanded
|
||||||
|
|
||||||
// 插入或删除SubItemsContainerCell
|
// 插入或删除SubItemsContainerCell
|
||||||
if !isExpanded {
|
if !isExpanded {
|
||||||
// 展开:插入SubItemsContainerCell
|
// 展开:插入SubItemsContainerCell
|
||||||
let subItemsRow = SubItemsRow(subItems: subItems)
|
let subItemsRow = SubItemsRow(subItems: subItems)
|
||||||
rows[indexPath.section].insert(subItemsRow, at: indexPath.row + 1)
|
rows[indexPath.section].insert(subItemsRow, at: indexPath.row + 1)
|
||||||
|
|
||||||
let insertIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
|
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.beginUpdates()
|
||||||
tableView.deleteRows(at: [deleteIndexPath], with: .fade)
|
tableView.insertRows(at: [insertIndexPath], with: .fade)
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
|
||||||
// 更新箭头状态
|
// 更新箭头状态
|
||||||
tableView.reloadRows(at: [indexPath], with: .none)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,6 +270,14 @@ 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]
|
||||||
|
|
||||||
|
// 处理ChatButtleRow
|
||||||
|
if let buttleRow = row as? SubButtleRow {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatButtleCollectionCell", for: indexPath) as! ChatButtleCollectionCell
|
||||||
|
cell.selectionStyle = .none
|
||||||
|
cell.configure(with: buttleRow)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
// 处理SubItemsRow
|
// 处理SubItemsRow
|
||||||
if let subItemsRow = row as? SubItemsRow {
|
if let subItemsRow = row as? SubItemsRow {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemsContainerCell", for: indexPath) as! SubItemsContainerCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: "SubItemsContainerCell", for: indexPath) as! SubItemsContainerCell
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue