From 8065fa4bf48eddbfece800dc7460f433b138d74d Mon Sep 17 00:00:00 2001 From: Lakr Date: Wed, 18 Jun 2025 01:50:47 +0800 Subject: [PATCH] feat: input box ui --- .../App/Packages/Intelligents/Package.swift | 2 + .../MainViewController.swift | 39 +++ .../Interface/View/InputBox/InputBox.swift | 239 ++++++++++++++++++ .../InputBox/InputBox.xcassets/Contents.json | 6 + .../Contents.json | 15 ++ .../inputbox.add.attachment.png | Bin 0 -> 1070 bytes .../Contents.json | 15 ++ .../inputbox.deep.thinking.png | Bin 0 -> 2063 bytes .../inputbox.network.imageset/Contents.json | 15 ++ .../inputbox.network.png | Bin 0 -> 2113 bytes .../inputbox.send.imageset/Contents.json | 15 ++ .../inputbox.send.imageset/inputbox.send.png | Bin 0 -> 2062 bytes .../inputbox.tool.imageset/Contents.json | 15 ++ .../inputbox.tool.imageset/inputbox.tool.png | Bin 0 -> 1635 bytes .../View/MainHeaderView/MainHeaderView.swift | 41 ++- 15 files changed, 381 insertions(+), 21 deletions(-) create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/inputbox.add.attachment.png create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/inputbox.deep.thinking.png create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.network.imageset/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.network.imageset/inputbox.network.png create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.send.imageset/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.send.imageset/inputbox.send.png create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.tool.imageset/Contents.json create mode 100644 packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.tool.imageset/inputbox.tool.png diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift index 9cdb8cc65d..76dd137cf9 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift @@ -26,6 +26,8 @@ let package = Package( "Then", .product(name: "Apollo", package: "apollo-ios"), .product(name: "OrderedCollections", package: "swift-collections"), + ], resources: [ + .process("Interface/View/InputBox/InputBox.xcassets") ]), ] ) diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/Controller/MainViewController/MainViewController.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/Controller/MainViewController/MainViewController.swift index c95de6cfd8..0698ce2c02 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/Controller/MainViewController/MainViewController.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/Controller/MainViewController/MainViewController.swift @@ -9,6 +9,10 @@ class MainViewController: UIViewController { $0.delegate = self } + private lazy var inputBox = InputBox().then { + $0.delegate = self + } + // MARK: - Lifecycle override func viewDidLoad() { @@ -32,11 +36,17 @@ class MainViewController: UIViewController { view.backgroundColor = .systemBackground view.addSubview(headerView) + view.addSubview(inputBox) headerView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) make.leading.trailing.equalToSuperview() } + + inputBox.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview() + make.bottom.equalTo(view.keyboardLayoutGuide.snp.top) + } } } @@ -57,3 +67,32 @@ extension MainViewController: MainHeaderViewDelegate { print("Menu tapped") } } + +// MARK: - InputBoxDelegate + +extension MainViewController: InputBoxDelegate { + func inputBoxDidTapAddAttachment() { + + } + + func inputBoxDidTapTool() { + + } + + func inputBoxDidTapNetwork() { + + } + + func inputBoxDidTapDeepThinking() { + + } + + func inputBoxDidTapSend() { + + } + + func inputBoxTextDidChange(_ text: String) { + + } + +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift new file mode 100644 index 0000000000..dbb20ae53f --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift @@ -0,0 +1,239 @@ +import SnapKit +import Then +import UIKit + +protocol InputBoxDelegate: AnyObject { + func inputBoxDidTapAddAttachment() + func inputBoxDidTapTool() + func inputBoxDidTapNetwork() + func inputBoxDidTapDeepThinking() + func inputBoxDidTapSend() + func inputBoxTextDidChange(_ text: String) +} + +class InputBox: UIView { + weak var delegate: InputBoxDelegate? + + private lazy var containerView = UIView().then { + $0.backgroundColor = .systemBackground + $0.layer.cornerRadius = 12 + $0.layer.borderWidth = 0.5 + $0.layer.borderColor = UIColor.systemGray4.cgColor + $0.layer.shadowColor = UIColor.black.cgColor + $0.layer.shadowOffset = CGSize(width: 0, height: 2) + $0.layer.shadowRadius = 6 + $0.layer.shadowOpacity = 0.04 + $0.clipsToBounds = false + } + + private lazy var textView = UITextView().then { + $0.backgroundColor = .clear + $0.font = .systemFont(ofSize: 16) + $0.textColor = .label + $0.isScrollEnabled = false + $0.textContainer.lineFragmentPadding = 0 + $0.textContainerInset = .zero + $0.delegate = self + $0.text = "This is AFFiNE AI" + } + + private lazy var placeholderLabel = UILabel().then { + $0.text = "Write your message..." + $0.font = .systemFont(ofSize: 16) + $0.textColor = .systemGray3 + $0.isHidden = true + } + + private lazy var addButton = UIButton(type: .system).then { + $0.backgroundColor = .systemBackground + $0.layer.cornerRadius = 6 + $0.layer.borderWidth = 0.5 + $0.layer.borderColor = UIColor.systemGray4.cgColor + $0.setImage(UIImage(named: "inputbox.add.attachment", in: .module, with: nil), for: .normal) + $0.tintColor = .secondaryLabel + $0.imageView?.contentMode = .scaleAspectFit + $0.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) + } + + private lazy var toolButton = UIButton(type: .system).then { + $0.setImage(UIImage(named: "inputbox.tool", in: .module, with: nil), for: .normal) + $0.tintColor = .secondaryLabel + $0.imageView?.contentMode = .scaleAspectFit + $0.addTarget(self, action: #selector(toolButtonTapped), for: .touchUpInside) + } + + private lazy var webButton = UIButton(type: .system).then { + $0.setImage(UIImage(named: "inputbox.network", in: .module, with: nil), for: .normal) + $0.tintColor = .secondaryLabel + $0.imageView?.contentMode = .scaleAspectFit + $0.addTarget(self, action: #selector(webButtonTapped), for: .touchUpInside) + } + + private lazy var reactButton = UIButton(type: .system).then { + $0.setImage(UIImage(named: "inputbox.deep.thinking", in: .module, with: nil), for: .normal) + $0.tintColor = .secondaryLabel + $0.imageView?.contentMode = .scaleAspectFit + $0.addTarget(self, action: #selector(reactButtonTapped), for: .touchUpInside) + } + + private lazy var sendButton = UIButton(type: .system).then { + $0.backgroundColor = UIColor.systemBlue + $0.layer.cornerRadius = 19 + $0.setImage(UIImage(named: "inputbox.send", in: .module, with: nil), for: .normal) + $0.tintColor = .white + $0.imageView?.contentMode = .scaleAspectFit + $0.addTarget(self, action: #selector(sendButtonTapped), for: .touchUpInside) + } + + private lazy var leftButtonsStackView = UIStackView().then { + $0.axis = .horizontal + $0.spacing = 16 + $0.alignment = .center + $0.addArrangedSubview(addButton) + } + + private lazy var rightButtonsStackView = UIStackView().then { + $0.axis = .horizontal + $0.spacing = 16 + $0.alignment = .center + $0.addArrangedSubview(toolButton) + $0.addArrangedSubview(webButton) + $0.addArrangedSubview(reactButton) + $0.addArrangedSubview(sendButton) + } + + private lazy var functionsStackView = UIStackView().then { + $0.axis = .horizontal + $0.spacing = 12 + $0.alignment = .center + $0.addArrangedSubview(leftButtonsStackView) + $0.addArrangedSubview(UIView()) // spacer + $0.addArrangedSubview(rightButtonsStackView) + } + + private lazy var mainStackView = UIStackView().then { + $0.axis = .vertical + $0.spacing = 16 + $0.alignment = .fill + $0.addArrangedSubview(textView) + $0.addArrangedSubview(functionsStackView) + } + + private var textViewHeightConstraint: Constraint? + private let minTextViewHeight: CGFloat = 22 + private let maxTextViewHeight: CGFloat = 100 + + var text: String { + get { textView.text ?? "" } + set { + textView.text = newValue + updatePlaceholderVisibility() + updateTextViewHeight() + } + } + + init() { + super.init(frame: .zero) + setupViews() + setupConstraints() + updatePlaceholderVisibility() + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupViews() { + backgroundColor = .clear + addSubview(containerView) + containerView.addSubview(mainStackView) + containerView.addSubview(placeholderLabel) + } + + private func setupConstraints() { + containerView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(16) + } + + mainStackView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(16) + } + + addButton.snp.makeConstraints { make in + make.size.equalTo(38) + } + + for button in [toolButton, webButton, reactButton] { + button.snp.makeConstraints { make in + make.size.equalTo(24) + } + } + + sendButton.snp.makeConstraints { make in + make.size.equalTo(38) + } + + textView.snp.makeConstraints { make in + textViewHeightConstraint = make.height.equalTo(minTextViewHeight).constraint + } + + placeholderLabel.snp.makeConstraints { make in + make.left.right.equalTo(textView) + make.top.equalTo(textView) + } + } + + private func updateTextViewHeight() { + let size = textView.sizeThatFits(CGSize(width: textView.frame.width, height: CGFloat.greatestFiniteMagnitude)) + let newHeight = max(minTextViewHeight, min(maxTextViewHeight, size.height)) + + textViewHeightConstraint?.update(offset: newHeight) + textView.isScrollEnabled = size.height > maxTextViewHeight + + UIView.animate( + withDuration: 0.5, + delay: 0, + usingSpringWithDamping: 0.8, + initialSpringVelocity: 1.0, + options: [.curveEaseInOut] + ) { + self.layoutIfNeeded() + self.superview?.layoutIfNeeded() + } + } + + private func updatePlaceholderVisibility() { + placeholderLabel.isHidden = !textView.text.isEmpty + } + + @objc private func addButtonTapped() { + delegate?.inputBoxDidTapAddAttachment() + } + + @objc private func toolButtonTapped() { + delegate?.inputBoxDidTapTool() + } + + @objc private func webButtonTapped() { + delegate?.inputBoxDidTapNetwork() + } + + @objc private func reactButtonTapped() { + delegate?.inputBoxDidTapDeepThinking() + } + + @objc private func sendButtonTapped() { + delegate?.inputBoxDidTapSend() + } +} + +// MARK: - UITextViewDelegate + +extension InputBox: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + updatePlaceholderVisibility() + updateTextViewHeight() + delegate?.inputBoxTextDidChange(textView.text) + } +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/Contents.json b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/Contents.json b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/Contents.json new file mode 100644 index 0000000000..a56be13406 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "inputbox.add.attachment.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/inputbox.add.attachment.png b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.add.attachment.imageset/inputbox.add.attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..7140de8b2501c293ad149ec01d100a9dfc00e20d GIT binary patch literal 1070 zcmeAS@N?(olHy`uVBq!ia0vp^K0xft!3HFGPB--dDVB6cUq=Rpjs4tz5?O(Km(&Q) zG+$o^Eg+kNfw4W4fd$B71Y#*5W?*1mz{tQ1q!~eC3z*=tJ`0%PY+sNH(g+la|?+^1EKYuyKZr4pZMcTdCfz)mLA!&A-1W z_Vp)?p2T|gva*=#d(WpB*dC=-hvyi zzW=`OE8ek6rp6}8*G#FvWmlCYMX0(L!?7cD9a45!;AC}=qRkzV)9v#^?1}EzF#`!CoyJvp#+?n^UYvWTieSl-zFRJpy3NAhr!&AM4baX4J8{HE_X^CV>-j&AN>?zU!~_vs7MjvuJANxr_I{cf}9lj~~M>Zb+fr5Jl{javF7-q0iB2*Y>% zWaXHd2M>FFeWBQ?Eg*d;7kL*rLm2s%F ze5sfIlkg?*`gP}LWs^+bOyHb*c&9u6$zp?F6D+w5)NC2m4$NSg@#;|`Q;%oEN`-t)+g6ywz()An8EMVz-geyxNKL&42EQ(gjL<`Xxz7qYusJ0&v@?YCoB4g z%V5`Iz3HdZw-i0$FyQSw{&>scP0pgPGPXbPe{;>*_elLzm6_LfReasfwPgK?i&JB! z$kjHs+bTtF*XyeIcbP0l+XkK!0N#N literal 0 HcmV?d00001 diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/Contents.json b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/Contents.json new file mode 100644 index 0000000000..a6380ac93e --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "inputbox.deep.thinking.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/inputbox.deep.thinking.png b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.xcassets/inputbox.deep.thinking.imageset/inputbox.deep.thinking.png new file mode 100644 index 0000000000000000000000000000000000000000..a785290da4b6053131f36607dae8779c0a0264ed GIT binary patch literal 2063 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