Hiding Picker's focus border on watchOS in SwiftUI

1.3k views Asked by At

I need to use a Picker view but I don't see any options to hide the green focus border.

Code:

@State private var selectedIndex = 0
var values: [String] = (0 ... 12).map { String($0) }

var body: some View {
    Picker(selection: $selectedIndex, label: Text("")) {
        ForEach(0 ..< values.count) {
            Text(values[$0])
        }
    }
    .labelsHidden()
}

Screenshot of the Picker

4

There are 4 answers

0
Tamás Sengel On BEST ANSWER

The following extension puts a black overlay over the picker border.

Result

Screenshot of the result

Code

extension Picker {
    func focusBorderHidden() -> some View {
        let isWatchOS7: Bool = {
            if #available(watchOS 7, *) {
                return true
            }

            return false
        }()

        let padding: EdgeInsets = {
            if isWatchOS7 {
                return .init(top: 17, leading: 0, bottom: 0, trailing: 0)
            }

            return .init(top: 8.5, leading: 0.5, bottom: 8.5, trailing: 0.5)
        }()

        return self
            .overlay(
                RoundedRectangle(cornerRadius: isWatchOS7 ? 8 : 7)
                    .stroke(Color.black, lineWidth: isWatchOS7 ? 4 : 3.5)
                    .offset(y: isWatchOS7 ? 0 : 8)
                    .padding(padding)
            )
    }
}

Usage

Make sure .focusBorderHidden() is the first modifier.

Picker( [...] ) {
    [...]
}
.focusBorderHidden()
[...]
0
Ray Hunter On

On the Picker, something like this can be added to cover up the green border.

@ScaledMetric var borderWidth: CGFloat = 5 // or it can be 3

Picker {
...
}.border(Color.black, width: borderWidth)
0
slimbikr On

Adding a mask of cornerRadius of whatever required but with a padding of 2 or over (so as to mask the outer edge of the view) as the green border width on the watch tends to be around 2... will do the trick

.mask(RoundedRectangle(cornerRadius: 12).padding(2))

I like the border method from Ray Hunter too but to keep things tidy and simple I rather stay away from having lots and lots of @... variables

0
KM42 On

Instead of using a picker, you can use whatever view you want (with or without a custom border) along with the .digitalCrownRotation() modifier.

See here for instructions: https://www.hackingwithswift.com/quick-start/swiftui/how-to-read-the-digital-crown-on-watchos-using-digitalcrownrotation

Note that it only works on a Double value, but you can make it appear integer using text formatting and by setting appropriate parameters (e.g., the step value).

The main functional difference I've found is that you can't "swipe" to change the value (but I think this is actually a bonus if you need to put it in a ScrollView...).

Lastly, be aware that for getting focus on a tap, it'll only work if they tap on non "clear space" inside your view (not the space between a border and any text/images inside). After some research I found that applying a .contentShape(Rectangle()) to the view acting as a picker fixed this. I think it needs to be before the .digitalCrownRotation() modifier, but I don't remember for sure.