How can I delay the obscuring/masking of characters in SwiftUI's SecureField so that each typed character shows for ~1sec before it's hidden? [closed] - swiftui

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
When using SwiftUI's SecureField the user's input it obscured / masked / turned into • immediately, which makes it difficult to confirm that one has actually typed the password correctly. Several of our users have complained about this, and it seems that most people expect to see the character they typed for about a second or so before it disappears, which gives them visual confirmation that they tapped the correct letter and can now move on w/o worrying.
Is it possible to add a delay in there and show the last typed character for a moment?

I don't think this is possible in SwiftUI at the moment, but here's the hack we've been using in the meantime. It stacks a SecureField on top of a TextField and toggles their opacities when the user clicks on the eye (to show the text). Granted, it shows the entire String and not the last letter, but it works.
I'm sure there are open source libraries of ways to do this in UIKit that you could convert, but I'll let someone else answer that.
struct SecureTextFieldView: View {
#State var text: String = ""
#State var showText: Bool = false
var body: some View {
ZStack {
TextField("Password", text: $text)
.opacity(showText ? 1.0 : 0.0)
SecureField("Password", text: $text)
.opacity(showText ? 0.0 : 1.0)
}
.padding()
.background(
Color(UIColor.secondarySystemBackground).cornerRadius(10)
)
.overlay(
Image(systemName: "eye.fill")
.foregroundColor(showText ? .black : .gray)
.padding(.trailing)
.onLongPressGesture(minimumDuration: 20, maximumDistance: 30, pressing: { (_) in
showText.toggle()
}, perform: {})
, alignment: .trailing
)
.padding()
}
}
struct SecureTextFieldView_Previews: PreviewProvider {
static var previews: some View {
SecureTextFieldView()
}
}

Related

Why is the SwiftUI TextEditor glitching upon font size changes?

I'm trying to create a TextEditor view where the user can customize the font size, but whenever I adjust the font size using a Slider and go back to typing in the TextEditor, the TextEditor starts glitching: the scroll position jumps up and down, the cursor ends up in the wrong position, and the text flickers.
Here's a reduced test case:
struct ContentView: View {
#State var text: String
#State var textSize: CGFloat
var body: some View {
VStack {
Slider(value: $textSize, in: 20.0...80.0)
TextEditor(text: $text)
.font(.system(size: textSize, weight: .semibold))
}
.padding()
}
}
Just type some text, adjust the font size, type more text. You might need to repeat that a few times, but it eventually starts glitching.
My hypothesis is that part of the UI isn't being updated on the main thread, but maybe that's just me coming from UIKit and not fully understanding SwiftUI yet.
Using Xcode 14.2 and iOS 16.2.

