Reputation: 9
I am working on a chat view and trying to override keyboard avoidance. As it is, when I tap on the TextField
to enter a comment, the entire view gets pushed upward, instead of just the textfield itself with the keyboard popping up below it. The chat view is presented via .sheet
with a presentationDetent of 0.8.
I have tried using .ignoresSafeArea(.keyboard, edges: .bottom)
after embedding the view in a GeometryReader and a ZStack but to no avail. I've also tried separating the components into different views.
Here is the relevant code:
struct TakesChatView: View {
@ObservedObject var viewModel: TakeCommentViewModel
@FocusState var focus: Bool
@State private var selectedMedia: [PhotosPickerItem] = []
@State private var selectedImageData: [Data] = []
@State private var selectedGIFData: [Data] = []
@State private var selectedVideoData: [Data] = []
var textFieldNotEmpty: Bool {
!viewModel.textToPost.isEmpty ||
!selectedImageData.isEmpty ||
!selectedGIFData.isEmpty ||
!selectedVideoData.isEmpty
}
var body: some View {
ZStack {
GeometryReader { _ in
VStack {
contentView
commentTextField
.padding(.horizontal, 10)
}
}
}
.ignoresSafeArea(.keyboard, edges: .bottom)
.navigationTitle(viewModel.take.title)
.navigationBarTitleDisplayMode(.inline)
.background(Color.containerBackground)
.onChange(of: viewModel.focus) { focus in
self.focus = focus
}
.onChange(of: selectedMedia) { _ in
loadMedia()
}
}
var contentView: some View {
VStack {
Spacer().frame(height: 105)
ScrollViewReader { scroll in
ScrollView(showsIndicators: true) {
ForEach(viewModel.comments, id: \.self) { comment in
TakeCommentView(
comment: comment,
viewModel: self.viewModel
)
.padding(.horizontal, 10)
.id(comment.documentID)
}
.onChange(of: viewModel.commentAdded) { id in
scroll.scrollTo(id, anchor: .bottom)
viewModel.commentAdded = nil
}
}
.scrollDismissesKeyboard(.immediately)
}
}
}
}
extension TakesChatView {
@ViewBuilder
var commentTextField: some View {
VStack {
HStack {
TextField("Type your comment", text: $viewModel.textToPost, axis: .vertical)
.keyboardType(.twitter)
.padding([.leading, .vertical], 6)
.focused($focus)
PhotosPicker(selection: $selectedMedia, matching: .any(of: [.images, .videos]), photoLibrary: .shared()) {
ComposeType.media.image
.frame(width: 40, height: 40, alignment: .center)
}
.onAppear {
selectedMedia.removeAll()
selectedImageData.removeAll()
selectedGIFData.removeAll()
selectedVideoData.removeAll()
}
postButton
}
.border(.lightGray, width: 1, cornerRadius: 10)
.padding([.bottom, .horizontal], 10)
}
}
}
What should I do in order to have the keyboard avoidance be removed so that when I tap on the TextField, it'll be like Instagram's comment section for example where the textfield moves up with the keyboard popping up below it, but the view itself that's displaying the comments isn't shifted upward entirely?
Edit: I have made reproducible code to represent my issue. The code is found below:
struct ButtonView: View {
@State private var showChat: Bool = false
var body: some View {
VStack {
Button {
showChat.toggle()
} label: {
Text("Tap Me!")
}
}
.sheet(isPresented: $showChat, content: {
ContentView(viewModel: ContentViewViewModel())
.presentationDetents([.fraction(0.8)])
.presentationDragIndicator(.hidden)
.overlay(
VStack {
RoundedRectangle(cornerRadius: 2)
.fill(Color.gray)
.frame(width: 40, height: 5)
.padding(.top, 15)
.opacity(0.8)
Label("Chat", systemImage: "message.badge")
.lineLimit(nil)
.padding(.top, 5)
.padding([.leading, .trailing], 16)
Divider()
.padding(.top, 5)
.padding([.leading, .trailing], 16)
Spacer()
}
.frame(maxWidth: .infinity, alignment: .top)
)
})
}
}
struct UserMockComments: Identifiable {
let userName: String
var comment: String
var id = UUID().uuidString
}
class ContentViewViewModel: ObservableObject {
@Published var mock = [UserMockComments]()
@Published var text: String = ""
init() {
fetchData()
}
func fetchData() {
let mockComments: [UserMockComments] = [
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!"),
.init(userName: "OHeg12", comment: "Hello World!")
]
self.mock = mockComments
}
}
struct ContentView: View {
@StateObject var viewModel = ContentViewViewModel()
@FocusState var focus: Bool
var body: some View {
ZStack {
GeometryReader { _ in
VStack {
comments
commentTextField
.padding(.horizontal, 10)
}
}
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
var comments: some View {
VStack {
Spacer().frame(height: 105)
ScrollViewReader { scroll in
ScrollView(showsIndicators: true) {
ForEach(viewModel.mock) { mockComments in
Text(mockComments.userName)
Text(mockComments.comment)
.padding(.horizontal, 10)
.id(mockComments.id)
}
}
.scrollDismissesKeyboard(.immediately)
}
}
}
@ViewBuilder
var commentTextField: some View {
VStack {
HStack {
TextField("Type your comment", text: $viewModel.text, axis: .vertical)
.keyboardType(.twitter)
.padding([.leading, .vertical], 6)
.focused($focus)
}
.padding([.bottom, .horizontal], 10)
}
}
}
#Preview {
@StateObject var viewModel = ContentViewViewModel()
return ContentView(viewModel: viewModel)
}
ButtonView
is the main view of the app and tapping on the button loads up the chat view with the issue I'm having.
Upvotes: -1
Views: 395
Reputation: 1557
Try the code below as your ContentView
body:
var body: some View {
ZStack {
GeometryReader { _ in
VStack {
comments
.frame(maxWidth: .infinity, alignment: .center) //center the comments
// commentTextField
// .padding(.horizontal, 10)
}
}
}
.safeAreaInset(edge: .bottom) {
commentTextField
.padding(.top,5)
.background(.white)
}
// .ignoresSafeArea(.keyboard, edges: .bottom)
}
Upvotes: 0