SwiftUI - MacOS - TextField within Alert not receiving focus - macos

I've added a TextField within an Alert in MacOS. However, the TextField doesn't receive focus, when the alert shows. Here's the code I've tested with. Is this even possible? If this is a bug, then please suggest other workarounds.
import SwiftUI
struct ContentView: View {
#State private var presented = false
#State private var username = ""
#FocusState private var focused: Bool
var body: some View {
VStack {
Button(action: {
focused = true
presented = true
}, label: {
Text("Click to show Alert")
})
}
.alert("", isPresented: $presented, actions: {
VStack {
TextField("User name (email address)", text: $username)
.focusable()
.focused($focused)
Button(action: {}, label: { Text("OK") })
}
.onAppear() {
// Try setting focus with a delay.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
focused = true
})
}
}, message: {
Text("The textField in this alert doesn't get the focus. Therefore, a manual click is needed to set focus and start typing.")
})
}
}

I can confirm that focus doesn't work inside Alert. A possible workaround is using a custom Sheet:
struct ContentView: View {
#State private var presented = false
#State private var username = ""
#FocusState private var focused: Bool
var body: some View {
VStack {
Button(action: {
presented = true
focused = true
}, label: {
Text("Click to show Sheet")
})
}
.sheet(isPresented: $presented, content: {
VStack {
// App Icon placeholder
RoundedRectangle(cornerRadius: 8)
.fill(.secondary)
.frame(width: 48, height: 48)
.padding(12)
Text("The textField in this alert doesn't get the focus. Therefore, a manual click is needed to set focus and start typing.")
.font(.caption)
.multilineTextAlignment(.center)
TextField("User name (email address)", text: $username)
.focused($focused)
.padding(.vertical)
Button(action: {
presented = false
}, label: {
Text("OK")
.frame(maxWidth: .infinity)
})
.buttonStyle(.borderedProminent)
}
.padding()
.frame(width: 260)
})
}
}

Related

What is this weird overlay shown on my SettingsPane?

Here's my code, I don't get why this results in this transparent overlay when I move focus to the Text or SecureField components
struct SettingsPane: View {
// MARK: View state
#State private var isSecured: Bool = true
#State private var showApiKeyPopover: Bool = false
// MARK: Preference Storage
#AppStorage("preference_showWorkboard") var showWorkboard = true
#AppStorage("preference_userName") var userName = ""
#AppStorage("preference_jiraApiKey") var jiraApiKey = ""
// MARK: View dimensions
var frameWidth:CGFloat = 200
var body: some View {
Form {
Section(header: Text("Credentials")) {
TextField("User", text: $userName)
.textContentType(.username)
.frame(width: frameWidth)
HStack(alignment: .center) {
Group {
if isSecured {
SecureField("Jira API Key", text: $jiraApiKey)
.textContentType(.password)
.frame(width: frameWidth)
.lineLimit(1)
} else {
TextField("Jira API Key", text: $jiraApiKey)
.textContentType(.password)
.frame(width: frameWidth)
.lineLimit(1)
}
}.popover(isPresented: $showApiKeyPopover) {
Text("To get an API key visit your Jira profile.\nClick 'Personal Access Tokens' and create one named 'Firehose' and add it to this field")
.padding()
}
Button(action: {
isSecured.toggle()
}) {
Image(systemName: self.isSecured ? "eye.slash" : "eye")
.accentColor(.gray)
}
.buttonStyle(.borderless)
Button(action: {
showApiKeyPopover.toggle()
}) {
Image(systemName: "questionmark.circle.fill")
.accentColor(.gray)
}
.buttonStyle(.borderless)
}
Divider()
Section(header: Text("Features")) {
Toggle("Show Workboard", isOn: $showWorkboard)
}
}
}
.padding()
.frame(minWidth: 400, maxWidth: 400)
}
}
It works fine with me (macOS 13.0, Xcode 14.1). I get this ...
Could it be some password utility interfering?

Text gets truncated or hidden on a TextField on a macOS app

