Я новичок, когда дело доходит до программирования на Swift, и пытаюсь научиться создавать собственное приложение для чата с помощью некоторых руководств на Youtube. Я продвинулся довольно далеко, но попал в ловушку. Я не понимаю, почему, когда я пытаюсь прокрутить это представление в SwiftUI, сообщение обрывается на полпути. Я думал, что это как-то связано с отступами, но отступы должны быть установлены на все. Посмотрите на скриншот ниже:
Я не уверен, что я делаю неправильно в своем коде, поскольку все работает. Мой код прикреплен ниже. Любая помощь будет оценена по достоинству!
import SwiftUI
struct ChatUIView: View {
@State var message = ""
//StateObject is the owner of the object...
@StateObject var allMessages = Messages()
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())
//send button
//hiding view...
if message != ""{
Button(action: {
//appending message...
//adding animation...
withAnimation(.easeIn){
allMessages.messages.append(Message(id: Date(), message: message, myMsg: false))
}
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())
}
//.edgesIgnoringSafeArea(.bottom)
.background(Color.blue.edgesIgnoringSafeArea(.top))
}
}
//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)
.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(width: 35, height: 35))
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)))
}
}
Это выглядит как ошибка ScrollView
, по крайней мере, в iOS 14.1. Если ScrollView
находится в полной области (т. е. без верхнего/нижнего колонтитула), то все в порядке.
Вы можете попробовать использовать fixedSize:
Text(msg.message)
.fixedSize(horizontal: false, vertical: true)
В моем Xcode 12.1/iOS 14.1 это не решает проблему. Вы проверили это? Какая среда?
@Asperi Я использую Xcode 12.3 / iOS 14.3, и он работает, как и ожидалось (даже без fixedSize
). Это действительно похоже на ошибку.
Наконец! Кое-что поправили в новой версии 12.х... до этого встречал только баги ))
@ pawello2222 pawello2222 Я уже пробовал это в своем коде, и это не сработало. решение, которое я принял, сработало для меня
Проблема заключается в использовании .clipShape(RoundedShape())
в VStack, содержащем ScrollView, TextField и Button. Если вы заметили, дело не только в том, что сообщение разрезано пополам, но и в том, что никакие другие сообщения (есть еще одно) не отображаются. Пользовательская форма RoundedShape недостаточно велика, чтобы охватить все виды. Когда я переключаю его на прямоугольник, встроенную форму, все отображается. Я не понимаю, почему в иерархии представлений, но это проблема. Я оставлю это кому-то другому, чтобы определить, что с ним происходит.
Когда вы пытаетесь отладить иерархию представлений, начните с модификаторов. Комментируйте их, пока что-то не изменится, а затем изучите их. Кроме того, порядок модификаторов имеет значение, так как использование модификатора в представлении дает вам совершенно новое представление. Попробуйте что-нибудь простое, например, поставьте .padding
перед .font
на Text
, и вы получите сообщение об ошибке. Это связано с тем, что в дополненном представлении нет шрифта-модификатора, хотя в модифицированном представлении шрифт есть.
Я принял это как ответ, потому что это сработало. Большое спасибо, и я буду иметь это в виду в будущем!
вопрос: я изменил clipShape на Rectangle()
, но теперь, когда я использую клавиатуру, он появляется, а затем быстро исчезает. У меня есть ошибка с .clipShape Rectangle()
Это хороший вопрос, и другой вопрос. SwiftUI не имеет (пока, я надеюсь) родной блокировки клавиатуры, так что придется использовать свою. По сути, вы resignFirstResponder или переключаете фокус на другой элемент. Если это происходит в вашем коде, это объясняет клавиатуру. Я не проверял это, когда отлаживал ваш код, и удалил то, что у меня было.
В качестве отправной точки взгляните на иерархию представлений, чтобы лучше понять, что на самом деле происходит.