How to Change the Appearance of the Datepicker in the Swiftui Framework to Only Months and Years

How do I change the appearance of the DatePicker in the SwiftUI framework to only months and years?

As others have already commented You would need to implement an HStack with two Pickers:

struct ContentView: View {

@State var monthIndex: Int = 0
@State var yearIndex: Int = 0

let monthSymbols = Calendar.current.monthSymbols
let years = Array(Date().year..<Date().year+10)

var body: some View {
GeometryReader{ geometry in
HStack(spacing: 0) {
Picker(selection: self.$monthIndex.onChange(self.monthChanged), label: Text("")) {
ForEach(0..<self.monthSymbols.count) { index in
Text(self.monthSymbols[index])
}
}.frame(maxWidth: geometry.size.width / 2).clipped()
Picker(selection: self.$yearIndex.onChange(self.yearChanged), label: Text("")) {
ForEach(0..<self.years.count) { index in
Text(String(self.years[index]))
}
}.frame(maxWidth: geometry.size.width / 2).clipped()
}
}
}
func monthChanged(_ index: Int) {
print("\(years[yearIndex]), \(index+1)")
print("Month: \(monthSymbols[index])")
}
func yearChanged(_ index: Int) {
print("\(years[index]), \(monthIndex+1)")
print("Month: \(monthSymbols[monthIndex])")
}
}

You would need this helper from this post to monitor the Picker changes

extension Binding {
func onChange(_ completion: @escaping (Value) -> Void) -> Binding<Value> {
.init(get:{ self.wrappedValue }, set:{ self.wrappedValue = $0; completion($0) })
}
}

And this calendar helper

extension Date {
var year: Int { Calendar.current.component(.year, from: self) }
}

How do I create a DatePicker in swiftUI that only select years and I want this to be in prespecified range of years?

You can create your custom year picker by using a simple Picker and passing it an array of Int as your years.

struct YearPicker: View {
private let numberFormatter: NumberFormatter = {
let nf = NumberFormatter()
nf.usesGroupingSeparator = false
return nf
}()

@State private var selectedYearIndex: Int
private let years: [Int]

init(start: Int, end: Int) {
let years = Array(start...end)
self._selectedYearIndex = State(initialValue: years.startIndex)
self.years = years
}

func yearString(at index: Int) -> String {
let selectedYear = years[index]
return numberFormatter.string(for: selectedYear) ?? selectedYear.description
}

var body: some View {
VStack {
Text("Your birth year is \(yearString(at: selectedYearIndex))")
Picker("Year", selection: $selectedYearIndex) {
ForEach(years.indices) { yearIndex in
Text("\(self.yearString(at: yearIndex))")
}
}
}
}
}

struct YearPicker_Previews: PreviewProvider {
static var previews: some View {
YearPicker(start: 2000, end: 2020)
}
}

YearPicker

UIDatePicker select Month and Year

Yeah, you probably want to just make your own picker. You don't have to subclass it or anything, though; just use a generic UIPickerView and return appropriate values from your UIPickerViewDelegate/UIPickerViewDataSource methods.

Fetching and setting date from datepicker, but still getting old default value

Reply against your second question about wrapper properties used in SwiftUI i.e @State, @Binding, @Published.

The most common @Things used in SwiftUI are:

•   @State - Binding<Value>
• @Binding - Binding<Value>
• @ObservedObject - Binding<Value> (*)
• @EnvironmentObject - Binding<Value> (*)
• @Published - Publisher<Value, Never>

(*) technically, we get an intermediary value of type Wrapper, which turns a Binding once we specify the keyPath to the actual value inside the object.
So, as you can see, the majority of the property wrappers in SwiftUI, namely responsible for the view’s state, are being “projected” as Binding, which is used for passing the state between the views.
The only wrapper that diverges from the common course is @Published, but:
1. It’s declared in Combine framework, not in SwiftUI
2. It serves a different purpose: making the value observable
3. It is never used for a view’s variable declaration, only inside ObservableObject
Consider this pretty common scenario in SwiftUI, where we declare an ObservableObject and use it with @ObservedObject attribute in a view:

class ViewModel: ObservableObject {
@Published var value: Int = 0
}

struct MyView: View {
@ObservedObject var viewModel = ViewModel()

var body: some View { ... }
}

MyView can refer to $viewModel.value and viewModel.$value - both expressions are correct. Quite confusing, isn’t it?
These two expressions ultimately represent values of different types: Binding and Publisher, respectively.
Both have a practical use:

var body: some View {
OtherView(binding: $viewModel.value) // Binding
.onReceive(viewModel.$value) { value // Publisher
// do something that does not
// require the view update
}
}

Hope it may help you.

Unable to adjust the value of datepicker for UI testing: PickerWheel of type 6

There still doesn't seem to be a supported method of changing the new date picker wheels as of Xcode 12.1.

I've come up with this method to get around the issue:

func adjustDatePicker(wheel: XCUIElement, to newValue: String) -> Bool {    
let x = wheel.frame.width / 2.0
let y = wheel.frame.height / 2.0
// each wheel notch is about 30px high, so tapping y - 30 rotates up. y + 30 rotates down.
var offset: CGFloat = -30.0
var reversed = false
let previousValue = wheel.value as? String
while wheel.value as? String != newValue {
wheel.coordinate(withNormalizedOffset: .zero).withOffset(CGVector(dx: x, dy: y + offset)).tap()
let briefWait = expectation(description: "Wait for wheel to rotate")
briefWait.isInverted = true
wait(for: [briefWait], timeout: 0.25)
if previousValue == wheel.value as? String {
if reversed {
// we already tried reversing, can't find the desired value
break
}
// we didn't move the wheel. try reversing direction
offset = 30.0
reversed = true
}
}

return wheel.value as? String == newValue
}

SwiftUI Picker onChange or equivalent?

Deployment target of iOS 14 or newer

Apple has provided a built in onChange extension to View, which can be used like this:

struct MyPicker: View {
@State private var favoriteColor = 0

var body: some View {
Picker(selection: $favoriteColor, label: Text("Color")) {
Text("Red").tag(0)
Text("Green").tag(1)
}
.onChange(of: favoriteColor) { tag in print("Color tag: \(tag)") }
}
}


Deployment target of iOS 13 or older

struct MyPicker: View {
@State private var favoriteColor = 0

var body: some View {
Picker(selection: $favoriteColor.onChange(colorChange), label: Text("Color")) {
Text("Red").tag(0)
Text("Green").tag(1)
}
}

func colorChange(_ tag: Int) {
print("Color tag: \(tag)")
}
}

Using this helper

extension Binding {
func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
return Binding(
get: { self.wrappedValue },
set: { selection in
self.wrappedValue = selection
handler(selection)
})
}
}


Related Topics



Leave a reply



Submit