添加群组cell

This commit is contained in:
Fin 2024-12-26 09:34:42 +08:00
parent 80732720ce
commit 612cadafec
9 changed files with 337 additions and 4 deletions

View File

@ -20,6 +20,7 @@
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */; };
061894C529962EB900E001C2 /* GradientButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061894C429962EB900E001C2 /* GradientButton.swift */; };
061894C729A75BEA00E001C2 /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061894C629A75BEA00E001C2 /* Algorithm.swift */; };
061C17082D1BDA4B00891D66 /* MessageGroupMoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061C17072D1BDA4B00891D66 /* MessageGroupMoreView.swift */; };
0627DABB298B6EA2002F3F69 /* DropBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0627DABA298B6EA2002F3F69 /* DropBoxView.swift */; };
0627DABD2990D615002F3F69 /* BorderTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0627DABC2990D615002F3F69 /* BorderTextField.swift */; };
062B98C3251B2762004562E7 /* BKButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C2251B2762004562E7 /* BKButton.swift */; };
@ -261,6 +262,7 @@
06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListTableViewCellViewModel.swift; sourceTree = "<group>"; };
061894C429962EB900E001C2 /* GradientButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientButton.swift; sourceTree = "<group>"; };
061894C629A75BEA00E001C2 /* Algorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Algorithm.swift; sourceTree = "<group>"; };
061C17072D1BDA4B00891D66 /* MessageGroupMoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageGroupMoreView.swift; sourceTree = "<group>"; };
0627DABA298B6EA2002F3F69 /* DropBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropBoxView.swift; sourceTree = "<group>"; };
0627DABC2990D615002F3F69 /* BorderTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderTextField.swift; sourceTree = "<group>"; };
062B98C2251B2762004562E7 /* BKButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKButton.swift; sourceTree = "<group>"; };
@ -680,6 +682,7 @@
067B2EB425693E38008B6BE1 /* MessageTableViewCellViewModel.swift */,
0668900A2D19525400E106F2 /* ShowLessAndClearView.swift */,
0668900C2D19582400E106F2 /* MessageGroupHeaderView.swift */,
061C17072D1BDA4B00891D66 /* MessageGroupMoreView.swift */,
);
path = MessageList;
sourceTree = "<group>";
@ -1287,6 +1290,7 @@
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */,
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,
061C17082D1BDA4B00891D66 /* MessageGroupMoreView.swift in Sources */,
0642B55A27EB13F100453D91 /* MutableTextCell.swift in Sources */,
1EFB545F2C32514000B8E51B /* SectionViewModel-iPad.swift in Sources */,
06EEF335291CD00000CA228A /* CryptoSettingViewModel.swift in Sources */,

View File

@ -0,0 +1,12 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"symbols" : [
{
"filename" : "keyboard_arrow_right_keyboard_arrow_right_symbol.svg",
"idiom" : "universal"
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -3150,6 +3150,29 @@
}
}
}
},
"viewMoreMessages" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "View %d More Messages"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "%d Daha Mesaj Görüntüle"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "查看更多%d条消息"
}
}
}
}
},
"version" : "1.0"

View File

@ -21,3 +21,35 @@ extension String {
return self.removingPercentEncoding ?? ""
}
}
// MARK: - NSAttributedString
extension String {
var bold: NSAttributedString {
return NSMutableAttributedString(string: self, attributes: [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)])
}
var underline: NSAttributedString {
return NSAttributedString(string: self, attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue])
}
var strikethrough: NSAttributedString {
return NSAttributedString(string: self, attributes: [.strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue as Int)])
}
var italic: NSAttributedString {
return NSMutableAttributedString(string: self, attributes: [.font: UIFont.italicSystemFont(ofSize: UIFont.systemFontSize)])
}
func colored(with color: UIColor) -> NSAttributedString {
return NSMutableAttributedString(string: self, attributes: [.foregroundColor: color])
}
}
// MARK: - Format
extension String {
func format(_ arguments: any CVarArg...) -> String {
return String(format: self, arguments)
}
}

View File

@ -50,7 +50,7 @@ class MessageGroupHeaderView: UIView {
make.bottom.equalTo(-10)
}
showLessAndClearView.snp.makeConstraints { make in
make.right.equalTo(-8)
make.right.equalToSuperview()
make.centerY.equalTo(groupNameLabel)
}
}

View File

