SwiftUI Textfield Alert for macOS - macos

Is it possible (also with detours via AppKit) to create text field alerts for macOS in SwiftUI? I found several tutorials on how to do that with UIKit for iOS but you can't use these solutions for macOS.

With SwiftUI 4 it is now possible to simply add a TextField to the .alert() modifier like you would do this with a Button.
struct ContentView: View {
#State private var showingAlert = false
#State private var input = ""
var body: some View {
Button("Show Alert") {
showingAlert = true
}
.alert("Enter your Name", isPresented: $showingAlert) {
TextField("Your Name", text: $input)
Button("OK") { }
}
}
}

Related

SwiftUI run function on losing focus TextField

I want to run some function whenever a textfield loses focus.
Textfield already has onEditingChange and onCommit but I want to run a validation function once the user leaves the textfield.
Something similar to what onblur does for web without cluttering any other view on screen.
In iOS 15, #FocusState and .focused can give you this functionality. On iOS 13 and 14, you can use onEditingChanged (shown in the second TextField)
struct ContentView: View {
#State private var text = ""
#State private var text2 = ""
#FocusState private var isFocused: Bool
var body: some View {
TextField("Text goes here", text: $text)
.focused($isFocused)
.onChange(of: isFocused) { newValue in
if !newValue {
print("Lost focus")
}
}
TextField("Other text", text: $text2, onEditingChanged: { newValue in
print("Other text:",newValue)
})
}
}

Preview crashes when saving to core data but simulator works