SwiftUI: Create a custom view that behaves like a SwiftUI List, taking multiple views in a closure and arranging them [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 months ago.
Improve this question
I am currently trying to create a custom view in SwiftUI that basically behaves like a SwiftUI List or Form. What I want is to achieve is something like the following:
CustomView {
Text("TBD")
Text("TBD")
Image(systemName: "heart")
}
The output of this custom view should then be a vertical list of the views that were passed in, plus some additional elements, like dividers. Something like what you would get from the following code:
VStack(alignment: .leading) {
Text("TBD")
Divider()
Text("TBD")
Divider()
Image(systemName: "heart")
}
The problem is that I have no idea how to create a generic view that would take any number of other views and then arrange them in such a way - Im not even sure if it is possible.
If there is anyone around that has experience with creating something like this, I would appreciate any hints or explanations.
Thanks!
Here is an example that works for 3 views:
struct CustomView: View {
let children: [AnyView]
init<C1: View, C2: View, C3: View>(#ViewBuilder content: () -> TupleView<(C1, C2, C3)>) {
self.children = [AnyView(content().value.0),
AnyView(content().value.1),
AnyView(content().value.2)]
}
var body: some View {
VStack(alignment: .leading) {
children[0]
Divider()
children[1]
Divider()
children[2]
}
}
}
I believe you need an init for each number of Views you want to support. Read more about this here:
https://forums.swift.org/t/swiftui-viewbuilder-result-is-a-tupleview-how-is-apple-using-it-and-able-to-avoid-turning-things-into-anyview/28181/14

SwiftUI controls don't respond to input (clicks) in Catalyst if revealed in ScrollView

I'm dealing with what I am nearly certain to be a SwiftUI/Catalyst bug and am looking for a solution to get around it.
In the following code, about 30% of the time (5/15 in my tests), once the controls are revealed, the Toggle elements do not respond to clicks (and thus do not turn on/off).
I'm testing on Xcode 12.3 on Big Sur 11.1, running this code in Catalyst. It does work as expected 100% of the time as far as I can tell on iOS 14.3.
struct ContentView: View {
var body: some View {
ScrollView {
Row()
Row()
}
.padding()
}
}
struct Row : View {
#State private var showControls = false
#State private var toggleOn = false
var body: some View {
VStack {
HStack {
Text("Top section")
Button("\(showControls ? "Hide" : "Show") controls") {
showControls.toggle()
}
}
.frame(height: 100)
if showControls {
HStack {
Toggle("Toggle", isOn: $toggleOn)
Toggle("Toggle", isOn: $toggleOn)
}
}
}
}
}
The problem seems to come from having the Rows embedded in the ScrollView. The problem disappears completely if the controls start in their visible state (ie showControls = true), and only happens when they get revealed (ie showControls.toggle()) after the app starts.
I've also noticed that while Toggle and Slider fail about 30% of the time, a plain Button seems to be responsive 100% of the time.
The view debugger doesn't show anything 'in front' of the views that would be intercepting clicks.
I've tried changing to a List, which solves the problem, but yields other unfortunate side effects in behavior that I'd like to avoid in my real non-trivial app.
Can anyone else think of a reliable solution to avoiding this?

How do I prevent SwiftUI's TabView from moving vertically when dragging? [duplicate]

I have set up a TabView in my application, so that I can swipe horizontally between multiple pages, but I also have an unwanted vertical scroll that may appear, with a bounce effect so. How can I disable this vertical scroll?
My code:
struct ContentView: View {
#State private var currentTabIndex: Double = 0
var body: some View {
VStack {
TabView(selection: $currentTabIndex) {
Text("Text n°1")
.tag(0)
Text("Text n°2")
.tag(1)
}
.border(Color.black)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
I had this same problem. It's not an exact solution, but you can turn off bouncing on scrollviews (which is used within a TabView). And as long as the items within the TabView are not larger than the TabView frame, it should act as if you disabled vertical scrolling.
I would call it either .onAppear or in your init function:
.onAppear(perform: {
UIScrollView.appearance().bounces = false
})
Note: this disables the bouncing on ALL scrollviews across your app... So you may want to re-enable it .onDisappear.
Still an issue with Xcode 12.4.
I managed to workaround that by wrapping the TabView within a ScrollView and using the alwaysBounceVertical property set to false, as follow:
ScrollView(.horizontal) {
TabView {
///your content goes here
}
.tabViewStyle(PageTabViewStyle())
}
.onAppear(perform: {
UIScrollView.appearance().alwaysBounceVertical = false
})
.onDisappear(perform: {
UIScrollView.appearance().alwaysBounceVertical = true
})
I actually came across this because I saw this effect in a tutorial but couldn’t replicate it on iOS 15.2. However, I managed to replicate it on iOS 14.4 on another simulator side by side. So I guess this behaviour is disabled or fundamentally changed in the newer iOS.
Demonstration

List not scrolling (in SwiftUI -- to disambiguate from other questions)

Using SwiftUI, I have a List of items retrieved from CoreData. Everything I read makes it look like the list should be something I can scroll normally, but in both the simulator and running on an iPad, the List doesn't scroll.
Clearly there is some piece missing, but I can't seem to find what it is. The list populates correctly, but it simply won't scroll.
struct PeopleList : View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(
entity: Person.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Person.lastName, ascending: true),
NSSortDescriptor(keyPath: \Person.firstName, ascending: true)
]
) var people: FetchedResults<Person>
var body: some View {
ZStack(alignment: .topLeading) {
List(self.people, id: \.self) { person in
Text(person.descriptionForList())
}
}
.frame(width: 400, height: 200)
.modifier(RoundedEdge(width: 4, color: Color(red: 0.6, green: 0.6, blue: 0.6), cornerRadius: 10))
.padding(10)
}
}
Things that didn't change anything:
Removing the ZStack { }
Replacing the List with a simple List(0...100, id: \.self) { item in Text("hey \(item)") } -- still won't scroll.
Adding a .frame() to the List() itself.
(added) removing the .frame .modifier and .padding all accomplished nothing.
This is not about (the following topics have Q&A's addressing them, but not this issue):
programmatic scrolling
disabling scrolling
scroll indicators
ScrollView
UIKit
How can I track down what might be preventing the List() from scrolling?
Turns out, the issue wasn't with anything in the struct that was posed above; it all had to do with the View where that struct was being used. Specifically, when a Color() -- even of .opacity(0) -- was in a ZStack sitting "above" the List(), the latter stops scrolling. Displays fine, but just won't scroll.
This question describes the same thing happening, albeit in somewhat different circumstances.
I'll leave this question up, since somebody else may be at the same place I was, "why isn't my List() scrolling?", rather than "why isn't my List() in a ZStack scrolling?" Hopefully a version of Swift later than 13 will fix this behavior!