聊天详情页面

This commit is contained in:
mh 2025-10-22 19:31:54 +08:00
parent a3b1677409
commit f75c4ecba0
61 changed files with 796 additions and 105 deletions

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "back_black@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "back_black@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_back_white@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_back_white@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_change_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_change_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_change_normal@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_change_normal@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_change_open@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_change_open@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_expand_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_expand_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_expand_open@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_expand_open@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_expand_phone@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_expand_phone@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_keyboard@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_keyboard@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_phone_vip@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_phone_vip@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_remind_voice@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_remind_voice@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_setting@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_setting@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_vip@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_vip@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "role_chat_voice@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "role_chat_voice@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -49,6 +49,20 @@ import UIKit
return v
}()
@objc lazy var centerStackH : UIStackView = {
let v = UIStackView()
v.spacing = 5
v.alignment = .center
addSubview(v)
v.snp.makeConstraints { make in
make.centerY.equalTo(backButton)
make.centerX.equalToSuperview()
make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing)
make.trailing.lessThanOrEqualTo(rightStackH.snp.leading)
}
return v
}()
@objc lazy var backButton: UIButton = {
let backButton = UIButton(type: .custom)
leftStackH.addArrangedSubview(backButton)
@ -69,16 +83,30 @@ import UIKit
titleLabel.preferredMaxLayoutWidth = 120
titleLabel.textAlignment = .center
titleLabel.font = .t.ttm//.fredokaOne(size: 18)
addSubview(titleLabel)
// addSubview(titleLabel)
centerStackH.addArrangedSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.centerY.equalTo(backButton)
make.centerX.equalToSuperview()
make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing)
make.trailing.lessThanOrEqualTo(rightStackH.snp.leading)
}
// titleLabel.snp.makeConstraints { make in
// make.centerY.equalTo(backButton)
// make.centerX.equalToSuperview()
// make.leading.greaterThanOrEqualTo(leftStackH.snp.trailing)
// make.trailing.lessThanOrEqualTo(rightStackH.snp.leading)
// }
return titleLabel
}()
@objc lazy var iconImgView: UIImageView = {
let icon = UIImageView()
icon.isHidden = true
icon.cornerRadius = 10.0
icon.snp.makeConstraints { make in
make.width.height.equalTo(20)
}
centerStackH.addArrangedSubview(icon)
return icon
}()
/// btn 44x44 is ok
@objc lazy var rightStackH: UIStackView = {
@ -131,6 +159,8 @@ import UIKit
//backgroundColor = .c.cbd
backgroundColor = .clear
iconImgView.backgroundColor = .blue
}
@available(*, unavailable)

View File

@ -0,0 +1,34 @@
//
// InputSafeView.swift
// Visual_Novel_iOS
//
// Created by mh on 2025/10/21.
//
import UIKit
class InputSafeView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 1. /
if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil }
// 2.
let hit = super.hitTest(point, with: event)
if hit != nil { return hit }
// 3. ****
for sub in subviews.reversed() {
let converted = sub.convert(point, from: self)
if sub.bounds.contains(converted) {
return sub.hitTest(converted, with: event)
} else {
if sub is SessionPhoneExpandView {
return sub.hitTest(converted, with: event)
}
}
}
return nil
}
}

View File

@ -0,0 +1,29 @@
//
// InputStackView.swift
// Visual_Novel_iOS
//
// Created by mh on 2025/10/21.
//
import UIKit
class InputStackView: UIStackView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 1. /
if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil }
// 2.
let hit = super.hitTest(point, with: event)
if hit != nil { return hit }
// 3. ****
for sub in subviews.reversed() {
if sub is SessionInputOperateView {
let converted = sub.convert(point, from: self)
return sub.hitTest(converted, with: event)
}
}
return nil
}
}

View File