I have a TextField on a .sheet that is acting up and I don't know why or how to solve it. When you type a long text, the cursor reaches the end of the TextField (the right end), and it stays there. You can continue to write (and the text will get entered) but you can't see it, nor scroll to it, nor move the cursor.
Is this a default behavior? If not, how do I fix it? What am I not doing right? I haven't found anything either here at SO, or on Apple's forums or various SwiftUI sites.
Here's how it looks
And here's the code for the view:
struct SheetViewNew: View {
#EnvironmentObject private var taskdata: DataModel
#Binding var isVisible: Bool
#Binding var enteredText: String
#Binding var priority: Int
var priorities = [0, 1, 2]
var body: some View {
VStack {
Text("Enter a new task")
.font(.headline)
.multilineTextAlignment(.center)
TextField("New task", text: $enteredText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.system(size: 14.0))
.onSubmit{
if enteredText == "" {
self.isVisible = false
return
}
taskdata.tasks.append(Task(id: UUID(), title: enteredText, priority: priority))
}
Picker("Priority", selection: $priority) {
Text("Today").tag(0)
Text("Important").tag(1)
Text("Normal").tag(2)
}.pickerStyle(RadioGroupPickerStyle())
Spacer()
HStack {
Button("Cancel") {
self.isVisible = false
self.enteredText = ""
}
.keyboardShortcut(.cancelAction)
.buttonStyle(DefaultButtonStyle())
Spacer()
Button("Add Task") {
if enteredText == "" {
self.isVisible = false
return
}
taskdata.tasks.append(Task(id: UUID(), title: enteredText, priority: priority))
taskdata.sortList()
self.isVisible = false
}
.keyboardShortcut(.defaultAction)
.buttonStyle(DefaultButtonStyle())
}
}
.frame(width: 300, height: 200)
.padding()
}
}

macOS: How to prevent that showing an alert dismisses the Popover it comes from

In a macOS App I use .popovers to show and edit cell details. If the user deletes specific content I want to show a warning .alert. But showing the alert always dismisses the Popover it originated from. How can I prevent that?
This should work as Apple is using it e.g. in Calendar, when you delete an attachment in a calendar entry.
Here is a simple demo code showing the issue:
struct ContentView: View {
#State private var showPopover = false
#State private var showAlert = false
var body: some View {
VStack {
Button("Show popover") { showPopover = true }
.popover(isPresented: $showPopover, arrowEdge: .leading) {
VStack {
Text("Popover")
Button("Delete someting") { showAlert = true}
}
.padding()
.frame(minWidth: 100, minHeight: 100)
}
.alert("Really delete?", isPresented: $showAlert) { }
// ^ dismisses the popover immediately
}
.frame(minWidth: 600, minHeight: 400)
}
}

SwiftUI How to add done button to picker

Ive make a mini app with just a button and a picker and the idea is to have a done button above the picker so once ive chosen a value i can press done and the picker will close.
I am aware if you click the "click me" button it will open and if you click it again close the picker but im looking for a button that appears with the picker and disapears with the clicker when clicked.
Almost like a toolbar above the picker with a done button
#State var expand = false
#State var list = ["value1", "value2", "value3"]
#State var index = 0
var body: some View {
VStack {
Button(action: {
self.expand.toggle()
}) {
Text("Click me \(list[index])")
}
if expand {
Picker(selection: $list, label: EmptyView()) {
ForEach(0 ..< list.count) {
Text(self.list[$0]).tag($0)
}
}.labelsHidden()
}
}
The third image is what im trying to accomplish and the first 2 are what ive currently got
Thank you for your help
Add a Button to the if-clause:
if expand {
VStack{
Button(action:{self.expand = false}){
Text("Done")
}
Picker(selection: $list, label: EmptyView()) {
ForEach(0 ..< list.count) {
Text(self.list[$0]).tag($0)
}
}.labelsHidden()
}
}
Here is an approach how I would do this... of course tuning is still possible (animations, rects, etc.), but the direction of idea should be clear
Demo of result:
Code:
struct ContentView: View {
#State var expand = false
#State var list = ["value1", "value2", "value3"]
#State var index = 0
var body: some View {
VStack {
Button(action: {
self.expand.toggle()
}) {
Text("Click me \(list[index])")
}
if expand {
Picker(selection: $index, label: EmptyView()) {
ForEach(0 ..< list.count) {
Text(self.list[$0]).tag($0)
}
}.labelsHidden()
.overlay(
GeometryReader { gp in
VStack {
Button(action: {
self.expand.toggle()
}) {
Text("Done")
.font(.system(size: 42))
.foregroundColor(.red)
.padding(.vertical)
.frame(width: gp.size.width)
}.background(Color.white)
Spacer()
}
.frame(width: gp.size.width, height: gp.size.height - 12)
.border(Color.black, width: 8)
}
)
}
}
}
}
Also this would work (especially if you're doing it outside of a Vstack)...
// Toolbar for "Done"
func createToolbar() {
let toolBar = UIToolbar()
toolBar.sizeToFit()
// "Done" Button for Toolbar on Picker View
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target:
self, action: #selector(PageOneViewController.dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
// Makes Toolbar Work for Text Fields
familyPlansText.inputAccessoryView = toolBar
kidsOptionText.inputAccessoryView = toolBar
ethnicityText.inputAccessoryView = toolBar
}
And be sure to call createToolbar() and you're done!
struct ContentView: View {
var colors = ["Red", "Green", "Blue"]
#State private var selectedColor = 0
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedColor, label: Text("Color")) {
ForEach(0 ..< colors.count) {
Text(self.colors[$0])
}
}
}
}
}
}
}
This has some form specific picker behavior where it opens up inline with no button hiding needed.

