Let’s Get Introspective With SwiftUI

If you migrated from Storyboards to SwiftUI, you probably realized how amazing declarative UI creation is. What you don’t immediately see is that SwiftUI is still in its infancy in some basic use-cases.

What I am mainly referring to is SwiftUI’s lack of exposure of what I consider basic functionality.

Let’s go through the problems and why Introspect is a badass answer who’s functionality should be implemented into SwiftUI.

https://github.com/siteline/SwiftUI-Introspect

Road Block 1 — TextField Focus

Scenario: On entry to your login page you want your app to autofocus on the SecureField/TextField.

The need to control text view focus is a common thing. I know Android has it. Why is it so hard to execute in SwiftUI? *Shrug*

Introspect makes it easy.

TextField(“”, text: $password)
.introspectTextField { (tf) in
tf.becomeFirstResponder()
}

NOTE: .introspectTextField(..) is used for both SecureField and TextField.

Road Block 2 — Show/Hide Password

Scenario: You want to show or hide your password. Seems simple. Seems common. Is it easy in SwiftUI?

Here it is with Introspect:

import SwiftUI
import Introspect
struct ShowHideSecureField: View {
@State var password: String = ""
@State var isShowingPassword: Bool = false
var body: some View {
VStack{
ZStack{
HStack{
if(!isShowingPassword){
SecureField(
"Password",
text: $password) {

}.introspectTextField { (tf) in
tf.becomeFirstResponder()
}
}else{
TextField(
"Password",
text: $password) { (isEditing) in
// is editing code
} onCommit: {
// on commit code
}.introspectTextField { (tf) in
tf.becomeFirstResponder()
}
}
// show only one of these is not empty.
if(!password.isEmpty){
Button {
isShowingPassword.toggle()
} label:{
Image(systemName: isShowingPassword ? "eye.slash" : "eye")
.foregroundColor(.white)
.frame(width: 20, height: 20, alignment: .center)
}
}
}
}
}.padding(10)
.background(Color.gray)
}
}

This example actually has the keyboard “popping” a little bit but I could not find a way around it.

There is another option without Introspect where instead of toggling show/hide, you base it on tapping and holding events.

For this you need the touch/down event monitor by (aheze):

Then you can do:

struct CustomSecureField: View {
@State var password: String = ""
@State var isShowingPassword: Bool = false
var body: some View {
VStack{
ZStack{
HStack{
SecureField(
isShowingPassword ? "" : "Password",
text: $password) {

}.opacity(isShowingPassword ? 0 : 1)
// show only one of these is not empty.
if(!password.isEmpty){
Image(systemName: isShowingPassword ? "eye.slash" : "eye")
.foregroundColor(.white)
.frame(width: 20, height: 20, alignment: .center)
.modifier(TouchDownUpEventModifier(changeState: { (buttonState) in
if buttonState == .pressed {
isShowingPassword = true
} else {
isShowingPassword = false
}
}))
}
}
if(isShowingPassword){
HStack{
Text(password)
.foregroundColor(.white)
Spacer()
}
}
}
}.padding(10)
.background(Color.gray)
}
}

Road Block 3 — Keyboard Done Button

Instead of good old “Return” you want “Done” or “Ok” as the submit button on your keyboard. Wait..we..can’t do that easily?

IN-TRO-FREAKIN-SPECT

struct TextFieldWithDone: View {
@State var text: String = ""
var body: some View {
TextField(
"Password",
text: $text) { (isEditing) in
// is editing code
} onCommit: {
// on commit code
}.introspectTextField { (tf) in
tf.returnKeyType = .done
}
}
}

If you have been around a while you probably know of Introspect. If you are new I highly recommend it after spending hours trying to find just the right solution. I hope the SwiftUI team takes to heart why this library is needed for such fundamental use cases.

🍻 Kanpai 🍻

Interested in all things Mobile Development! Continuous learner and avid LitRPG reader.