@ -0,0 +1,124 @@
//
// SessionCoverView.swift
// Visual_Novel_iOS
//
// Created by mh on 2025/10/21.
//
import UIKit
class SessionCoverView: UIView {
var aiAnswerBlock: (()->Void)? = nil
lazy var expandBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "role_chat_expand_open"), for: .normal)
btn.setImage(UIImage(named: "role_chat_expand_close"), for: .selected)
btn.backgroundColor = .clear
btn.isUserInteractionEnabled = false
return btn
}()
lazy var lineView: UIView = {
let view = UIView()
// view.backgroundColor = UIColor.init(white: 1.0, alpha: 0.2)
view.backgroundColor = .red
view.cornerRadius = 0.5
view.alpha = 0
return view
}()
lazy var phoneBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "role_chat_expand_phone"), for: .normal)
btn.addTarget(self, action: #selector(phoneBtnClicked), for: .touchDown)
btn.alpha = 0
return btn
}()
lazy var emailBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "role_chat_expand_phone"), for: .normal)
btn.addTarget(self, action: #selector(emailBtnClicked), for: .touchDown)
btn.alpha = 0
return btn
}()
lazy var bgview: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(bgviewTap)))
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func bgviewTap() {
print("bgviewTap-bgviewTap")
}
@objc func phoneBtnClicked() {
self.aiAnswerBlock?()
}
@objc func emailBtnClicked() {
self.aiAnswerBlock?()
}
func expandStatusChanged(_ expand: Bool) {
expandBtn.isSelected = expand
self.backgroundColor = expand ? .orange : .blue
UIView.animate(withDuration: 0.25) {
self.phoneBtn.alpha = expand ? 1 : 0
self.emailBtn.alpha = expand ? 1 : 0
self.lineView.alpha = expand ? 1 : 0
}
}
private func setupUI() {
addSubview(expandBtn)
addSubview(lineView)
addSubview(bgview)
addSubview(phoneBtn)
addSubview(emailBtn)
expandBtn.snp.makeConstraints { make in
make.bottom.left.right.equalToSuperview()
make.height.equalTo(44)
}
lineView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.height.equalTo(1.0)
make.width.equalTo(10)
make.bottom.equalTo(expandBtn.snp.top)
}
phoneBtn.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(lineView.snp.top).offset(-17)
make.width.height.equalTo(30)
}
emailBtn.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(phoneBtn.snp.top).offset(-20)
make.width.height.equalTo(30)
}
bgview.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.bottom.equalTo(lineView.snp.top)
}
}
}

View File