I am creating a simple to do list in swiftUI. When saving tasks to core data in the preview, it crashes. using the simulator works fine however. I tried re-opening the project and looking for similar questions online, but have not found anything yet.
It would be great if anyone could help me, I am new to IOS and Xcode. Thanks a lot!
Here is my code:
import SwiftUI
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(sortDescriptors: []) private var tasks:
// gets stuff from entity "Task" in datamodel
FetchedResults<Task>
#State var name: String = ""
#State var cat: String = ""
var body: some View {
VStack{
TextField("Enter Task Name", text: $name)
TextField("Enter Task Category", text: $cat)
Button(
action: {
let newTask = Task(context: viewContext)
newTask.name = name
newTask.category = cat
do{
try viewContext.save()
}
catch{
let error = error as NSError
fatalError("unresolved error:\(error)")
}
},
label:{
Text("Save Task")
}
)
List{
ForEach(tasks, id:\.self){
task in Text(task.name ?? "untitled")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You are using this
#Environment(\.managedObjectContext) private var viewContext
So you need to pass that in.
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, moc)
}
When using a context in previews, set it up with the
NSPersistentStoreDescription type of NSInMemoryStoreType.

SwiftUI onCommit doesnt move cursor to next textfield - MacOS app

I have two TextFields that use onCommit. Pressing enter saves the value. However, this does not automatically move the cursor to the next TextField. I want it to work like the tab button which moves the cursor to next TextField but this doesn't save the value (due to onCommit requiring enter/return to be pressed). The best solution I have found is using a button but that results in poor usability as I would be using a ForLoop over this view.
struct ModuleDetailView: View {
#Binding var subjectDetails: [Subjects]
#State private var subject = ""
#State private var grade = ""
var body: some View {
VStack {
TextField("Subject", text: $subject, onCommit: appendData)
TextField("Grade", text: $grade, onCommit: appendData)
VStack {
Text("Output")
ForEach(subjectDetails) { subject in
HStack {
Text(subject.name)
Text(subject.grade)
}
}
}
}
}
func appendData() {
if subject != "" && grade != "" {
let module = Subjects(name: subject, grade: grade)
subjectDetails.append(module)
}
}
}
The preview code:
struct ModuleDetailView_Previews: PreviewProvider {
static var previews: some View {
PreviewWrapper()
}
struct PreviewWrapper: View {
#State var modules = [Subjects]()
var body: some View {
ModuleDetailView(subjectDetails: $modules)
}
}
}
both textfields are visible at the same time. What happens is that after I press enter from the first textField the cursor just vanishes, which works differently from when we press TAB - simply moves to the next. And I want it to work similar to how it behaves when TAB is pressed. Therefore, in this case using a firstResponder might not be a good option
Subjects struct:
struct Subjects: Identifiable {
let id = UUID()
var name: String
var grade: String
}

SwiftUI - Edit Button causing a strange (and annoying) animation

I recently implemented an Edit Button into my app. However, it created a strange and annoying animation in the View both when it loads (everything comes in from the left), and when I sort the elements. I noticed that if I remove the .animation that is after the .environment it solves this issue, but then everything appears and moves instantly without the .easeInOut look that I wanted to give. How can I apply this animation only to the appearing and disappearing of the sort and delete buttons of the cells of the Form?
If you want to take a look at my problem (since I don't think I was able to explain it correctly) you can look at this video.
The code is this one, ContentView:
import SwiftUI
struct ContentView: View {
#ObservedObject var dm : DataManager
#ObservedObject var vm : ValueModel
#State var showAlertDeleteContact = false
#State var isEditing = false
#State private var editMode = EditMode.inactive
var body: some View {
NavigationView {
Color(UIColor.systemGray6).ignoresSafeArea(.all).overlay(
VStack {
scrollViewFolders
Form {
ForEach(dm.storageValues) { contacts in
NavigationLink(
destination:
//Contact View,
label: {
IconView(dm: dm, vm: contacts)
})
}.onDelete(perform: { indexSet in
self.showAlertDeleteContact = true
self.indexDeleteContact = indexSet
})
.onMove(perform: onMove)
Section {
buttonNewFolder
buttonSort
}
}
}
.navigationBarTitle("Contacts")
.navigationBarItems(trailing: editButton)
.environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive))
.animation(.easeInOut)
//I also tried with this -> .animation(.some(Animation.default))
)
}.alert(isPresented: $showAlertDeleteContact, content: {
alertDeleteContact
})
}
If you want to recreate the project, the DataManager is:
import SwiftUI
import Combine
class DataManager : Equatable, Identifiable, ObservableObject {
static let shared = DataManager()
#Published var storageValues : [ValueModel] = []
typealias StorageValues = [ValueModel]
//The rest of the code
}
And the ValueModel is:
import SwiftUI
import Combine
class ValueModel : Codable, Identifiable, Equatable, ObservableObject, Comparable {
var id = UUID()
var valueName : String
var notes : String?
var expires : Date?
init(valueName: String, notes: String?, expires: Date?) {
self.valueName = valueName
self.notes = notes
self.expires = expires
}
}
Thanks to everyone who will help me!

How to access a variable of an instance of a view in ContentView SwiftUI?

So in ContentView, I've created a view with the following:
ViewName()
I'd like to change a variable in ContentView to the value of a variable in ViewName. I was hoping I could do something like:
ViewName() {
contentViewVariable = self.variableNameInViewNameInstance
}
but that was just kind of a guess as to how to access the value; it didn't work. Any suggestions would be greatly appreciated!
You can use #State and #Binding to achieve that. You should watch these WWDC videos in 2019 to learn more about this.
wwdc 2019 204 - introduction to swiftui
wwdc 2019 216 - swiftui essentials
wwdc 2019 226 - data flow through swiftui
struct ContentView: View {
#State private var variable: String
var body: some View {
ViewName($variable)
}
}
struct ViewName: View {
#Binding var variableInViewName: String
init(variable: Binding<String>) {
_variableInViewName = variable
}
doSomething() {
// updates variableInViewName and also variable in ContentView
self.variableInViewName = newValue
}
}
For whatever reason it is needed technically it is possible to do via callback closure.
Caution: the action in such callback should not lead to refresh sender view, otherwise it would be just either cycle or value lost
Here is a demo of usage & solution. Tested with Xcode 11.4 / iOS 13.4
ViewName { sender in
print("Use value: \(sender.vm.text)")
}
and
struct ViewName: View {
#ObservedObject var vm = ViewNameViewModel()
var callback: ((ViewName) -> Void)? = nil // << declare
var body: some View {
HStack {
TextField("Enter:", text: $vm.text)
}.onReceive(vm.$text) { _ in
if let callback = self.callback {
callback(self) // << demo of usage
}
}
}
}
class ViewNameViewModel: ObservableObject {
#Published var text: String = ""
}

Resources