聊天输入框细节
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "launch_bg@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "launch_bg@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 643 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/chat_scr_bottom.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "chat_scr_bottom@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "chat_scr_bottom@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/chat_scr_bottom.imageset/chat_scr_bottom@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/chat_scr_bottom.imageset/chat_scr_bottom@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_ai@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_ai@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_ai.imageset/role_chat_ai@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_ai.imageset/role_chat_ai@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_up_cancel.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_up_cancel@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_up_cancel@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_up_cancel.imageset/role_chat_up_cancel@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 349 B |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_up_cancel.imageset/role_chat_up_cancel@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 746 B |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_cancel.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_voice_cancel@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_voice_cancel@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_cancel.imageset/role_chat_voice_cancel@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_cancel.imageset/role_chat_voice_cancel@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 297 KiB |
22
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_normal.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_voice_normal@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "role_chat_voice_normal@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_normal.imageset/role_chat_voice_normal@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 183 KiB |
BIN
Visual_Novel_iOS/Assets.xcassets/Role/role_chat_voice_normal.imageset/role_chat_voice_normal@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 289 KiB |
|
|
@ -13,6 +13,8 @@ class IMVoiceHoldView: UIView {
|
|||
|
||||
var voiceWaveContainer: GradientView!
|
||||
var wave : LottieAnimationView!
|
||||
|
||||
var swipeStackView: UIStackView!
|
||||
|
||||
var tipLabel: UILabel!
|
||||
|
||||
|
|
@ -56,40 +58,33 @@ class IMVoiceHoldView: UIView {
|
|||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
voiceIconDecorationView = {
|
||||
let v = UIImageView()
|
||||
v.image = UIImage(named: "role_chat_voice_normal")
|
||||
addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
make.height.equalTo(v.snp.width).multipliedBy(171/375.0)
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
// gradientBg = {
|
||||
// let colors = [
|
||||
// CLGlobalToken.color(token: .glo_color_black)!.withAlphaComponent(0),
|
||||
// CLGlobalToken.color(token: .glo_color_black)!.withAlphaComponent(0.65),
|
||||
// CLGlobalToken.color(token: .glo_color_black)!,
|
||||
// ]
|
||||
// let v = GradientView(colors: colors, gradientType: .topToBottom)
|
||||
// addSubview(v)
|
||||
// v.snp.makeConstraints { make in
|
||||
// make.edges.equalToSuperview()
|
||||
// }
|
||||
// return v
|
||||
// }()
|
||||
|
||||
voiceWaveContainer = {
|
||||
let gradient = CLSystemToken.gradient(token: .cpgn)
|
||||
let v = GradientView(colors: gradient.colors(), gradientType: .leftToRight)
|
||||
// let gradient = CLSystemToken.gradient(token: .cpgn)
|
||||
// let v = GradientView(colors: gradient.colors(), gradientType: .leftToRight)
|
||||
let v = GradientView(colors: [UIColor.clear], gradientType: .leftToRight)
|
||||
|
||||
v.cornerRadius = 24
|
||||
addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(64)
|
||||
make.leading.equalToSuperview().offset(48)
|
||||
make.trailing.equalToSuperview().offset(-48)
|
||||
make.top.equalTo(voiceIconDecorationView.snp.top).offset(10)
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.equalTo(120)
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
// let temp = UIImageView()
|
||||
// temp.image = UIImage(named: "temp_voice_wave")
|
||||
// v.addSubview(temp)
|
||||
// temp.snp.makeConstraints { make in
|
||||
// make.edges.equalToSuperview()
|
||||
// }
|
||||
|
||||
return v
|
||||
}()
|
||||
|
||||
|
|
@ -113,6 +108,15 @@ class IMVoiceHoldView: UIView {
|
|||
voiceAnimation.isHidden = false
|
||||
return voiceAnimation
|
||||
}()
|
||||
|
||||
swipeStackView = {
|
||||
let stackView = UIStackView(arrangedSubviews: [])
|
||||
stackView.spacing = 5.0
|
||||
stackView.distribution = .fill
|
||||
stackView.alignment = .fill
|
||||
|
||||
return stackView
|
||||
}()
|
||||
|
||||
tipLabel = {
|
||||
let v = UILabel()
|
||||
|
|
@ -121,30 +125,7 @@ class IMVoiceHoldView: UIView {
|
|||
addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(voiceWaveContainer.snp.bottom).offset(24)
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
voiceIconDecorationView = {
|
||||
let v = UIImageView()
|
||||
v.image = UIImage(named: "voice_record_decoration")
|
||||
addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
make.height.equalTo(v.snp.width).multipliedBy(120/393.0)
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
voiceIcon = {
|
||||
let v = UIImageView()
|
||||
let image = MWIconFont.image(fromIcon: .voiceMsg, size: CGSize(width: 32, height: 32), color: CLGlobalToken.color(token: .glo_color_grey_80))
|
||||
v.image = image
|
||||
addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(voiceIconDecorationView).offset(36)
|
||||
make.top.equalTo(voiceWaveContainer.snp.bottom).offset(5)
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -37,10 +37,15 @@ class SessionCoverView: UIView {
|
|||
return btn
|
||||
}()
|
||||
|
||||
lazy var emailBtn: UIButton = {
|
||||
let vipImgView: UIImageView = {
|
||||
let imgView = UIImageView(image: UIImage(named: "role_chat_phone_vip"))
|
||||
return imgView
|
||||
}()
|
||||
|
||||
lazy var aiBtn: 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.setImage(UIImage(named: "role_chat_ai"), for: .normal)
|
||||
btn.addTarget(self, action: #selector(aiBtnClicked), for: .touchDown)
|
||||
btn.alpha = 0
|
||||
return btn
|
||||
}()
|
||||
|
|
@ -70,7 +75,7 @@ class SessionCoverView: UIView {
|
|||
self.aiAnswerBlock?()
|
||||
}
|
||||
|
||||
@objc func emailBtnClicked() {
|
||||
@objc func aiBtnClicked() {
|
||||
self.aiAnswerBlock?()
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +84,7 @@ class SessionCoverView: UIView {
|
|||
self.backgroundColor = expand ? .orange : .blue
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
self.phoneBtn.alpha = expand ? 1 : 0
|
||||
self.emailBtn.alpha = expand ? 1 : 0
|
||||
self.aiBtn.alpha = expand ? 1 : 0
|
||||
self.lineView.alpha = expand ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
|
@ -90,7 +95,9 @@ class SessionCoverView: UIView {
|
|||
addSubview(lineView)
|
||||
addSubview(bgview)
|
||||
addSubview(phoneBtn)
|
||||
addSubview(emailBtn)
|
||||
addSubview(vipImgView)
|
||||
|
||||
addSubview(aiBtn)
|
||||
|
||||
expandBtn.snp.makeConstraints { make in
|
||||
make.bottom.left.right.equalToSuperview()
|
||||
|
|
@ -110,7 +117,12 @@ class SessionCoverView: UIView {
|
|||
make.width.height.equalTo(30)
|
||||
}
|
||||
|
||||
emailBtn.snp.makeConstraints { make in
|
||||
vipImgView.snp.makeConstraints { make in
|
||||
make.right.equalTo(phoneBtn.snp.right)
|
||||
make.bottom.equalTo(phoneBtn.snp.bottom)
|
||||
}
|
||||
|
||||
aiBtn.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalTo(phoneBtn.snp.top).offset(-20)
|
||||
make.width.height.equalTo(30)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class SessionInputOperateView: UIView {
|
|||
var inputTextView: CLTextView!
|
||||
let minTextViewHeight = 40.0//56.0
|
||||
var sendBtn: UIButton!
|
||||
var isFirstIn: Bool = true
|
||||
|
||||
//var tapInputFieldAction: (() -> Void)?
|
||||
// var voiceHoldAction: ((_ onVoice: Bool) -> Void)?
|
||||
|
|
@ -111,7 +112,7 @@ class SessionInputOperateView: UIView {
|
|||
safeView.addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
make.left.equalTo(expandView.snp.right).offset(12)
|
||||
make.right.equalToSuperview().offset(-24)
|
||||
make.right.equalToSuperview().offset(-20)
|
||||
// make.height.equalTo(48)
|
||||
make.top.equalToSuperview().inset(16)
|
||||
make.centerY.equalToSuperview()
|
||||
|
|
@ -164,6 +165,7 @@ class SessionInputOperateView: UIView {
|
|||
let v = CLTextView()
|
||||
block.addSubview(v)
|
||||
v.placeholder = "Type a message..."
|
||||
v.textContainerInset = UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 0)
|
||||
v.placeholderTextColor = UIColor.init(white: 1, alpha: 0.4)
|
||||
v.backgroundColor = .clear
|
||||
v.limit.maxCharacterNumber = 500
|
||||
|
|
@ -182,10 +184,10 @@ class SessionInputOperateView: UIView {
|
|||
|
||||
voiceHoldView = {
|
||||
let v = UIView()
|
||||
block.addSubview(v)
|
||||
block.insertSubview(v, belowSubview: modeButton)
|
||||
v.snp.makeConstraints { make in
|
||||
make.top.bottom.equalToSuperview()
|
||||
make.left.right.equalTo(inputTextView)
|
||||
make.left.right.top.bottom.equalToSuperview()
|
||||
// make.left.right.equalTo(inputTextView)
|
||||
}
|
||||
|
||||
let voiceImgView = UIImageView(image: UIImage(named: "role_chat_remind_voice"))
|
||||
|
|
@ -221,7 +223,7 @@ class SessionInputOperateView: UIView {
|
|||
|
||||
// 添加长按手势
|
||||
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
|
||||
longPress.minimumPressDuration = 0.5 // 长按时间,默认 0.5 秒
|
||||
longPress.minimumPressDuration = 0.2 // 长按时间,默认 0.5 秒
|
||||
voiceHoldView.addGestureRecognizer(longPress)
|
||||
|
||||
$state.sink { [weak self] state in
|
||||
|
|
@ -237,30 +239,42 @@ class SessionInputOperateView: UIView {
|
|||
// let size = CGSize(width: 26, height: 26)
|
||||
switch stateOf {
|
||||
case .text:
|
||||
// fakeTextfield.isHidden = false
|
||||
inputTextView.isHidden = false
|
||||
voiceHoldView.isHidden = true
|
||||
// let image = MWIconFont.image(fromIcon:4 .voiceMsg, size: size, color: .white)
|
||||
sendBtn.isHidden = false
|
||||
let image = UIImage(named: "role_chat_voice")
|
||||
modeButton.setImage(image, for: .normal)
|
||||
if !isFirstIn {
|
||||
self.fixTextViewHeight()
|
||||
self.inputTextView.becomeFirstResponder()
|
||||
}
|
||||
case .voice:
|
||||
// fakeTextfield.isHidden = true
|
||||
inputTextView.isHidden = true
|
||||
voiceHoldView.isHidden = false
|
||||
sendBtn.isHidden = true
|
||||
let image = UIImage(named: "role_chat_keyboard")
|
||||
modeButton.setImage(image, for: .normal)
|
||||
self.inputTextView.snp.updateConstraints { make in
|
||||
make.height.equalTo(40)
|
||||
}
|
||||
self.inputTextView.resignFirstResponder()
|
||||
AudioRecordTool.audioAuth()
|
||||
}
|
||||
|
||||
if isFirstIn {
|
||||
isFirstIn = false
|
||||
}
|
||||
}
|
||||
|
||||
private func fixTextViewHeight(){
|
||||
if inputTextView.contentSize.height > minTextViewHeight {
|
||||
inputTextView.snp.updateConstraints { make in
|
||||
make.height.equalTo(max(inputTextView.contentSize.height, 48)) // 73
|
||||
let maxH = inputTextView.contentSize.height
|
||||
make.height.equalTo(max(maxH >= 173 ? 173 : maxH, 48)) // 73
|
||||
}
|
||||
}else{
|
||||
inputTextView.snp.updateConstraints { make in
|
||||
make.height.equalTo(inputTextView.contentSize.height) //minTextViewHeight
|
||||
make.height.equalTo(minTextViewHeight) //minTextViewHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,6 +393,8 @@ extension SessionController {
|
|||
|
||||
@objc func tapNaviMore(sender: UIButton) {
|
||||
// sessionNavigationView.upDownNoticeView.showUnlocked(string: "XY")
|
||||
inputEntrance.inputTextView.resignFirstResponder()
|
||||
|
||||
let vc = ChatSettingListController()
|
||||
vc.aiId = aiId
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,44 @@ extension SessionController {
|
|||
return v
|
||||
}()
|
||||
|
||||
toolView = {
|
||||
let view = UIView()
|
||||
bottomViewsStackV.addArrangedSubview(view)
|
||||
|
||||
let exchangeBtn = UIButton(type: .custom)
|
||||
exchangeBtn.setImage(UIImage(named: "role_chat_change_open"), for: .normal)
|
||||
// exchangeBtn.addTarget(, action: <#T##Selector#>, for: <#T##UIControl.Event#>)
|
||||
// view.addSubview(exchangeBtn)
|
||||
// exchangeBtn.snp.makeConstraints { make in
|
||||
// make.right.equalToSuperview().inset(20)
|
||||
// make.bottom.equalToSuperview().inset(2)
|
||||
// make.top.equalToSuperview().inset(5)
|
||||
// }
|
||||
|
||||
let scrToBottomBtn = UIButton(type: .custom)
|
||||
scrToBottomBtn.setImage(UIImage(named: "chat_scr_bottom"), for: .normal)
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [scrToBottomBtn, exchangeBtn])
|
||||
stackView.spacing = 10.0
|
||||
stackView.alignment = .fill
|
||||
stackView.alignment = .fill
|
||||
view.addSubview(stackView)
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.right.equalToSuperview().inset(20)
|
||||
make.bottom.equalToSuperview().inset(2)
|
||||
make.top.equalToSuperview().inset(5)
|
||||
}
|
||||
|
||||
let vipImgView = UIImageView(image: UIImage(named: "role_chat_vip"))
|
||||
vipImgView.isUserInteractionEnabled = false
|
||||
view.addSubview(vipImgView)
|
||||
vipImgView.snp.makeConstraints { make in
|
||||
make.bottom.equalToSuperview().inset(0)
|
||||
make.right.equalToSuperview().inset(18)
|
||||
}
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
inputEntrance = {
|
||||
let v = SessionInputOperateView()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class SessionController: CLBaseViewController {
|
|||
|
||||
// MARK: BottomViews
|
||||
var bottomViewsStackV : InputStackView!
|
||||
var toolView: UIView!
|
||||
var inputEntrance: SessionInputOperateView!
|
||||
var inputBar: SessionInputView!
|
||||
var moreView: IMMoreItemView!
|
||||
|
|
@ -395,7 +396,7 @@ extension SessionController {
|
|||
// self.bottomViewsStackV.setNeedsDisplay()
|
||||
// self.bottomViewsStackV.layoutIfNeeded()
|
||||
// showMoreItems(show: false)
|
||||
hideAllBottomViews(except: [inputEntrance])
|
||||
hideAllBottomViews(except: [toolView, inputEntrance])
|
||||
updateInputEntrance(offsetY: 0, duration: 0, curve: UIView.AnimationCurve.easeOut.rawValue) // 0.3
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@ class SessionNavigationView: UIView {
|
|||
|
||||
func refreshMoreButtonBadge(){
|
||||
let config = AppCache.fetchCache(key: CacheKey.chatRedBadgeConfig.rawValue, type: ChatSettingCacheConfig.self) ?? ChatSettingCacheConfig()
|
||||
moreBadge.isHidden = config.chatBackgroundTapped && config.chatBubbleTapped
|
||||
// moreBadge.isHidden = config.chatBackgroundTapped && config.chatBubbleTapped
|
||||
moreBadge.isHidden = true
|
||||
}
|
||||
|
||||
@objc private func notiMoreButtonBadgeChanged(){
|
||||
|
|
@ -207,6 +208,7 @@ class SessionNavigationView: UIView {
|
|||
moreBadge = {
|
||||
let v = BadgeView()
|
||||
v.onlyShowPoint = true
|
||||
v.isHidden = true
|
||||
navigationView.addSubview(v)
|
||||
v.snp.makeConstraints { make in
|
||||
// make.top.equalTo(naviMoreButton)
|
||||
|
|
|
|||