@ -1,4 +1,5 @@
//
//
// SessionInputOperateView.swift
//
@ -23,19 +24,20 @@ enum InputOperateState {
class SessionInputOperateView: UIView {
weak var delegate: SessionInputOperateViewDelegate?
var safeView: UIView!
var giftButton: EPIconPrimaryButton!
var safeView: InputSafeView!
var expandView: SessionPhoneExpandView!
var block: UIView!
var effectViewOnBlock: UIVisualEffectView!
var modeButton: UIButton!
var rightStackH: UIStackView!
var helpButton: UIButton!
var moreButton: UIButton!
var fakeTextfield: UITextField!
var voiceHoldView: UIView!
var inputTextView: CLTextView!
let minTextViewHeight = 40.0//56.0
var sendBtn: UIButton!
//var tapInputFieldAction: (() -> Void)?
// var voiceHoldAction: ((_ onVoice: Bool) -> Void)?
@ -45,6 +47,9 @@ class SessionInputOperateView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.clipsToBounds = false //
self.isUserInteractionEnabled = true
setupViews()
setupData()
setupEvent()
@ -54,28 +59,47 @@ class SessionInputOperateView: UIView {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 1. /
if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil }
// 2.
let hit = super.hitTest(point, with: event)
if hit != nil { return hit }
// 3. ****
for sub in subviews.reversed() {
let converted = sub.convert(point, from: self)
return sub.hitTest(converted, with: event)
}
return nil
}
private func setupViews() {
safeView = {
let v = UIView()
v.backgroundColor = .clear
let v = InputSafeView()
// v.backgroundColor = .orange
addSubview(v)
v.snp.makeConstraints { make in
make.leading.top.trailing.equalToSuperview()
make.left.top.right.equalToSuperview()
// make.bottom.equalToSuperview().offset(-UIWindow.safeAreaBottom*0.5-16)
make.bottom.equalToSuperview()
}
return v
}()
giftButton = {
let v = EPIconPrimaryButton(radius: .round, iconSize: .large, iconCode: .giftBorder)
v.addTarget(self, action: #selector(tapGiftButton), for: .touchUpInside)
expandView = {
let v = SessionPhoneExpandView()
safeView.clipsToBounds = false
safeView.isUserInteractionEnabled = true
safeView.addSubview(v)
// v.backgroundColor = .blue
v.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(24)
make.top.equalToSuperview().offset(16)
make.bottom.equalToSuperview().offset(-16)
make.size.equalTo(v.bgImageSize())
make.left.equalToSuperview().offset(24)
make.top.equalToSuperview().inset(16).priority(999)
make.bottom.equalToSuperview().inset(16)
make.width.height.equalTo(44)
}
return v
}()
@ -83,13 +107,13 @@ class SessionInputOperateView: UIView {
block = {
let v = UIView()
v.backgroundColor = .c.cseln
v.layer.cornerRadius = 24
v.layer.masksToBounds = true
v.cornerRadius = 24
safeView.addSubview(v)
v.snp.makeConstraints { make in
make.leading.equalTo(giftButton.snp.trailing).offset(12)
make.trailing.equalToSuperview().offset(-24)
make.height.equalTo(48)
make.left.equalTo(expandView.snp.right).offset(12)
make.right.equalToSuperview().offset(-24)
// make.height.equalTo(48)
make.top.equalToSuperview().inset(16)
make.centerY.equalToSuperview()
}
return v
@ -108,75 +132,51 @@ class SessionInputOperateView: UIView {
modeButton = {
let v = UIButton()
block.addSubview(v)
let size = CGSize(width: 20, height: 20)
let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white)
let size = CGSize(width: 26, height: 26)
// let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white)
let image = UIImage(named: "role_chat_voice")
v.setImage(image, for: .normal)
v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
v.addTarget(self, action: #selector(tapModeButton), for: .touchUpInside)
v.snp.makeConstraints { make in
make.size.equalTo(size)
make.leading.equalToSuperview().offset(16)
make.centerY.equalToSuperview()
make.left.equalToSuperview().inset(12)
make.bottom.equalToSuperview().inset(11)
}
return v
}()
rightStackH = {
let v = UIStackView()
v.spacing = 20
v.alignment = .center
sendBtn = {
let btn = UIButton()
block.addSubview(btn)
btn.setImage(UIImage(named: "role_chat_send"), for: .normal)
btn.addTarget(self, action: #selector(tapSendButton), for: .touchUpInside)
btn.snp.makeConstraints { make in
make.bottom.equalToSuperview().inset(2)
make.right.equalToSuperview().inset(3)
make.width.equalTo(60)
make.height.equalTo(44)
}
return btn
}()
inputTextView = {
let v = CLTextView()
block.addSubview(v)
v.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-16)
make.centerY.equalToSuperview()
}
return v
}()
helpButton = {
let v = UIButton()
rightStackH.addArrangedSubview(v)
let size = CGSize(width: 20, height: 20)
let image = MWIconFont.image(fromIcon: .prompt, size: size, color: .white)
v.setImage(image, for: .normal)
v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
v.addTarget(self, action: #selector(tapHelpButton), for: .touchUpInside)
v.snp.makeConstraints { make in
make.size.equalTo(size)
}
return v
}()
moreButton = {
let v = UIButton()
rightStackH.addArrangedSubview(v)
let size = CGSize(width: 20, height: 20)
let image = MWIconFont.image(fromIcon: .add, size: size, color: .white)
v.setImage(image, for: .normal)
v.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
v.addTarget(self, action: #selector(tapMoreButton), for: .touchUpInside)
v.snp.makeConstraints { make in
make.size.equalTo(size)
}
return v
}()
fakeTextfield = {
let v = UITextField()
v.placeholder = "Type a message..."
v.placeholderTextColor = UIColor.init(white: 1, alpha: 0.4)
v.backgroundColor = .clear
v.limit.maxCharacterNumber = 500
v.font = .t.tll
v.textColor = .c.ctpn
block.addSubview(v)
v.textColor = UIColor.white
v.font = UIFont.systemFont(ofSize: 14)
v.snp.makeConstraints { make in
make.left.equalTo(modeButton.snp.right).offset(10)
make.right.equalTo(sendBtn.snp.left).offset(-10)
make.height.equalTo(minTextViewHeight)
make.centerY.equalToSuperview()
make.leading.equalTo(modeButton.snp.trailing).offset(16)
make.trailing.equalTo(rightStackH.snp.leading).offset(-16)
make.top.equalToSuperview().inset(4)
}
// v.text = "Chat"
v.attributedPlaceholder = "Chat".withAttributes([
.font(.t.tll),
.textColor(UIColor.c.ctsn),
])
v.delegate = self
return v
}()
@ -185,19 +185,26 @@ class SessionInputOperateView: UIView {
block.addSubview(v)
v.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.leading.trailing.equalTo(fakeTextfield)
make.left.right.equalTo(inputTextView)
}
let voiceImgView = UIImageView(image: UIImage(named: "role_chat_remind_voice"))
let label = UILabel()
label.font = .t.tll
label.textColor = .white
label.textAlignment = .center
label.text = "Hold to Talk"
v.addSubview(label)
label.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.centerY.equalToSuperview()
let stackView = UIStackView(arrangedSubviews: [voiceImgView, label])
stackView.spacing = 3
stackView.alignment = .center
stackView.distribution = .fill
v.addSubview(stackView)
stackView.snp.makeConstraints { make in
make.centerX.centerY.equalToSuperview()
}
return v
}()
}
@ -207,6 +214,11 @@ class SessionInputOperateView: UIView {
}
private func setupEvent() {
inputTextView.textPublisher.sink {[weak self] str in
self?.fixTextViewHeight()
}.store(in: &cancellables)
//
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
longPress.minimumPressDuration = 0.5 // 0.5
@ -222,22 +234,37 @@ class SessionInputOperateView: UIView {
// MARK: - Helper
private func refreshByState(stateOf: InputOperateState) {
let size = CGSize(width: 20, height: 20)
// let size = CGSize(width: 26, height: 26)
switch stateOf {
case .text:
fakeTextfield.isHidden = false
// fakeTextfield.isHidden = false
inputTextView.isHidden = false
voiceHoldView.isHidden = true
let image = MWIconFont.image(fromIcon: .voiceMsg, size: size, color: .white)
// let image = MWIconFont.image(fromIcon:4 .voiceMsg, size: size, color: .white)
let image = UIImage(named: "role_chat_voice")
modeButton.setImage(image, for: .normal)
case .voice:
fakeTextfield.isHidden = true
// fakeTextfield.isHidden = true
inputTextView.isHidden = true
voiceHoldView.isHidden = false
let image = MWIconFont.image(fromIcon: .iconKeyboard, size: size, color: .white)
let image = UIImage(named: "role_chat_keyboard")
modeButton.setImage(image, for: .normal)
AudioRecordTool.audioAuth()
}
}
private func fixTextViewHeight(){
if inputTextView.contentSize.height > minTextViewHeight {
inputTextView.snp.updateConstraints { make in
make.height.equalTo(max(inputTextView.contentSize.height, 48)) // 73
}
}else{
inputTextView.snp.updateConstraints { make in
make.height.equalTo(inputTextView.contentSize.height) //minTextViewHeight
}
}
}
// MARK: - Action
@objc private func tapGiftButton() {
@ -253,6 +280,10 @@ class SessionInputOperateView: UIView {
}
}
@objc private func tapSendButton() {
}
@objc private func tapMoreButton() {
delegate?.operateTapMoreAction()
}

View File

@ -0,0 +1,101 @@
//
// SessionPhoneExpandView.swift
// Visual_Novel_iOS
//
// Created by mh on 2025/10/20.
//
import Foundation
class SessionPhoneExpandView: UIView {
lazy var expandView: SessionCoverView = {
let view = SessionCoverView()
// view.backgroundColor = UIColor.init(white: 0, alpha: 0.4)
view.backgroundColor = .blue
view.cornerRadius = 22.0
view.aiAnswerBlock = { [weak self] in
self?.tapClicked()
}
let tap = UITapGestureRecognizer(target: self, action: #selector(tapClicked))
view.addGestureRecognizer(tap)
return view
}()
var expand: Bool = false
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 1. /
if alpha <= 0.01 || isHidden || !isUserInteractionEnabled { return nil }
// 2.
let hit = super.hitTest(point, with: event)
if hit != nil { return hit }
// 3. ****
for sub in subviews.reversed() {
let converted = sub.convert(point, from: self)
if sub.bounds.contains(converted) {
return sub.hitTest(converted, with: event)
}
}
return nil
}
override init(frame: CGRect) {
super.init(frame: frame)
self.clipsToBounds = false //
setupViews()
setupData()
setupEvent()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func tapClicked() {
print("tapClicked 被调用 - expand: \(expand)")
self.expandView.expandStatusChanged(!self.expand)
UIView.animate(withDuration: 0.25) {
// expandView
self.expandView.snp.updateConstraints { make in
make.height.equalTo(self.expand ? 44 : 152)
}
self.layoutIfNeeded()
} completion: { finish in
if finish {
self.expand = !self.expand
print("动画完成 - 新的 expand 状态: \(self.expand)")
}
}
}
private func setupViews() {
addSubview(expandView)
expandView.snp.makeConstraints { make in
make.height.width.equalTo(44.0)
make.bottom.equalToSuperview()
make.centerX.equalToSuperview()
}
//
self.snp.makeConstraints { make in
make.height.equalTo(44.0)
}
}
private func setupData() {
}
private func setupEvent() {
}
}

View File

@ -14,8 +14,9 @@ extension SessionController {
func setupInputView() {
bottomViewsStackV = {
let v = UIStackView()
let v = InputStackView()
v.axis = .vertical
v.clipsToBounds = false //
view.addSubview(v)
v.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
@ -97,7 +98,7 @@ extension SessionController {
}
inputBar.textView.textPublisher.sink {[weak self] string in
self?.inputEntrance.fakeTextfield.text = string
self?.inputEntrance.inputTextView.text = string
}.store(in: &cancellables)
}

View File

@ -13,7 +13,7 @@ class SessionController: CLBaseViewController {
// var headView: SessionAIHeadView!
// MARK: BottomViews
var bottomViewsStackV : UIStackView!
var bottomViewsStackV : InputStackView!
var inputEntrance: SessionInputOperateView!
var inputBar: SessionInputView!
var moreView: IMMoreItemView!
@ -23,6 +23,7 @@ class SessionController: CLBaseViewController {
var voiceHoldView: IMVoiceHoldView!
var pureBgOperateView:SessionPureBgOperateView!
var titleStr: String? = nil
// cell
var menuCell: SessionCell?
@ -78,9 +79,10 @@ class SessionController: CLBaseViewController {
}
}
convenience init(conversationId: String) {
convenience init(conversationId: String, title: String? = "") {
self.init()
self.conversationId = conversationId
self.titleStr = title
conversation = V2NIMConversation()
let stings = conversationId.components(separatedBy: "|")
@ -149,11 +151,14 @@ class SessionController: CLBaseViewController {
extension SessionController {
func setupUI() {
view.clipsToBounds = true
view.clipsToBounds = false //
navigationView.backgroundColor = .clear
navigationView.backButton.setImage(nil, for: .normal)
sessionNavigationView = {
let v = SessionNavigationView()
v.navigationView.title = self.titleStr ?? ""
v.navigationView.iconImgView.isHidden = false
view.addSubview(v)
v.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
@ -368,9 +373,9 @@ extension SessionController {
}
func doKeyboardShowActions(){
self.inputBar.isHidden = false
self.inputBar.isHidden = true
self.inputEntrance.isHidden = true
self.inputEntrance.isHidden = false
// self.moreView.isHidden = true
//showMoreItems(show: false)
}

View File

@ -180,11 +180,13 @@ class SessionNavigationView: UIView {
private func setupViews() {
addNavigationView()
navigationView.bgView.alpha = 0
navigationView.backButton.setImage(UIImage(named: "role_back_white"), for: .normal)
navigationView.paddingRightForRightStack = 2
likeView = {
let v = HeartLikeCountView(viewSize: .xl)
v.purIconStyle()
v.isHidden = true
navigationView.rightStackH.addArrangedSubview(v)
return v
}()
@ -192,7 +194,10 @@ class SessionNavigationView: UIView {
naviMoreButton = {
let v = EPIconGhostButton(radius: .none, iconSize: .medium, iconCode: .chatroomMore)
navigationView.rightStackH.addArrangedSubview(v)
v.setBackgroundImage(nil, for: .highlighted)
v.setImage(UIImage(named: "role_chat_setting"), for: .normal)
v.setImage(UIImage(named: "role_chat_setting"), for: .selected)
v.setImage(UIImage(named: "role_chat_setting"), for: .highlighted)
v.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 52, height: 44))
}

View File

@ -122,7 +122,8 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let sessionId = "439217670979585@r@t"
AppRouter.goChatVC(conversationId: sessionId, title: "Character · 18", complete: nil)
}
func collectionView(_ collectionView: UICollectionView,

View File

@ -35,11 +35,11 @@ extension AppRouter{
complete?()
}
static func goChatVC(conversationId: String?, complete: (() -> Void)? = nil) {
static func goChatVC(conversationId: String?, title: String? = nil, complete: (() -> Void)? = nil) {
guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{return}
guard let sessionId = conversationId else{return}
let vc = SessionController(conversationId: sessionId)
let vc = SessionController(conversationId: sessionId, title: title)
let nvc = UIWindow.getTopViewController(base: UIWindow.applicationKey?.rootViewController)?.navigationController
nvc?.pushViewController(vc, animated: true)
complete?()