Has anyone updated SwiftUI Popovers to work in Xcode beta-3?

Updated to Xcode beta-3, Popover was deprecated... having one hell of a time trying to figure out how to make it work again!?!?
It no longer "pops up" it slides up from the bottom.
It's no longer positioned or sized correctly, takes up the whole screen.
Once dismissed, it never wants to appear again.
This was the old code, that worked perfectly...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
#State private var showPositions = false
var body: some View {
HStack {
Spacer()
Button(action: { self.showPositions = true } ) {
Text("Position")
}
.presentation(showPositions ? Popover(content: MultiPicker(items: Exercise.Position.allCases, selected:$filter.positions),
dismissHandler: { self.showPositions = false })
: nil)
}
.padding()
}
}
And this is the new code...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
#State private var showPositions = false
var body: some View {
HStack {
Spacer()
Button(action: { self.showPositions = true } ) {
Text("Position")
}
.popover(isPresented: $showPositions) {
MultiPicker(items: Exercise.Position.allCases, selected:self.$filter.positions)
.onDisappear { self.showPositions = false }
}
}
.padding()
}
}
I ended up using PresentationLink just so I can move forward with everything else...
struct ExerciseFilterBar : View {
#Binding var filter: Exercise.Filter
var body: some View {
HStack {
Spacer()
PresentationLink(destination: MultiPicker(items: Exercise.Position.allCases, selected:$filter.positions)) {
Text("Position")
}
}
.padding()
}
}
It works, as far as testing is concerned, but it's not a popover.
Thanks for any suggestions!
BTW, this code is being in the iPad simulator.
On OSX the code below works fine
struct ContentView : View {
#State var poSelAbove = false
#State var poSelBelow = false
#State var pick : Int = 1
var body: some View {
let picker = Picker(selection: $pick, label: Text("Pick option"), content:
{
Text("Option 0").tag(0)
Text("Option 1").tag(1)
Text("Option 2").tag(2)
})
let popoverWithButtons =
VStack {
Button("Not Dismiss") {
}
Divider()
Button("Dismiss") {
self.poSelAbove = false
}
}
.padding()
return VStack {
Group {
Button("Show button popover above") {
self.poSelAbove = true
}.popover(isPresented: $poSelAbove, arrowEdge: .bottom) {
popoverWithButtons
}
Divider()
Button("Show picker popover below") {
self.poSelBelow = true
}.popover(isPresented: $poSelBelow, arrowEdge: .top) {
Group {
picker
}
}
}
Divider()
picker
.frame(width: 300, alignment: .center)
Text("Picked option: \(self.pick)")
.font(.subheadline)
}
// comment the line below for iOS
.frame(width: 800, height: 600)
}
On iOS (iPad) the popover will appear in a strange transparent full screen mode. I don't think this is intended. I have added the problem to my existing bug report.

Resources