处理聊天UI细节问题
This commit is contained in:
parent
9405f4e42c
commit
8db4792e61
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/Contents.json
vendored
Normal file
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_response@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_response@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/role_chat_response@2x.png
vendored
Normal file
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/role_chat_response@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/role_chat_response@3x.png
vendored
Normal file
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_response.imageset/role_chat_response@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/Contents.json
vendored
Normal file
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_send@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "role_chat_send@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/role_chat_send@2x.png
vendored
Normal file
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/role_chat_send@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/role_chat_send@3x.png
vendored
Normal file
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_bg_send.imageset/role_chat_send@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -7,6 +7,37 @@ final class StreamChatBubbleCell: UITableViewCell {
|
||||||
private let bubbleView = UIView()
|
private let bubbleView = UIView()
|
||||||
private let messageLabel = UILabel()
|
private let messageLabel = UILabel()
|
||||||
var onLongPress: (() -> Void)?
|
var onLongPress: (() -> Void)?
|
||||||
|
|
||||||
|
lazy var sendBgImgView: UIImageView = {
|
||||||
|
guard let originalImage = UIImage(named: "role_chat_bg_send") else {
|
||||||
|
return UIImageView()
|
||||||
|
}
|
||||||
|
// 发送消息:尖尖在右上角,focus 点应该在右上角附近
|
||||||
|
// 假设图片尺寸,focus 点设置为右上角区域(距离右边和顶部一定距离)
|
||||||
|
let imageSize = originalImage.size
|
||||||
|
let focusX = imageSize.width - 20.0 // 距离右边 20pt
|
||||||
|
let focusY = 20.0 // 距离顶部 20pt
|
||||||
|
let stretchedImage = originalImage.makeStretchable(from: originalImage, focus: CGPoint(x: focusX, y: focusY))
|
||||||
|
let imgV = UIImageView(image: stretchedImage)
|
||||||
|
imgV.contentMode = .scaleToFill
|
||||||
|
return imgV
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var respBgImgView: UIImageView = {
|
||||||
|
guard let originalImage = UIImage(named: "role_chat_bg_response") else {
|
||||||
|
return UIImageView()
|
||||||
|
}
|
||||||
|
// 接收消息:尖尖在左上角,focus 点应该在左上角附近
|
||||||
|
// focus 点设置为左上角区域(距离左边和顶部一定距离)
|
||||||
|
let imageSize = originalImage.size
|
||||||
|
let focusX = 20.0 // 距离左边 20pt
|
||||||
|
let focusY = 20.0 // 距离顶部 20pt
|
||||||
|
let stretchedImage = originalImage.makeStretchable(from: originalImage, focus: CGPoint(x: focusX, y: focusY))
|
||||||
|
let imgV = UIImageView(image: stretchedImage)
|
||||||
|
imgV.contentMode = .scaleToFill
|
||||||
|
return imgV
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var longPressGesture: UILongPressGestureRecognizer = {
|
private lazy var longPressGesture: UILongPressGestureRecognizer = {
|
||||||
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
|
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
|
||||||
gesture.minimumPressDuration = 0.4
|
gesture.minimumPressDuration = 0.4
|
||||||
|
|
@ -34,8 +65,8 @@ final class StreamChatBubbleCell: UITableViewCell {
|
||||||
|
|
||||||
private func setupViews() {
|
private func setupViews() {
|
||||||
contentView.addSubview(bubbleView)
|
contentView.addSubview(bubbleView)
|
||||||
bubbleView.layer.cornerRadius = 18
|
bubbleView.addSubview(sendBgImgView)
|
||||||
bubbleView.layer.masksToBounds = true
|
bubbleView.addSubview(respBgImgView)
|
||||||
bubbleView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
bubbleView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
bubbleView.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
bubbleView.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
contentView.addGestureRecognizer(longPressGesture)
|
contentView.addGestureRecognizer(longPressGesture)
|
||||||
|
|
@ -52,20 +83,30 @@ final class StreamChatBubbleCell: UITableViewCell {
|
||||||
|
|
||||||
func configure(message: StreamChatMessageModel) {
|
func configure(message: StreamChatMessageModel) {
|
||||||
messageLabel.text = message.text
|
messageLabel.text = message.text
|
||||||
|
respBgImgView.isHidden = message.isSelf
|
||||||
|
sendBgImgView.isHidden = !message.isSelf
|
||||||
|
|
||||||
if message.isSelf {
|
if message.isSelf {
|
||||||
bubbleView.backgroundColor = UIColor.c.cpn
|
// 发送消息:深色背景,尖尖在右上角
|
||||||
messageLabel.textColor = .white
|
messageLabel.textColor = .white
|
||||||
|
bubbleView.backgroundColor = .clear
|
||||||
|
|
||||||
bubbleView.snp.remakeConstraints { make in
|
bubbleView.snp.remakeConstraints { make in
|
||||||
make.top.equalToSuperview().offset(6)
|
make.top.equalToSuperview().offset(6)
|
||||||
make.bottom.equalToSuperview().offset(-6)
|
make.bottom.equalToSuperview().offset(-6)
|
||||||
make.width.lessThanOrEqualTo(UIScreen.width * 2.0 / 3.0)
|
make.width.lessThanOrEqualTo(UIScreen.width * 2.0 / 3.0)
|
||||||
make.width.greaterThanOrEqualTo(60)
|
make.width.greaterThanOrEqualTo(40)
|
||||||
make.trailing.equalToSuperview().offset(-16)
|
make.trailing.equalToSuperview().offset(-16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendBgImgView.snp.remakeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bubbleView.backgroundColor = UIColor.white.withAlphaComponent(0.2)
|
// 接收消息:浅色背景,尖尖在左上角
|
||||||
messageLabel.textColor = .white
|
messageLabel.textColor = UIColor(hex: "#333333")
|
||||||
|
bubbleView.backgroundColor = .clear
|
||||||
|
|
||||||
bubbleView.snp.remakeConstraints { make in
|
bubbleView.snp.remakeConstraints { make in
|
||||||
make.top.equalToSuperview().offset(6)
|
make.top.equalToSuperview().offset(6)
|
||||||
make.bottom.equalToSuperview().offset(-6)
|
make.bottom.equalToSuperview().offset(-6)
|
||||||
|
|
@ -73,6 +114,10 @@ final class StreamChatBubbleCell: UITableViewCell {
|
||||||
make.width.greaterThanOrEqualTo(60)
|
make.width.greaterThanOrEqualTo(60)
|
||||||
make.leading.equalToSuperview().offset(16)
|
make.leading.equalToSuperview().offset(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
respBgImgView.snp.remakeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageLabel.snp.remakeConstraints { make in
|
messageLabel.snp.remakeConstraints { make in
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,33 @@ public extension UIImage {
|
||||||
func pngBase64String() -> String? {
|
func pngBase64String() -> String? {
|
||||||
return pngData()?.base64EncodedString()
|
return pngData()?.base64EncodedString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 根据“焦点”生成四周可拉伸、四角不变的 UIImage
|
||||||
|
/// 返回的图 *本身* 就是 resizable,尺寸由外部 Auto Layout 决定
|
||||||
|
func makeStretchable(from image: UIImage,
|
||||||
|
focus: CGPoint) -> UIImage {
|
||||||
|
|
||||||
|
let scale = image.scale
|
||||||
|
let w = image.size.width * scale
|
||||||
|
let h = image.size.height * scale
|
||||||
|
let fx = focus.x * scale
|
||||||
|
let fy = focus.y * scale
|
||||||
|
|
||||||
|
// 保护四角:以焦点为中心对称留 cap
|
||||||
|
let capLeft = min(fx, w - fx) * 0.5
|
||||||
|
let capRight = w - capLeft
|
||||||
|
let capTop = min(fy, h - fy) * 0.5
|
||||||
|
let capBottom = h - capTop
|
||||||
|
|
||||||
|
let insets = UIEdgeInsets(top: capTop,
|
||||||
|
left: capLeft,
|
||||||
|
bottom: capBottom,
|
||||||
|
right: capRight)
|
||||||
|
|
||||||
|
// 只生成可拉伸图,不立即渲染固定尺寸
|
||||||
|
return image.resizableImage(withCapInsets: insets,
|
||||||
|
resizingMode: .stretch)
|
||||||
|
}
|
||||||
|
|
||||||
/// Base 64 encoded JPEG data of the image.
|
/// Base 64 encoded JPEG data of the image.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue