I would like to know if it's possible to load a SwiftUI view into an NSView.
I was hoping to set the windows content view to aSwiftUI but it seems impossible
window?.contentView = view where view is of swiftUI
If we talk about AppKit which NSView is part of. There is a way to convert SwiftUI view to NSView using NSHostingView like this:
NSHostingView(rootView: yourSwiftUIView)
So, you can write:
window?.contentView = NSHostingView(rootView: view)
If you need the same for UIKit - use UIHostingController in pretty much similar but slightly more complex way:
UIHostingController(rootView: view).view
Related
I want to control low memory state in my SwiftUI app.
What is the alternative of applicationDidReceiveMemoryWarning in UIKit for SwiftUI when you choose SwiftUI lifecycle?
I found didReceiveMemoryWarningNotification for Notification Center, but it is also implemented on UIKit, not on Foundation.
Or if there is no alternative, should I use UIKit lifecycle?
Thanks
You can use a Combine publisher which detects UIApplication.didReceiveMemoryWarningNotification notifications.
Example:
struct ContentView: View {
private let memoryWarningPublisher = NotificationCenter.default.publisher(for: UIApplication.didReceiveMemoryWarningNotification)
var body: some View {
Text("Hello world!")
.onReceive(memoryWarningPublisher) { _ in
print("memory warning")
}
}
}
Result:
I’m having trouble using an arrow key as a .keyboardShortcut in SwiftUI. Sample iOS app:
struct ContentView: View {
#State var time: Date = Date()
var body: some View {
VStack {
Button("Press Me") {
time = Date()
}
.keyboardShortcut(KeyEquivalent.rightArrow, modifiers: [])
Text("\(time)")
}
}
}
This puts up a button that, when pressed, changes the time displayed in the text. I should be able to use the right arrow key on the keyboard and get it to work as well, but it doesn’t. If I change the keyboardShortcut line to, say, this:
.keyboardShortcut(KeyEquivalent(“a”), modifiers: [])
everything works as expected. You can press the “a” key and the time changes. If you hold down the command key, you get the system-provided HUD that shows the “a" shortcut. Change it to .rightAarrow and it shows the HUD but there’s an enclosed “?” for the shortcut, and the shortcut doesn’t fire when the arrow key is pressed.
(I’m aware I could do this using UIKit. Trying to understand why the SwiftUI version doesn’t work.)
I am attempting to accomplish the same objective in my MacOS SwiftUI app. Using your code as an example, I inserted the .keyboardShortcut(KeyEquivalent.rightArrow, modifiers: []) after my Button{} and it works fine. I then pasted your entire code into my ContentView and again it works fine. I do not know why it works in my MacOS app but not in your iOS app.
Copying my answer from this post. I wasn't able to use SwiftUI's commands to get this to work on iOS/iPadOS. However I found some luck using view controllers, and if you're using SwiftUI views then this will work with a hosting controller.
In your view controller, add the code below. The important bit is setting wantsPriorityOverSystemBehavior to true:
override var keyCommands: [UIKeyCommand]? {
let upArrow = UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(test))
upArrow.wantsPriorityOverSystemBehavior = true
return [upArrow]
}
#objc func test(_ sender: UIKeyCommand) {
print(">>> test was pressed")
}
This is my first post to this forum and really hope that somebody can help me here. I would highly appreciate any help!
I am writing my first app with Swift UI (never used UIKit before) which I want to publish later on.
This is also my first app which has CoreData implemented.
For example I use the following entities:
Family, Person
1 Person can have 1 Family
1 Family can have many Persons
My app is structured as follows:
ContentView:
Contains a TabView with 2 other views in it. A Settings View and a View with a LazyVGrid.
LazyVGrid View:
This View shows a GridItem for every Family. I get the Families with the following Fetchrequest:
#Environment(\.managedObjectContext) private var viewContext
// These are the Families from the FetchRequest
#FetchRequest(entity: Family.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Family.created, ascending: false)]
) var families: FetchedResults<Family>
Every GridItem is linking to a "FamilyDetailView" via NavigationLink. So i pass the family as the following:
NavigationLink(destination: FamilyDetailView(family: family).environment(\.managedObjectContext, self.viewContext), label: {Text("Family Name")
In the FamilyDetailView I get the Family with a property wrapper:
#State var family : Family
In this FamilyDetailView is the problem i have.
Here I also have a LazyVGrid, which shows 1 NavigationLink for every Person in the Family in a GridItem . In this GridItem I also show for example the "name" of the Person.
When tapping the NavigationLink i get to the last View, the PersonDetailView. This View gets the Person which is also an entity which has a relationship to the Family Entity.
I pass it as the follow:
NavigationLink(
destination: PersonDetailView(person: person),
label: {Text("Person")})
In the PersonDetailView I now change the name of the person and save the changed to CoreData.
The change is saved without a problem, the problem is that when I go back, using the topleading back button from the NavigationView, the Views are not updated. I have to restart the App to see the changes..
I know that the Problem has to be with passing the Data, but I cant figuring out what I did wrong.
I really appreciate everyone trying to help me!
Thank you very very much!!
CoreData objects are reference types, i.e. classes, conformed to ObservableObject protocol, so instead of state wrap corresponding property with ObservedObject, like
#ObservedObject var family : Family
Thank you very much, this helped me a lot. The FamilyDetail View is updating with that fix. Just one thing does not update. I have a "FamilyRowView" which is display for every GridItem in my Family LazyVGrid. This now also gets uses "#ObservedObject var family : Family" but still doent update. Any ideas for that? Thank you so much!
FamilyListView:
NavigationLink(destination: FamilyDetailView(family: family)
.environment(\.managedObjectContext, self.viewContext),
label: {**FamilyRowView**(family: family)
.environment(\.managedObjectContext, self.viewContext)})
FamilyRowView:
struct FamilyRowView: View {
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var family : Family
var body: some View {
It seems like to be the same as the other views that are working not but it doesnt update the View..
In a SwiftUI app, I need to set the focus on a TextField and bring the keyboard automatically, in standard Swift this would be done with:
field.becomeFirstResponder()
But this does not seem to exist in SwiftUI.
I found a work around here.
But, my field uses :onCommit; which is not in the sample code.
What is the way to set the :onCommit functionality when using UIViewRepresentable ?
iOS 15+ has a solution for this.
#FocusState combined with the focused(_:) modifier can be used to control first responder status for textfields.
struct ExampleView: View {
#FocusState private var isFocused: Bool
#State private var textInput = ""
var body: some View {
TextField("Example", text: $textInput)
.focused($isFocused)
Button("Confirm") {
if textInput {
isFocused = true
}
}
}
}
For iOS15
There is a solution implemented by apple (as mentioned by #AlphaWulf)
For iOS14
In my opinion, the best approach is to implement your own version of TextField using the UIRepresentable protocol. This might sound like something difficult but it is actually quite simple.
Why it is better to implement your own text field over the solutions using view hierarchy introspection?
One is that a solution based on traversing underlying views is hacky by nature and even a minor iOS version update might break it.
Secondly, in a real-world app, you will want to set additional things on the text field (like return button type and supplementary view) but Apple didn't make a way of doing so and you will be forced to wrap a UITextField in any case.
https://blog.agitek.io/swiftui-2-first-responder-b6a828243268
In this post I have a detailed solution that is similar to what Apple has implemented in SwiftUI 3.
There is an open-source project for your needs, at https://github.com/mobilinked/MbSwiftUIFirstResponder
TextField("Name", text: $name)
.firstResponder(id: FirstResponders.name, firstResponder: $firstResponder, resignableUserOperations: .all)
TextEditor(text: $notes)
.firstResponder(id: FirstResponders.notes, firstResponder: $firstResponder, resignableUserOperations: .all)
I'm embedding a view controller with variable-height UITextView inside a parent SwiftUI VStack and the view controller sizes it's frame to the whole screen between viewDidLoad and viewDidLayoutSubviews. The UITextView expands only to the size of the text inside itself and centers itself inside the parent view.
I'm trying to add this view controller in a VStack and have it behave externally like other SwiftUI components do - sized exactly to the content it contains - but it wants to be sized to the whole screen minus the other VStack elements.
I can get the correct size of the UITextView in didLayoutSubviews and pass it upwards to SwiftUI where it can be set properly - but where do I do that?
In the example screenshot below, the orange is the embedded UIView background, the green is the UITextView and the VStack looks like this:
VStack {
HighligherVC()
Text("Tap and drag to highlight")
.foregroundColor(.gray)
.font(.caption)
}
Without being able to see more of your code, it's slightly difficult to say what the best solution would be, but based purely on this part of your question...
I can get the correct size of the UITextView in didLayoutSubviews and pass it upwards to SwiftUI where it can be set properly - but where do I do that?
I would suggest that you pass a binding property to your view controller that can be set to the calculated text view height, meaning that the view that contains your VStack would have a #State property like this:
#State private var textViewHeight: CGFloat = 0
You would then declare a #Binding property on your HighlighterVC and add an initializer like this:
#Binding var textViewHeight: CGFloat
init(textViewHeight: Binding<CGFloat>) {
self._textViewHeight = textViewHeight
}
And then you would set textViewHeight to the calculated height in your didLayoutSubviews and add a .frame modifier to your HighlighterVC like this:
VStack {
HighlighterVC(textViewHeight: self.$textViewHeight)
.frame(height: self.textViewHeight)
Text("Tap and drag to highlight")
.foregroundColor(.gray)
.font(.caption)
}
Like I said at the beginning of my answer, this solution (that I believe would work, but since I can't test it, I'm not 100% certain) is based on your thoughts about what it is that you need. Without seeing more code, it's impossible for me to say if this is the best solution.
Add fixedSize may solve this.
.fixedSize(horizontal: false, vertical: true)