Unable to simultaneously satisfy constraints. SwiftUI - ios

I am new to Swift and SwiftUI and I am not sure as to why I am experiencing this error as I do not have any view modifiers on any of my linked views, and yet I have this error:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6000003807d0 'accessoryView.bottom' _UIRemoteKeyboardPlaceholderView:0x7fc40b6609d0.bottom == _UIKBCompatInputView:0x7fc40b577c70.top (active)>",
"<NSLayoutConstraint:0x6000003be350 'assistantHeight' SystemInputAssistantView.height == 45 (active, names: SystemInputAssistantView:0x7fc40b5050a0 )>",
"<NSLayoutConstraint:0x600000380aa0 'assistantView.bottom' SystemInputAssistantView.bottom == _UIKBCompatInputView:0x7fc40b577c70.top (active, names: SystemInputAssistantView:0x7fc40b5050a0 )>",
"<NSLayoutConstraint:0x600000380af0 'assistantView.top' V:[_UIRemoteKeyboardPlaceholderView:0x7fc40b6609d0]-(0)-[SystemInputAssistantView] (active, names: SystemInputAssistantView:0x7fc40b5050a0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000380af0 'assistantView.top' V:[_UIRemoteKeyboardPlaceholderView:0x7fc40b6609d0]-(0)-[SystemInputAssistantView] (active, names: SystemInputAssistantView:0x7fc40b5050a0 )>
My SwiftUI Views are as follows:
struct RecordView: View {
#EnvironmentObject var modelView : JournalRecordsModelView
#State private var navigationPath: [JournalRecordsModel.Record] = []
#State private var showAddRecord: Bool = false
var body: some View {
NavigationStack(path: $navigationPath) {
List {
ForEach(modelView.currentData) { record in
NavigationLink(value: record, label: { Text(record.timeDate) })
}.onDelete(perform: { index in
index.forEach({ i in
modelView.deleteRecord(i)
})
})
}
.navigationDestination(for: JournalRecordsModel.Record.self) { record in
RecordDetailedView(record: record, navigationPath: $navigationPath).environmentObject(modelView)
}
.navigationTitle("Your Records")
.navigationBarItems(trailing: Button(action: {
showAddRecord.toggle()
}, label: {
Image(systemName: "plus")
}))
.sheet(isPresented: $showAddRecord) {
AddRecordView(showAddRecord: self.$showAddRecord).environmentObject(modelView)
}
}
}
}
struct AddRecordView: View {
#EnvironmentObject var modelView : JournalRecordsModelView
#Binding var showAddRecord: Bool
#State private var showSubmitAddAlert: Bool = false
#State private var dateTime = Date.now
#State private var title: String = ""
#State private var content: String = ""
#State private var feeling: String = "Nil"
var body: some View {
HStack {
Text("Add Record")
.font(.title)
.fontWeight(.bold)
.frame(
width: UIScreen.main.bounds.width / 2.2,
height: 20,
alignment: .leading
).padding([.leading])
Button(action : {
showAddRecord.toggle()
},
label: {
Image(systemName: "xmark")
}).frame(
width: UIScreen.main.bounds.width / 2.2,
height: 20,
alignment: .trailing
)
.padding([.trailing])
}.padding([.top, .bottom])
DatePicker("Date and Time", selection: $dateTime)
.padding(.horizontal)
TextField("Entry Name", text: $title)
.padding(.horizontal)
TextField("What are your thoughts today?", text: $content)
.padding(.horizontal)
Text("How are you feeling?")
.font(.body)
.padding(.horizontal)
Picker("How do you feel?", selection: $feeling) {
ForEach(modelView.currentFeelings, id: \.self) { feeling in
Text(feeling)
}
}
.padding(.horizontal)
.pickerStyle(MenuPickerStyle())
Spacer()
Button {
modelView.addRecord(dateTime, title, content, feeling)
showSubmitAddAlert.toggle()
}
label: {
Image(systemName: "doc.fill.badge.plus")
}
.disabled(title.isEmpty || content.isEmpty || feeling == "Nil")
.alert("Record added. Please confirm addition of record.", isPresented: $showSubmitAddAlert) {
//the moment i click OK on the alert, have constraints error, button is causing the error
**Button("Ok", role: .cancel) {
showAddRecord.toggle()
}**
}
}
}
The button in AddRecordView seems to be causing the problem but I am not sure as to why it is causing the problem. The UI and the app does not crash when running even though the error pops out. I would appreciate any advices in advance. Thank you.
I have checked on all variable names and checked any modifiers for my views that are related to sizing of the views, as I assume that the error is related to dimensions.

