Skip to content

Commit

Permalink
refactor: use pure SwiftUI for input views (#59)
Browse files Browse the repository at this point in the history
* refactor: remove attributedTextStyle

* refactor: use pure swiftui input view

* chore: add import [skip ci]

* chore: correct lint issue [skip ci]
  • Loading branch information
EnesKaraosman authored Apr 10, 2024
1 parent 3931556 commit ec6300e
Show file tree
Hide file tree
Showing 15 changed files with 44 additions and 653 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let package = Package(
dependencies: [
// Image downloading library
.package(url: "https://github.com/onevcat/Kingfisher.git", from: "7.11.0"),
.package(url: "https://github.com/EnesKaraosman/SwiftUIEKtensions.git", from: "0.2.0"),
.package(url: "https://github.com/EnesKaraosman/SwiftUIEKtensions.git", from: "0.4.0"),
.package(url: "https://github.com/dkk/WrappingHStack.git", from: "2.2.11")
],
targets: [
Expand Down
27 changes: 8 additions & 19 deletions Sources/SwiftyChat/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,23 @@ public struct ChatView<Message: ChatMessage, User: ChatUser>: View {
@Binding private var scrollToBottom: Bool
@State private var isKeyboardActive = false

@State private var contentSizeThatFits: CGSize = .zero
private var messageEditorHeight: CGFloat {
min(
contentSizeThatFits.height,
0.25 * UIScreen.main.bounds.height
)
}

public var body: some View {
GeometryReader { geometry in
ZStack(alignment: .bottom) {
chatView(in: geometry)
inputView()
.onPreferenceChange(ContentSizeThatFitsKey.self) {
contentSizeThatFits = $0
}
.frame(height: messageEditorHeight)
.padding(.bottom, 12)
.safeAreaInset(
edge: .bottom,
content: inputView
)

PIPVideoCell<Message>()
}
.iOS { $0.keyboardAwarePadding() }
.iOSOnlyModifier { $0.keyboardAwarePadding() }
}
.environmentObject(DeviceOrientationInfo())
.environmentObject(VideoManager<Message>())
.edgesIgnoringSafeArea(.bottom)
.iOS { $0.dismissKeyboardOnTappingOutside() }
.iOSOnlyModifier { $0.dismissKeyboardOnTappingOutside() }
}

@ViewBuilder private func chatView(in geometry: GeometryProxy) -> some View {
Expand Down Expand Up @@ -102,7 +92,7 @@ public struct ChatView<Message: ChatMessage, User: ChatUser>: View {
.frame(height: inset.bottom)
.id("bottom")
}
.padding(EdgeInsets(top: inset.top, leading: inset.leading, bottom: 0, trailing: inset.trailing))
.padding(inset)
.onChange(of: scrollToBottom) { value in
if value {
withAnimation {
Expand All @@ -117,7 +107,7 @@ public struct ChatView<Message: ChatMessage, User: ChatUser>: View {
scrollTo = nil
}
}
.iOS {
.iOSOnlyModifier {
// Auto Scroll with Keyboard Notification
$0.onReceive(
NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
Expand All @@ -137,7 +127,6 @@ public struct ChatView<Message: ChatMessage, User: ChatUser>: View {
}
}
.background(Color.clear)
.padding(.bottom, messageEditorHeight + 30)
}

}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftyChat/Common/Modifiers/AvatarModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ internal struct AvatarModifier<Message: ChatMessage, User: ChatUser>: ViewModifi
@ViewBuilder private var avatarImage: some View {
if(!showAvatarForMessage){
blankAvatar
}else if let imageURL = user.avatarURL, currentStyle.imageStyle.imageSize.width > 0 {
} else if let imageURL = user.avatarURL, currentStyle.imageStyle.imageSize.width > 0 {
KFImage(imageURL).resizable()
} else if let avatar = user.avatar, currentStyle.imageStyle.imageSize.width > 0 {
Image(uiImage: avatar).resizable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import SwiftUI
#if os(iOS)
internal extension UIApplication {
func addTapGestureRecognizer() {
guard let window = windows.first else { return }
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
guard let window = windowScene?.windows.first else { return }
let tapGesture = AnyGestureRecognizer(target: window, action:#selector(UIView.endEditing))
tapGesture.requiresExclusiveTouchType = false
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self //I don't use window as delegate to minimize possible side effects
tapGesture.delegate = self // I don't use window as delegate to minimize possible side effects
window.addGestureRecognizer(tapGesture)
}
}
Expand Down
36 changes: 15 additions & 21 deletions Sources/SwiftyChat/Common/Modifiers/KeyboardAwareModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,26 @@
// Copyright © 2020 All rights reserved.
//

import SwiftUI
#if os(iOS)
import Combine
import SwiftUI
import SwiftUIEKtensions

internal struct KeyboardAwareModifier: ViewModifier {

@State private var keyboardHeight: CGFloat = 0

private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
Publishers.Merge(
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue }
.map { $0.cgRectValue.height },
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat(0) }
).eraseToAnyPublisher()
internal extension View {
func keyboardAwarePadding() -> some View {
ModifiedContent(content: self, modifier: KeyboardAwareModifier())
}

public func body(content: Content) -> some View {
}

struct KeyboardAwareModifier: ViewModifier {
@State private var keyboardHeight: CGFloat = .zero

func body(content: Content) -> some View {
content
.padding(.bottom, keyboardHeight)
.onReceive(keyboardHeightPublisher) { height in
withAnimation(.easeOut(duration: 0.2)) {
keyboardHeight = height
}
.onKeyboardAppear { height in
keyboardHeight = height
}
}
}
#endif
16 changes: 0 additions & 16 deletions Sources/SwiftyChat/Extension/View++.swift

This file was deleted.

49 changes: 11 additions & 38 deletions Sources/SwiftyChat/InputView/BasicInputView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,58 +10,29 @@ import SwiftUI
public struct BasicInputView: View {

@Binding private var message: String
@Binding private var isEditing: Bool
private let placeholder: String

@State private var contentSizeThatFits: CGSize = .zero

private var internalAttributedMessage: Binding<NSAttributedString> {
Binding<NSAttributedString>(
get: {
NSAttributedString(
string: self.message,
attributes: [
NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body),
NSAttributedString.Key.foregroundColor: UIColor.label,
]
)
},
set: { self.message = $0.string }
)
}

private var onCommit: ((ChatMessageKind) -> Void)?

public init(
message: Binding<String>,
isEditing: Binding<Bool>,
placeholder: String = "",
onCommit: @escaping (ChatMessageKind) -> Void
) {
self._message = message
self.placeholder = placeholder
self._isEditing = isEditing
self._contentSizeThatFits = State(initialValue: .zero)
self.onCommit = onCommit
}

private var messageEditorHeight: CGFloat {
min(
self.contentSizeThatFits.height,
0.25 * UIScreen.main.bounds.height
)
}

@ViewBuilder
private var messageEditorView: some View {
MultilineTextField(
attributedText: self.internalAttributedMessage,
placeholder: placeholder,
isEditing: self.$isEditing
)
.onPreferenceChange(ContentSizeThatFitsKey.self) {
self.contentSizeThatFits = $0
if #available(iOS 16.0, *) {
TextField(placeholder, text: $message, axis: .vertical)
.lineLimit(5)
} else {
TextEditor(text: $message)
.frame(maxHeight: 64)
}
.frame(height: self.messageEditorHeight)
}

private var sendButton: some View {
Expand All @@ -83,12 +54,14 @@ public struct BasicInputView: View {
}

public var body: some View {
VStack {
Divider()
VStack(spacing: .zero) {
Divider().padding(.bottom, 8)
HStack {
self.messageEditorView
self.sendButton
}
}
.padding(.horizontal, 8)
.padding(.bottom, 36)
}
}
Loading

0 comments on commit ec6300e

Please sign in to comment.