@ -0,0 +1,56 @@
//
// MessageGroupMoreView.swift
// Bark
//
// Created by huangfeng on 12/25/24.
// Copyright © 2024 Fin. All rights reserved.
//
import UIKit
class MessageGroupMoreView: UIView {
private let moreLabel: UILabel = {
let label = UILabel()
label.textColor = BKColor.grey.darken3
label.font = UIFont.preferredFont(ofSize: 12)
return label
}()
let arrowImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "keyboard_arrow_right_symbol")?.withRenderingMode(.alwaysTemplate)
imageView.tintColor = BKColor.grey.darken2
return imageView
}()
var count: Int = 0 {
didSet {
moreLabel.text = NSLocalizedString("viewMoreMessages").format(count)
}
}
init() {
super.init(frame: CGRect.zero)
self.backgroundColor = BKColor.grey.lighten5
self.layer.cornerRadius = 28 / 2
self.clipsToBounds = true
self.addSubview(moreLabel)
self.addSubview(arrowImageView)
moreLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(8)
make.height.equalTo(28).priority(.medium)
make.top.bottom.equalToSuperview()
}
arrowImageView.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-6)
make.centerY.equalToSuperview()
make.left.equalTo(moreLabel.snp.right).offset(4)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -137,12 +137,12 @@ class MessageItemView: UIView {
dateLabel.snp.makeConstraints { make in
make.left.equalTo(bodyLabel)
make.top.equalTo(bodyLabel.snp.bottom).offset(12)
make.bottom.equalTo(panel).offset(-12)
make.bottom.equalTo(panel).offset(-12).priority(.medium)
}
panel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16)
make.right.equalToSuperview().offset(-16).priority(.medium)
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}

View File

@ -8,11 +8,12 @@
import Material
import RxSwift
import SnapKit
import UIKit
/// cell
class MessageTableViewCell: UITableViewCell {
let messageView = MessageItemView()
private let messageView = MessageItemView()
var message: Message? {
get {
return messageView.message
@ -38,3 +39,196 @@ class MessageTableViewCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
}
/// cell
class MessageGroupTableViewCell: UITableViewCell {
/// 便
private let panel: UIView = {
let panel = UIView()
panel.backgroundColor = BKColor.background.primary
return panel
}()
/// 5 5
private let messageViews = [
MessageItemView(isShowShadow: true),
MessageItemView(isShowShadow: true),
MessageItemView(isShowShadow: true),
MessageItemView(isShowShadow: true),
MessageItemView(isShowShadow: true)
]
/// header
private let groupHeader = MessageGroupHeaderView()
/// 5
private let moreView = MessageGroupMoreView()
/// header top offset
private var groupHeaderTopConstraint: Constraint? = nil
///
var isExpanded: Bool = false {
didSet {
refreshViewState()
refreshLayout()
self.contentView.layoutIfNeeded()
}
}
///
var messages: [Message] = [] {
didSet {
for (index, item) in messageViews.enumerated() {
if index < messages.count {
item.message = messages[index]
item.isHidden = false
} else {
item.isHidden = true
}
}
refreshLayout()
}
}
///
var moreCount: Int = 0 {
didSet {
moreView.count = moreCount
}
}
///
var groupName: String? {
set {
if let newValue, !newValue.isEmpty {
groupHeader.groupName = newValue
} else {
groupHeader.groupName = NSLocalizedString("default")
}
}
get {
return groupHeader.groupName
}
}
///
var showLessAction: (() -> Void)? {
get {
return groupHeader.showLessAction
}
set {
groupHeader.showLessAction = newValue
}
}
///
var clearAction: (() -> Void)? {
get {
return groupHeader.clearAction
}
set {
groupHeader.clearAction = newValue
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.contentView.addSubview(panel)
panel.addSubview(groupHeader)
panel.addSubview(moreView)
for view in messageViews.reversed() {
panel.addSubview(view)
}
panel.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
groupHeader.snp.remakeConstraints { make in
groupHeaderTopConstraint = make.top.equalToSuperview().offset(0).constraint
make.left.equalTo(16)
make.right.equalTo(-16)
}
moreView.snp.remakeConstraints { make in
make.bottom.equalToSuperview().offset(-18)
make.centerX.equalToSuperview()
}
refreshViewState()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// UI
private func refreshViewState() {
self.messageViews.first?.bodyLabel.isUserInteractionEnabled = isExpanded
self.contentView.gestureRecognizers?.first?.isEnabled = !isExpanded
for (index, view) in messageViews.enumerated() {
if isExpanded {
view.maskAlpha = 0
} else {
view.maskAlpha = index == 0 ? 0 : CGFloat(index + 1) * 0.01
}
}
}
///
private func refreshLayout() {
// header
groupHeaderTopConstraint?.update(offset: isExpanded ? 0 : 15)
// 5 messageViews.count
let maxCount = min(self.messages.count, self.messageViews.count)
//
for view in messageViews {
view.snp.removeConstraints()
}
for (index, item) in messageViews.enumerated() {
if index >= maxCount {
break
}
item.snp.remakeConstraints { make in
make.left.right.equalToSuperview()
if isExpanded {
if index == 0 {
make.top.equalTo(groupHeader.snp.bottom).offset(8)
} else {
make.top.equalTo(messageViews[index - 1].snp.bottom).offset(8)
}
if index == maxCount - 1 {
if moreCount > 0 {
make.bottom.equalTo(moreView.snp.top).offset(-18)
} else {
make.bottom.equalToSuperview().offset(-18)
}
}
item.transform = .identity
} else {
if index == 0 {
make.top.equalToSuperview()
item.transform = .identity
} else {
// 1
make.top.equalToSuperview().offset(min(index * 8, 1 * 8))
make.height.equalTo(messageViews[0])
// index
let scale = 1 - CGFloat(index) * 0.04
item.transform = CGAffineTransform(scaleX: scale, y: 1)
}
if index == maxCount - 1 {
make.bottom.equalToSuperview().offset(-8)
}
}
}
}
}
}