Message you are seeing is more a hint/warning as an error. There are constraints that do conflict and as the messeages tells you, it will solve this by breaking one constraint. This is not a huge deal, but could lead the UI to not look like you want it to look. You should check the layoutConstraint system anyways to know what is going on here. The listed constraints are system constraints, so it will be hard to figure out by checking them.
What you could try is:
**Button("Ok", role: .cancel) {
showSubmitAddAlert = false
showAddRecord = false
}**
this should close the alert before closing the presented AddRecordView
Also I would recommend setting the bools explicitly to true/false, because in every case there should be no scenario where it is toggled the other way around (makes it much easier to read, and also prevents unwanted behaviour if the value is not set as it should be).

Related

Two extra views put inside of a ForEach with a filtering search bar

So I have a ScrollView that contains a list of all the contacts imported from a user's phone. Above the ScrollView, I have a 'filter search bar' that has a binding that causes the list to show only contacts where the name contains the same string as the search bar filter. For some reason, the last two contacts in the list always pop up at the bottom of the list, no matter what the string is (even if it's a string not contained in any of the contact names on the phone). I tried deleting a contact and the problem persists, because the original contact was just replaced with the new second to last contact. Any help fixing this would be much appreciated!
struct SomeView: View {
#State var friendsFilterText: String = ""
#State var savedContacts: CustomContact = []
var body: some View {
var filteredContactsCount = 0
if friendsFilterText.count != 0 {
for contact in appState.savedContacts {
if contact.name.lowercased().contains(friendsFilterText.lowercased()) {
filteredContactsCount += 1
}
}
} else {
filteredContactsCount = savedContacts.count
}
return HStack {
Image(systemName: "magnifyingglass")
ZStack {
HStack {
Text("Type a name...")
.opacity(friendsFilterText.count > 0 ? 0 : 1)
Spacer()
}
CocoaTextField("", text: $friendsFilterText)
.background(Color.clear)
}
Button(action: {
friendsFilterText = ""
}, label: {
Image(systemName: "multiply.circle.fill")
})
}.frame(height: 38)
HStack(spacing: 10) {
Text("Your contacts (\(filteredContactsCount))")
Spacer()
Button(action: {
fetchContacts()
}, label: {
Image(systemName: "square.and.arrow.down")
})
Button(action: {
// edit button action
}, label: {
Text("Edit")
})
}
ScrollView {
VStack {
ForEach(savedContacts, id: \.self.name) { contact in
if contact.name.lowercased().contains(friendsFilterText.lowercased()) || friendsFilterText.count == 0 {
Button(action: {
// contact button action
}, label: {
HStack(spacing: 20) {
Image(systemName: "person.crop.circle.fill")
.font(.system(size: 41))
.frame(width: 41, height: 41)
VStack(alignment: .leading, spacing: 4) {
Text(contact.name)
Text(contact.phoneNumber)
}
Spacer()
}.frame(height: 67)
})
}
}
}
}
}
}
CustomContact is a custom struct with properties phoneNumber and name. I've attached images below of the issue I'm experiencing. I'm thinking MAYBE it's because there's something off timing-wise with the friendsFilterText and the ForEach rendering but I'm really not sure.
In the image set below, the 'Extra Contact 1' and 'Extra Contact 2' are ALWAYS rendered, unless I add a filter, then switch to a different view, then back to this view (which leads me to believe it's a timing thing again).
https://imgur.com/a/CJW2CUS
You should move the count calculation out of the view into a computed var.
And if CustomContact is your single contact struct, it should actually read #State var savedContacts: [CustomContact] = [] i.e. an array of CustomContact.
The rest worked fine with me, no extra contacts showing.
struct ContentView: View {
#State var friendsFilterText: String = ""
#State var savedContacts: [CustomContact] = []
// computed var
var filteredContactsCount: Int {
if friendsFilterText.isEmpty { return savedContacts.count }
return savedContacts.filter({ $0.name.lowercased().contains(friendsFilterText.lowercased()) }).count
}
var body: some View {
...

The compiler is unable to type-check this expression in a reasonable time in SwiftUI?

I have a line of code that sets the background of Text to an Image that is fetched by finding the first three letters of the string. For some reason this won't run and keeps giving me the error above. Any ideas on how I can fix this?
There are a lot of images that need to be set as the backgrounds for multiple different pieces of text. I believe I have the right idea by using the prefix of the string, but it seems like Xcode is having difficulty/won't run this.
Pretty sure this specific line is giving me issues, but would love some feedback.
.background(Image(colorOption.prefix(3)).resizable())
import SwiftUI
struct ColorView: View {
// #ObservedObject var survey = Survey()
#ObservedObject var api = ColorAPIRequest(survey: DataStore.instance.currentSurvey!)
#State var showingConfirmation = true
#State var showingColorView = false
#State var tempSelection = ""
#EnvironmentObject var survey: Survey
//#EnvironmentObject var api: APIRequest
var colorOptionsGrid: [[String]] {
var result: [[String]] = [[]]
let optionsPerRow = 4
api.colorOptions.dropFirst().forEach { colorOption in
if result.last!.count == optionsPerRow { result.append([]) }
result[result.count - 1].append(colorOption)
}
return result
}
var body: some View {
VStack {
Text("Select Tape Color")
.font(.system(size:70))
.bold()
.padding(.top, 20)
NavigationLink("", destination: LengthView(), isActive: $showingColorView)
HStack {
List {
ForEach(colorOptionsGrid, id: \.self) { colorOptionRow in
HStack {
ForEach(colorOptionRow, id: \.self) { colorOption in
Button(action: {
// self.survey.length = lengthOption
self.tempSelection = colorOption
self.showingConfirmation = false
}
) {
ZStack {
Color.clear
Text(colorOption.prefix(3))
.font(.title)
.foregroundColor(self.tempSelection == colorOption ? Color.white : Color.black)
.frame(width: 200, height: 100)
.background(Image(colorOption.prefix(3)).resizable())
//Image(colorOption.prefix(3)).resizable()
}
}.listRowBackground(self.tempSelection == colorOption ? Color.pink : Color.white)
.multilineTextAlignment(.center)
}
}
}
}.buttonStyle(PlainButtonStyle())
}
Button(action: {
self.survey.color = self.tempSelection
self.showingColorView = true
self.showingConfirmation = true
}) {
Text("Press to confirm \(tempSelection)")
.bold()
.padding(50)
.background(Color.pink)
.foregroundColor(.white)
.font(.system(size:40))
.cornerRadius(90)
}.isHidden(showingConfirmation)
.padding(.bottom, 50)
}
}
}
The compiler actually gives a fairly decent suggestion when it tells you to break the expression up. The simplest you can do is extract the background image into a separate function like this:
func backgroundImage(for colorOption: String) -> some View {
Image(String(colorOption.prefix(3))).resizable()
}
and then replace the call to
.background(Image(colorOption.prefix(3)).resizable())
with
.background(self.backgroundImage(for: colorOption))
Also note that I wrapped colorOption.prefix(3) in a String constructor, simply because .prefix(_:) returns a Substring, but the Image(_:) constructor requires a String.

SwiftUI ForEach no animation delay

I recently updated to Xcode 11.3 and now a perviously working animation delay in my ForEach statement has failed. Below is a simplified version of my code.
Thanks
import SwiftUI
struct ContentView: View {
#State var show: Bool = false
var transition: AnyTransition {
let insertion = AnyTransition.move(edge: .trailing)
let removal = AnyTransition.move(edge: .leading)
return .asymmetric(insertion: insertion, removal: removal)
}
var body: some View {
VStack {
Button(action: { withAnimation { self.show.toggle() } } ) {
Text("start animation")
}
if show == true {
test()
.transition(transition)
}
}
}
}
struct wordArray: Identifiable{
var id = UUID()
var words: String
}
struct test: View{
let circleArray = [
wordArray(words: "This"),
wordArray(words: "Should"),
wordArray(words: "Be"),
wordArray(words: "Delayed"),
]
var body: some View{
VStack{
ForEach(circleArray) { wordArray in
Text("\(wordArray.words)")
.animation(Animation.easeInOut.delay(0.5))
}
Text("like")
.animation(Animation.easeInOut.delay(0.5))
Text("This")
.animation(Animation.easeInOut.delay(1))
}.frame(width: UIScreen.main.bounds.width)
}
}
It may be due to the double VStack. You can change the VStack to Group in testView.
Group{
ForEach(circleArray) { wordArray in
Text("\(wordArray.words)")
.animation(Animation.easeInOut.delay(0.5))
}
Text("like")
.animation(Animation.easeInOut.delay(0.5))
Text("This")
.animation(Animation.easeInOut.delay(1))
}.frame(width: UIScreen.main.bounds.width)
I found a solution to the problems related to, in many cases, views inside ForEach not working with either transitions or animations.
The first solution was to contain the items in a ForEach within a List view.
The other option is to store the group of views in the ForEach, within a ScrollView, which is my preferred options, as a list view comes with a great deal of limitations in what and how you can render it.

SwiftUI ForEach not correctly updating in scrollview

I have a SwiftUI ScrollView with an HStack and a ForEach inside of it. The ForEach is built off of a Published variable from an ObservableObject so that as items are added/removed/set it will automatically update in the view. However, I'm running into multiple problems:
If the array starts out empty and items are then added it will not show them.
If the array has some items in it I can add one item and it will show that, but adding more will not.
If I just have an HStack with a ForEach neither of the above problems occur. As soon as it's in a ScrollView I run into the problems.
Below is code that can be pasted into the Xcode SwiftUI Playground to demonstrate the problem. At the bottom you can uncomment/comment different lines to see the two different problems.
If you uncomment problem 1 and then click either of the buttons you'll see just the HStack updating, but not the HStack in the ScrollView even though you see init print statements for those items.
If you uncomment problem 2 and then click either of the buttons you should see that after a second click the the ScrollView updates, but if you keep on clicking it will not update - even though just the HStack will keep updating and init print statements are output for the ScrollView items.
import SwiftUI
import PlaygroundSupport
import Combine
final class Testing: ObservableObject {
#Published var items: [String] = []
init() {}
init(items: [String]) {
self.items = items
}
}
struct SVItem: View {
var value: String
init(value: String) {
print("init SVItem: \(value)")
self.value = value
}
var body: some View {
Text(value)
}
}
struct HSItem: View {
var value: String
init(value: String) {
print("init HSItem: \(value)")
self.value = value
}
var body: some View {
Text(value)
}
}
public struct PlaygroundRootView: View {
#EnvironmentObject var testing: Testing
public init() {}
public var body: some View {
VStack{
Text("ScrollView")
ScrollView(.horizontal) {
HStack() {
ForEach(self.testing.items, id: \.self) { value in
SVItem(value: value)
}
}
.background(Color.red)
}
.frame(height: 50)
.background(Color.blue)
Spacer()
Text("HStack")
HStack {
ForEach(self.testing.items, id: \.self) { value in
HSItem(value: value)
}
}
.frame(height: 30)
.background(Color.red)
Spacer()
Button(action: {
print("APPEND button")
self.testing.items.append("A")
}, label: { Text("APPEND ITEM") })
Spacer()
Button(action: {
print("SET button")
self.testing.items = ["A", "B", "C"]
}, label: { Text("SET ITEMS") })
Spacer()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UIHostingController(
// problem 1
rootView: PlaygroundRootView().environmentObject(Testing())
// problem 2
// rootView: PlaygroundRootView().environmentObject(Testing(items: ["1", "2", "3"]))
)
Is this a bug? Am I missing something? I'm new to iOS development..I did try wrapping the actual items setting/appending in the DispatchQueue.main.async, but that didn't do anything.
Also, maybe unrelated, but if you click the buttons enough the app seems to crash.
Just ran into the same issue. Solved with empty array check & invisible HStack
ScrollView(showsIndicators: false) {
ForEach(self.items, id: \.self) { _ in
RowItem()
}
if (self.items.count == 0) {
HStack{
Spacer()
}
}
}
It is known behaviour of ScrollView with observed empty containers - it needs something (initial content) to calculate initial size, so the following solves your code behaviour
#Published var items: [String] = [""]
In general, in such scenarios I prefer to store in array some "easy-detectable initial value", which is removed when first "real model value" appeared and added again, when last disappears. Hope this would be helpful.
For better readability and also because the answer didn't work for me. I'd suggest #TheLegend27 answer to be slightly modified like this:
if self.items.count != 0 {
ScrollView(showsIndicators: false) {
ForEach(self.items, id: \.self) { _ in
RowItem()
}
}
}

SwiftUI Textfield Controls, Errors on Exit

SwiftUI Textfield Controls, Errors on Exit
I'm receiving multiple console chatter items when simply moving from textfield to
textfield in SwiftUI. When adding a new record or editing an existing one, if I
hit the keyboard return key (running on a device) I receive no warnings or errors. If
I simply tap another textfield (without using the return key) I get all this chatter.
Start:
19:45:46.969678-0700 ToDo[688:477250] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"",
"",
"",
""
)
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
2019-08-29 19:45:46.995748-0700 ToDo[688:477250] [Snapshotting] Snapshotting a view (0x14fe87ff0, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.
End:
In both cases the data is saved correctly. I am storing the data in Core Data.iOS 13.1, Xcode 11M392r - website says Beta 7, my Xcode says this build is Beta 6, Catalina19A546d.
Here's the view:
struct AddNewToDo: View {
#Environment(\.managedObjectContext) var managedObjectContext
#Environment(\.presentationMode) var presentationMode
#State private var updatedTitle: String = "No Title"
#State private var updatedFirstName: String = "No Title"
#State private var updatedLastName: String = "No Title"
#State private var updatedDate: Date = Date()
#State private var updatedDateString: String = "July 2019"
var body: some View {
VStack {
VStack(alignment: .leading) {
Text("ToDo Title:")
.padding(.leading, 5)
.font(.headline)
TextField("Enter a Title", text: $updatedTitle)
.onAppear {
self.updatedTitle = ""
}
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(10)
VStack(alignment: .leading) {
Text("First Name:")
.padding(.leading, 5)
.font(.headline)
TextField("Enter First Name", text: $updatedFirstName)
.onAppear {
self.updatedFirstName = ""
}
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(10)
VStack(alignment: .leading) {
Text("Last Name:")
.padding(.leading, 5)
.font(.headline)
TextField("Enter Last Name", text: $updatedLastName)
.onAppear {
self.updatedLastName = ""
}
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(10)
VStack(alignment: .leading) {
Text("Created Date:")
.padding(.leading, 5)
.font(.headline)
TextField("Enter a date", text: $updatedDateString)
.onAppear {
let formatter = DateFormatter()
formatter.timeZone = .current
formatter.dateFormat = "M-d-yyyy HH:mm:ss"
let myString = formatter.string(from: Date())
//let myCoreDataString = formatter.string(from: self.toDoItem.createdAt!)
//print(myString)
//print("myCoredataString is \(myCoreDataString)")
self.updatedDateString = myString
}
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding(10)
VStack {
Button(action: ({
let nmo = ToDoItem(context: self.managedObjectContext)
nmo.id = UUID()
nmo.title = self.updatedTitle
nmo.firstName = self.updatedFirstName
nmo.lastName = self.updatedLastName
nmo.createdAt = Date()
do {
try self.managedObjectContext.save()
} catch {
print(error)
}
self.updatedTitle = ""
self.updatedFirstName = ""
self.updatedLastName = ""
self.updatedDateString = ""
self.presentationMode.wrappedValue.dismiss()
})) {
Text("Save")
}
.padding()
}
.padding(10)
Spacer()
}
}
}
Any guidance would be appreciated.

Resources