SwiftUI: Set a Max Width on Spacer()

Published under Mango Snippets, Oct 5, 2020

SwiftUI's Spacer() has a minLength parameter, but some times we also want a maxLength. There isn't one, but you can use .frame(maxWidth:) to achieve the same effect:

Spacer()
    .frame(maxWidth: 16)

SwiftUI: How To Programmatically Make a TextField First Responder

Published under Mango Snippets, Oct 5, 2020

It's a common scenario to make a text field first responder when it first appears. A typical example is the login screen. As soon as it appears, you want the account field to be in focus and the keyboard to appear. Then the user can start typing right way.

SwiftUI in iOS 15 introduced a new property wrapper called @FocusState. It allows you to control which input has focus. It can be bound to a Bool or an enum:

struct ContentView: View {
    @FocusState private var focused: Bool
    @State private var name = "Mango Umbrella"

    var body: some View {
        VStack {
            TextField("Name", text: self.$name)
                .focused(self.$focused)
            Button("Focus on name") {
                self.focused = true
            }
        }
    }

But how can you make the text field focus as soon as the view appears? You can't assign an initial value to the property. Another thought is to change its value in .onAppear like this:

struct ContentView: View {
    @FocusState private var focused: Bool

    var body: some View {
        VStack {
            // ...
        }
        .onAppear {
            self.focused = true
        }
    }
}

This won't work unfortunately. It appears that @FocusState only works when the view has been rendered after some time. A not-so-good workaround is to add a delay:

struct ContentView: View {
    @FocusState private var focused: Bool

    var body: some View {
        VStack {
            // ...
        }
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                self.focused = true
            }
        }
    }
}

I don't recommend this approach though. The delay is arbitrary, and slower devices might need a longer delay. It isn't a great user experience anyway.

For this task, it's best falling back to UIKit's becomeFirstResponder(). It's easy to wrap a UITextField in a UIViewRepresentable like this:

struct MyTextField: UIViewRepresentable {
    typealias UIViewType = UITextField

    @Binding var becomeFirstResponder: Bool

    func makeUIView(context: Context) -> UITextField {
        return UITextField()
    }
    
    func updateUIView(_ textField: UITextField, context: Context) {
        if self.becomeFirstResponder {
            DispatchQueue.main.async {
                textField.becomeFirstResponder()
                self.becomeFirstResponder = false
            }
        }
    }
}

Notice the async call. It is necessary because it's modifying the state and it isn't allowed in updateUIView. If you forget, Xcode will warn you Modifying state during view update, this will cause undefined behavior.

To make it first responder when the view appears, just call it in .onAppear. And it works without a delay:

struct ContentView: View {
    @State private var becomeFirstResponder = false

    var body: some View {
        MyTextField(becomeFirstResponder: self.$becomeFirstResponder)
            .onAppear {
                self.becomeFirstResponder = true
            }
    }
}

TIP: If you use SwiftUI Instrospect, you can directly inspect the backing UITextField without your own wrapper:

struct ContentView: View {
    @State private var name = "Mango Umbrella"
    @State private var becomeFirstResponder = true

    var body: some View {
        TextField("Name", text: self.$name)
            .introspectTextField { textField in
                if self.becomeFirstResponder {
                    textField.becomeFirstResponder()
                    self.becomeFirstResponder = false
                }
            }
        }
    }
}

∞ Apple Publishes New Webpages Explaining the Benefits of the App Store and the Company’s Developer Program

Published under Mango Linked, Sep 24, 2020

The more PR effort Apple puts into this, the worse their image is. Wake up.

SwiftUI: How To Draw Borders With Rounded Rectangles

Published under Mango Snippets, Jul 23, 2020

In SwiftUI, it’s easy to find out there is a .border modifier, and a .cornerRadius modifier. However, those tools can’t create borders with rounded corners. Instead, you can use the .overlay modifier with a RoundedRectangle shape:

Text("DA Tips")
    .padding()
    .overlay(
        RoundedRectangle(cornerRadius: 16)
            .stroke(Color.pink, lineWidth: 2)
    )

Swift: How To Count Occurrences of a Character in a Swift String

Published under Mango Snippets, Jul 21, 2020

To count occurrences of a character in a Swift String, you can use Sequence.filter and then Array.count:

"Mango Snippets".filter({ $0 == "n"}).count

Output:

2

DA CamCal is Now FREE

Published under Mango Exposure, May 3, 2020

To give more people the choice, DA CamCal is now FREE with ads by default. You can still pay a one time fee to remove them. Don't worry if you have previously purchased the app (thank you!), you won't see ads.

Happy photographing no matter where you are.

-- Dolee ^_^

∞ GitHub is Now Free for Teams

Published under Mango Linked, Apr 14, 2020

We’re happy to announce we’re making private repositories with unlimited collaborators available to all GitHub accounts. All of the core GitHub features are now free for everyone. 🎉

Microsoft has risen again.

∞ The Half of It Official Trailer

Published under Mango Linked, Apr 10, 2020

Official trailer for The Half of It, from writer/director Alice Wu:

Shy, straight-A student Ellie is hired by sweet but inarticulate jock Paul, who needs help winning over a popular girl. But their new and unlikely friendship gets complicated when Ellie discovers she has feelings for the same girl.

😍 I can't wait for this.

∞ Othering the Virus

Published under Mango Linked, Apr 9, 2020

In the West, perception of the virus as a threat came only very late. Terrifying news from China was available since late January: High death rates, permanent damage from the disease, people dying in their homes or in the street in front of overloaded hospitals, entire families dying. But far into February, western observers did not see an urgent need to act.

This is a good read, from Marius Meinhof, a sociologist at the University of Bielefeld in Germany.

Published under Mango Shorts, Mar 26, 2020

Most of the “Face with Medical Mask” emojis should be redesigned. The face should be completely neutral, even just closing eyes is not neutral.

« Previous page Page 13 of 53 Next page »