Is there a way to get/obtain the current font used in a Text object in SwiftUI? ex. Text("abc").font() does not work.
The current font is accessible from the environment:
struct ChildView: View {
#Environment(\.font) var font
var body: some View {
Text("Italic version of the hierarchy's font")
.font((font ?? .body).italic())
}
}
See https://developer.apple.com/documentation/swiftui/environmentvalues for the full list of available keys, they can come in handy.
You can use systemFont.
Text("ddd").font(Font.system(size: 50))
So you don't need to know exact name of the font.
Related
I'm working in SwiftUI and have a GeometryProxy as a property of class used on a view & subview. My code is working fine, but I'd like the subview to render in Preview properly, but to do this, the PreviewProvider needs to have valid GeometryProxy data in a property (geo) of the class I've created (LayoutData). SwiftUI doesn't let me update an object's property as indicated in the comments in the code below. Is there some other way I might be able to get a valid GeometryProxy & use this to update a property in my LayoutData class when using the PreviewProvider? Code otherwise works great & I can run my Live Preview from the parent View, but it would be nice to see things rendered properly on my subview - which I could do if I could pass in my LayoutData object with a valid GeometryProxy property.
Really, all I need from the GeometryProxy is the screen width, so I could use just that in my LayoutData class, but I don't want to use UIScreen.main.width since this will be deprecated & I don't know of another way to get a valid width other than GeometryReader. Here is the code I've attempted, but of course, the comment line trying to update a variable property inside a View, as I've shown, can't be done in SwiftUI.
struct ButtonLayout_Previews: PreviewProvider {
static var previews: some View {
var layoutData = LayoutData()
GeometryReader { previewGeo in
layoutData.geo = previewGeo // You can't do this in SwiftUI
ButtonLayoutView(resultMessage: .constant(""), layoutData: layoutData)
}
}
}
I also know that I can introduce an additional, separate GeometryProxy property for the subview & pass that in as an extra property, using the GeometryReader setup, above, but I don't want to add an extra variable to my code if it's not needed & if I can use the Preview with the GeometryProxy property of my LayoutData class. Thanks for any ideas!
I was able to make the assignment using an empty let statement, and including the assignment after the equal, like below. Any concerns with this? It seems like I'm tricking SwiftUI into accepting an assignment where it normally wouldn't, but this works:
struct ButtonLayout_Previews: PreviewProvider {
static var previews: some View {
let layoutData = LayoutData()
GeometryReader { previewGeo in
let _ = layoutData.geo = previewGeo
ButtonLayoutView(resultMessage: .constant(""), layoutData: layoutData)
}
}
}
Is it possible to declare a View as a var in some way, as in below struct?
struct OptionViews {
var title: String
var imageName: String
var targetView: View
}
I want to use the above struct to present as an array of possible selections (as in a settings view) where if I click on an item a it should open the targetView. However, the above struct is not allowed due to "Protocol View can only be used as a generic constraint", is there a way around this or another way to accomplish this? I need to know which View should be opened when the specific item is selected, but if i cant specify the view as part of the item, that does not seem possible.
Thanks,
Marcus
I guess what #Asperi said in comment is right. Not sure what you exactly want but maybe you could make your struct generic like:
struct OptionViews<Content> where Content: View {
var title: String
var imageName: String
var targetView: Content
}
// example
let optionViews = OptionViews(title: "Titel",
imageName: "Image",
targetView: Image(systemName: "heart"))
What is the correct way to implement a Picker component with specific logic within a Section element?
I would like to have each type displayed in a separate row.
var types = ["Books", "Films", "Music"]
var body: some View {
Form {
Section(header: Text("Type")) {
TextField("Type", text: $newCategoryType)
// Picker
}
}
}
First you must have a #State property that can be updated based on what selection the user makes, say in this case we have this
#State private var selectedType = "Books"
Then you will implement a Picker SwiftUI struct as follows
Picker("Please choose a type", selection: $selectedType) {
ForEach(types, id: \.self) {
Text($0)
}
}
Note that the \.self is really important for ForEach to distinguish between each element inside the list, without which the Picker won't perform the selection action correctly.
The above is enough for doing the job of displaying each option as a row since that is the default behaviour of ForEach
Additionally if you want to customise the look and feel of the picker
you would like to see .pickerStyle() view modifier, for which the docs and examples are mentioned
Also
Tip: Because pickers in forms have this navigation behavior, it’s important you present them in a NavigationView on iOS otherwise you’ll find that tapping them doesn’t work. This might be one you create directly around the form, or you could present the form from another view that itself was wrapped in a NavigationView.
I'm writing a MacOS document app using the SwiftUI App lifecycle, and all the tricks I see here and elsewhere for sending a menu action to the active window depend on using platform specific implementation, which is (mostly) unavailable in a SwiftUI Lifecycle app. What I'm looking for is something like SideBarCommands(), which adds a menu item that, when selected by mouse or command key, toggles the appearance of the sidebar in the active window. All the Command examples I have seen thus far are trivial, none address a multi-document, multi-window use case.
Given a ContentView declared thusly:
struct ContentView: View {
#Binding var document: TickleDocument
var body: some View {
TextEditor(text: $document.text)
}
public func menuTickle() {
document.text = "Wahoo!"
}
}
and a command, which is added via:
struct TickleApp: App {
public static var target:TickleDocument?
var body: some Scene {
let docGroup = DocumentGroup(newDocument: TickleDocument()) { file in
ContentView(document: file.$document)
}
docGroup
.commands {
CommandMenu("App Tickles") {
Button("Tickle The ContentView") {
// Here's where I need to call menuTickle() on the active ContentView
}.keyboardShortcut("t")
}
}
}
}
}
What do I need to do so the button closure can call menuTickle() on the active ContentView? I know it can be done, because SideBarCommands() does it (unless Apple is using some non-public API to do it...).
For bonus points, tell me how I can detect whether or not I'm the active ContentView while body is being evaluated, and how I can detect when it changes! Tracking the Environment variable scenePhase is worthless - it always reports active, and never changes.
My question is a duplicate of this one.
The answer to that question contains a link to a solution that I have verified works, and can be found here
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)