I have created a SwiftUI View for each of my menus in my app. However, when I go to the chat view and tap on the UITextField, I get the following now:
I am new to SwiftUI and has able to hack together this view (along with the ContentView.swift below) through some youtube videos. However, I'm not sure how to fix this (and if I just need to change my ContentView.swift file all together). My code is available below:
//Chat.swift
import SwiftUI
import Firebase
import Alamofire
import SwiftyJSON
struct ChatUIView: View {
@State var message = ""
//StateObject is the owner of the object...
@StateObject var allMessages = Messages()
@StateObject private var keyboardHandler = KeyboardHandler()
var body: some View {
VStack{
ZStack{
/*
HStack{
Spacer()
}*/
VStack(spacing:5){
Text("Chat")
.fontWeight(.bold)
}
.foregroundColor(.white)
}
.padding(.all)
//Spacer()
VStack{
//Spacer()
//Displaying Message...
ScrollView(.vertical, showsIndicators: false, content: {
ScrollViewReader{reader in
VStack(spacing: 20){
ForEach(allMessages.messages){msg in
//Chat Bubbles...
ChatBubble(msg: msg)
}
//whenever new data is inserted, scroll to bottom...
.onChange(of: allMessages.messages) {(value) in
//scrolling only user message...
if value.last!.myMsg{
reader.scrollTo(value.last?.id)
}
}
}
.padding([.horizontal, .bottom])
.padding(.top, 25)
}
})
HStack(spacing:15){
HStack(spacing: 15){
TextField("Message", text: self.$message)
}
.padding(.vertical, 12)
.padding(.horizontal)
.background(Color.black
.opacity(0.06)
.clipShape(Capsule())
)
//.clipShape(Capsule())
//send button
//hiding view...
if message != ""{
Button(action: {
//appending message...
//adding animation...
withAnimation(.easeIn){
allMessages.messages.append(Message(id: Date(), message: message, myMsg: true))
}
message = ""
}, label: {
Image("send")
.resizable()
.frame(width: 25, height: 25)
.rotationEffect(.init(degrees: 45))
.padding()
//.aspectRatio(contentMode: .fit)
//.font(.system(size: 0.5))
//.padding(.all)
.background(Color.black.opacity(0.07))
.clipShape(Circle())
})
}
}
.padding(.horizontal)
.animation(.easeOut)
//
}
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.background(Color.white)
//.clipShape(RoundedShape())
.clipShape(Rectangle())
.keyboardType(.default)
.padding(.bottom, keyboardHandler.keyboardHeight)
}
//.edgesIgnoringSafeArea(.bottom)
.background(Color.blue.edgesIgnoringSafeArea(.top))
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
//Chat Bubbles...
struct ChatBubble : View {
var msg : Message
var body: some View{
//Automatics scroll to bottom...
//First assigning id to each row
HStack(alignment: .top, spacing: 10){
if msg.myMsg{
//pushing msg to the left...
//minimum space ...
Spacer(minLength: 25)
Text(msg.message)
.padding(.all)
.background(Color.black.opacity(0.06))
//.cornerRadius(15)
.clipShape(BubbleArrow(myMsg: msg.myMsg))
} else {
//pushing msg to the right...
Text(msg.message)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(nil)
.foregroundColor(.white)
.padding(.all)
//.background(Color.black.opacity(0.06))
.background(Color.blue)
.clipShape(BubbleArrow(myMsg: msg.myMsg))
Spacer(minLength: 25)
}
}
.id(msg.id)
//.padding(msg.myMsg ? .leading : .trailing, 55)
//.padding(.vertical,10)
}
}
// Bubble Arrow...
struct BubbleArrow : Shape {
var myMsg : Bool
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: myMsg ? [.topLeft, .bottomLeft, .bottomRight] : [.topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 10, height: 10))
return Path(path.cgPath)
}
}
// Custom Rounded Shape...
/*
struct RoundedShape : Shape {
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize())
return Path(path.cgPath)
}
}*/
// Model Data For Message...
struct Message : Identifiable, Equatable {
var id: Date
var message: String
var myMsg: Bool
//var profilePic: String
//var photo: Data?
}
class Messages: ObservableObject {
@Published var messages : [Message] = []
//sample data...
init() {
let strings = ["Hi!", "hello!", "How are you doing?!", "Fine, I just want to talk about life", "ok, I may be able to help with that", "This is awesome, thanks", "So what do you want to talk about?", "movies sound like a good topic. Let's start there!", "Ok, so tell me: What's you favorite movie?", "Definitely, interstellar for sure."]
for i in 0..<strings.count{
//simple logic for two sided message View...
messages.append(Message(id: Date(), message: strings[i], myMsg: i % 2 == 0 ? false : true))
}
}
func writeMessage(id: Date, msg: String, photo: Data?, myMsg: Bool){
messages.append((Message(id: id, message: msg, myMsg: myMsg)))
}
}
//KeyboardHandler.swift
import Combine
import SwiftUI
final class KeyboardHandler: ObservableObject {
@Published private(set) var keyboardHeight: CGFloat = 0
private var cancellable: AnyCancellable?
private let keyboardWillShow = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.compactMap { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height }
private let keyboardWillHide = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map {_ in CGFloat.zero }
init() {
cancellable = Publishers.Merge(keyboardWillShow, keyboardWillHide)
.subscribe(on: DispatchQueue.main)
.assign(to: \.self.keyboardHeight, on: self)
}
}
//ContentView.Swift
import SwiftUI
import Firebase
import Alamofire
private var sessionId = ""
struct ContentView: View {
@AppStorage("status") var status = false
var body: some View {
ZStack{
if status{
VStack(spacing: 0){
Home()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home: View{
@State var selectedTab = "Home"
var edges = UIApplication.shared.windows.first?.safeAreaInsets
@Namespace var animation
@StateObject var modelData = ModelView()
@StateObject var model = ModelData()
var body: some View {
VStack(spacing: 0){
GeometryReader{_ in
ZStack{
//Tabs...
NavigationView{
ScrollView{
ForEach(1...2,id: \.self){i in
Button(action: model.logOut, label: {
Image("peace-sign")
.resizable()
.renderingMode(.original)
.frame(width: 45, height: 45)
VStack(alignment: .leading, spacing: 12) {
Text("Log Out")
}
Spacer(minLength: 0)
})
}
.padding(.bottom)
}
.navigationBarHidden(true)
}
.opacity(selectedTab == "Home" ? 1 : 0)
Chat()
.opacity(selectedTab == "Chat" ? 1: 0)
}
}
.onChange(of: selectedTab){(_) in
switch(selectedTab){
case "Chat": if !modelData.isChatLoad{modelData.loadChat()}
default: ()
}
}
//TabView...
HStack(spacing: 0){
ForEach(tabs,id: \.self){tab in
TabButton(title: tab, selectedTab: $selectedTab, animation: animation)
if tab != tabs.last{
Spacer(minLength: 0)
}
}
}
.padding(.horizontal,30)
// for iphone like 8 and SE
.padding(.bottom,edges!.bottom == 0 ? 15 : edges!.bottom)
//.background(Color.primary.opacity(0.2))
}
/*
.ignoresSafeArea(.all, edges: .bottom)
.background(Color.black.opacity(0.06).ignoresSafeArea(.all, edges: .all))
*/
}
}
struct TabButton : View{
var title : String
@Binding var selectedTab : String
var animation : Namespace.ID
var body: some View{
Button(action: {
withAnimation{
selectedTab = title
}
}){
VStack(spacing: 6){
//Top Indicator....
//Custom Shape...
//Slide in and out animation
ZStack{
CustomShape()
//.fill(Color.blue.opacity(selectedTab == title ? 1 : 0))
.fill(Color.clear)
.frame(width: 45, height: 6)
if selectedTab == title{
CustomShape()
//.fill(Color.blue.opacity(selectedTab == title ? 1 : 0))
.fill(Color.blue)
.frame(width: 45, height: 6)
.matchedGeometryEffect(id: "Tab_Change", in: animation)
}
}
.padding(.bottom,10)
Image(title)
.renderingMode(.template)
.resizable()
.foregroundColor(selectedTab == title ? Color.blue : Color.gray)
.frame(width:24, height:24)
/*
Text(title)
.font(.caption)
.foregroundColor(Color.black.opacity(selectedTab == title ? 0.6 : 0.2))
.frame(width: 24, height: 24)
*/
}
}
}
}
var tabs = ["Home", "Chat"]
I think that something needs to be changed in my ContentView.swift file but I don't know how to propagate that change to the Chat.swift UI View. Any help/tips would be greatly appreciated!

