How to change Selection colour(AM,PM) and time of GraphicalDatePickerStyle Date picker Swiftui - swift

How to Change GraphicalDatePickerStyle Date picker text color and Selection Colour:-
Code:-
DatePicker("Time",selection: dateProxy,displayedComponents: [.hourAndMinute])
.datePickerStyle(GraphicalDatePickerStyle())
.accentColor(.white)
.labelsHidden()
Output:-
But I want to achieve DatePicker given below, is it possible in GraphicalDatePickerStyle Style or not?
Try to Achieve:-

Seems like you are looking for the dark mode picker! Just apply the dark colorScheme to the component:
DatePicker("Time", selection: $date, displayedComponents: [.hourAndMinute])
.datePickerStyle(GraphicalDatePickerStyle())
.background(Color.black)
.environment(\.colorScheme, .dark) // <- This modifier
Result:
🌈 Apply color
Also, you can apply the colorMultiply modifier for coloring the picker in a pleasant way:
Demo Full Code
struct ContentView: View {
#State private var date = Date()
var colors: [Color] = [.white, .gray, .red, .green, .blue, .orange, .yellow, .pink, .purple] // <- You can use any color! This is just for demonstrate
var body: some View {
VStack {
ForEach(colors, id:\.self) { color in
DatePicker("Time", selection: $date, displayedComponents: [.hourAndMinute])
.datePickerStyle(GraphicalDatePickerStyle())
.background(Color.black.grayscale(1))
.colorMultiply(color) // <- This line does the trick
.environment(\.colorScheme, .dark) // <- Don't Forget this
}
}
}
}

This works for macOS 11 and iOS 14 (Xcode 12 beta 3)...
1.Take the Label out of the DatePicker - I've left it commented out here so its obvious to readers that the Label should be taken out of the DatePicker View.
Place the DatePicker in an HStack (or other ViewBuilder.
Add a Label for the DatePicker in the HStack.
Use the .colorMultiplier view modifier to change the color of the DatePicker date and time "button".
In this particular example I am also changing the colour of the date and time "button" depending on whether the binding value in the date picker is nil.
let now = Date()
HStack {
Text("Date")
DatePicker(selection: Binding($observedObject.date, replacingNilWith: now)) {
// Text("Date")
// .foregroundColor(Color(.label))
}
.colorMultiply(observedObject.date == nil ? Color(.placeholderText) : Color(.label))
}
Also I'm using an extension to Binding in this example which is something I use throughout my core data projects.
public extension Binding where Value: Equatable {
init(_ source: Binding<Value?>, replacingNilWith nilProxy: Value) {
self.init(
get: { source.wrappedValue ?? nilProxy },
set: { newValue in
if newValue == nilProxy {
source.wrappedValue = nil
}
else {
source.wrappedValue = newValue
}
})
}
}
As always full credit to Alan Quatermain for this extension to Binding.

Closest I got:
Group {
DatePicker("Time",selection: $dateProxy, displayedComponents: [.hourAndMinute])
.datePickerStyle(GraphicalDatePickerStyle())
.accentColor(.black)
.colorInvert()
.labelsHidden()
.padding()
}
.background(Color.black.opacity(0.8))

Related

Contracting an expanded DatePicker on SwiftUI / MacOS

Is there a way to get a DatePicker that has been expanded to contract when the user clicks on a date?
The following code will crash the app - or at least put it into the locked state.
There is a Text component showing the date. When you click on it the DatePicker appears in its place - but there is no way to dismiss it after it is an expanded mode. Trying to swap back to the original Text, by setting edit mode to false when the date changes effectively locks the app:
struct ContentView: View {
#State private var date: Date = .now
#State private var editMode: Bool = false
var body: some View {
VStack {
if editMode {
DatePicker("Test", selection: $date)
.labelsHidden()
.onChange(of: date) { _ in
editMode = false
}
.frame(width: 50)
} else {
Text(date.formatted(date: .abbreviated, time: .omitted))
.onTapGesture {
editMode = true
}
}
}
.padding()
}
}
Ideally I'd also be able to dismiss it programatically.
I have tried using a ZStack with opacity control, such as:
struct ContentView: View {
#State private var date: Date = .now
#State private var editMode: Bool = false
var body: some View {
ZStack {
DatePicker("Test", selection: $date)
.labelsHidden()
.onChange(of: date) { _ in
editMode = false
}
.frame(width: 50)
.opacity(editMode ? 1.0 : 0.0)
Text(date.formatted(date: .abbreviated, time: .omitted))
.onTapGesture {
editMode = true
}
.opacity(editMode ? 0.0 : 1.0)
}
.padding()
}
}
But this isn't working in my actual use case. I'd really like to know if there was a way of dismissing that DatePicker expanded form.

SwiftUI 2.0 Change DatePicker background

Is there anyway to change the background color of the DatePicker view? I have the following Picker:
DatePicker(selection: $form.start, in: Date()..., displayedComponents: .date) {}
.padding(.vertical, 6)
.labelsHidden()
.accentColor(.black)
But the picker has that grayish tint around the date (See image below). I just want the entire background to be white. I tried .background(Color.white) but that doesn't do anything. How can I make the entire background white?
I discovered that you can do this by initialising it as follows:
init() {
UIDatePicker.appearance().backgroundColor = UIColor.init(.white) // changes bg color
UIDatePicker.appearance().tintColor = UIColor.init(.white) // changes font color
}
I don't agree with changing the default DatePicker, specially with this kind of 🔨, as you risk break future OS updates of it plus your/your designers vision of it is probably not as intuitive/accessible as Apple vision.
Still, if you are required to do it as I had to, here is how to do it while developing with a min target of iOS 14.
struct DatePickerView: View {
#State private var selectedDate = Date()
let dateFormatter = DateFormatter()
var body: some View {
HStack {
Text(dateFormatter.string(from: selectedDate))
.overlay {
DatePicker(
"",
selection: $selectedDate,
displayedComponents: .date
)
.blendMode(.destinationOver)
}
}
}
}
You can do this as follows:
dateTimePicker.setValue(UIColor.white, forKey: "backgroundColor")

How to change DatePicker's texts color in SwiftUI?

I saw this question but it doesn't work in SwiftUI.
Changing text color of datepicker
I tried .forgroundColor and .accentColor but they don't change the texts' color.
Using .colorInvert() and .colorMultiply(.white) will change the text color to white but won't work when the device is in Dark Mode. Instead, try this:
DatePicker("Date", selection: $selection)
.colorScheme(.dark) // or .light to get black text
I just simple set the accentColor and it works.
#State private var date = Date()
DatePicker("Select a date",
selection: $date, in: ...Date(),
displayedComponents: .date)
.labelsHidden()
.accentColor(.orange)
try this:
var body: some View {
Group {
DatePicker(selection: $selected) {
Text("Date")
}
.colorInvert()
.colorMultiply(Color.blue)
}
}
I use extension for customized text color for both of light mode and dark mode.
extension View {
#ViewBuilder func changeTextColor(_ color: Color) -> some View {
if UITraitCollection.current.userInterfaceStyle == .light {
self.colorInvert().colorMultiply(color)
} else {
self.colorMultiply(color)
}
}
}
and sample is as below.
DatePicker("Date", selection: $selection)
.labelsHidden()
.changeTextColor(.green)
you can do this to make the text appear white in light or dark mode if you use a background color
struct PickerDate: View {
#Environment(\.colorScheme) var colorScheme
#State var date: Date
var body: some View {
VStack {
DatePicker("Record Date", selection: $date, in: ...Date(), displayedComponents: .date)
.labelsHidden()
.colorMultiply(colorScheme == .dark ? .black : .white)
.colorInvert()
}
}
}
}
I found applying this modifier to the DatePicker to be cleaner:
Forces the color to white
.environment(\.colorScheme, .dark)
Forces the color to dark
.environment(\.colorScheme, .light)
DatePicker("DatePicker", selection: self.$date, displayedComponents: .hourAndMinute)
.labelsHidden()
.colorMultiply(Color.white)
.colorInvert()

SwiftUI TextField doesn't commit change when tapping another TextField

I am building some basic form functionality in my app at the moment and I am having trouble with TextFields not changing the value in the relevant binded variable when tapping another TextField or pressing "Done" in Edit Mode.
#Binding var jobDetails: JobDetails
#Environment(\.colorScheme) var colorScheme: ColorScheme
...
var body: some View {
...
HStack {
Text("Hourly Rate")
Spacer()
TextField("", value: $jobDetails.hourlyRateBasic, formatter: TextFormatters().currencyFormatter())
.keyboardType(.asciiCapableNumberPad)
.multilineTextAlignment(.trailing)
...
In the iOS simulator, the field only seems to update when I physically hit the return key on my keyboard after typing in a new value (not the soft keyboard in the simulator). I would like the TextField to commit it's change to jobDetails.hourlyRateBasic when tapping another TextField or pressing "Done" to exit edit mode.
It seems that onEditingChanged fires when I tap another TextField, but I don't know how to leverage that into changing the jobDetails with the new value.
This is typical behavior of TextField in SwiftUI. Following is an example of it and alternative method to make TextField more responsive while typing.
import SwiftUI
struct ContentView: View {
#State private var text: String = "0"
#State private var num: Int = 0
private var resultString: String {
if let num = Int(self.text) {
return String(num*num)
}
return "0"
}
private var resultInt: Int {
return self.num*self.num
}
var body: some View {
VStack {
VStack(alignment:.leading) {
Text("Input number as String")
TextField("String Number",text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Input number as Int")
TextField("Int Number", value: self.$num, formatter: NumberFormatter())
.textFieldStyle(RoundedBorderTextFieldStyle())
}
Spacer()
Text("From String")
Text("Square of \(self.text) is \(self.resultString)") .font(.title)
Spacer()
Text("From Int")
Text("Square of \(self.num) is \(self.resultInt)") .font(.title)
Spacer()
}.padding()
}
}
This is already fixed. Tested with Xcode 13.3 / iOS 15.4
struct TestView: View {
#State private var value1 = 1.0
#State private var text = ""
private var currencyFormatter: NumberFormatter = {
var nf = NumberFormatter()
nf.numberStyle = .currency
return nf
}()
var body: some View {
VStack {
HStack {
Text("Hourly Rate [\(value1)]")
Spacer()
TextField("", value: $value1, formatter: currencyFormatter)
.keyboardType(.asciiCapableNumberPad)
.multilineTextAlignment(.trailing)
}
HStack {
Text("Other")
Spacer()
TextField("Enter something", text: $text)
}
}
}
}

Changing the color of a button in SwiftUI based on disabled or not

I have a textfield with a send button that's a systemImage arrow. I want the foreground color of the image to change depending on whether the textField is empty or not. (I.e. the button is gray, and it is disabled if the textfield is empty. It's blue if the count of the textfield text is > 1).
I have a workaround that's not perfect:
if chatMessageIsValid {
Spacer()
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.padding(.leading, 10)
.textFieldStyle(.roundedBorder)
Button(action: sendMessage) {
Image(systemName: "arrow.up.circle")
.foregroundColor(Color.blue)
.padding(.trailing, 10)
}.disabled(!chatMessageIsValid)
}
} else {
Spacer()
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.padding(.leading, 10)
.textFieldStyle(.roundedBorder)
Button(action: sendMessage) {
Image(systemName: "arrow.up.circle")
.foregroundColor(Color.gray)
.padding(.trailing, 10)
}.disabled(!chatMessageIsValid)
}
}
This almost works, and it does change the color of the image if the text is > 1 in length. However, due to the change in state you're kicked out of editing the textfield after one character is typed, and you'll need to select the textfield again to continue typing. Is there a better way to do this with the .disabled modifier?
I guess you want this:
You can add a computed property for the button color, and pass the property to the button's foregroundColor modifier. You can also use a single padding modifier around the HStack instead of separate paddings on its subviews.
struct ContentView : View {
#State var chatMessage: String = ""
var body: some View {
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.textFieldStyle(.roundedBorder)
Button(action: sendMessage) {
Image(systemName: "arrow.up.circle")
.foregroundColor(buttonColor)
}
.disabled(!chatMessageIsValid)
}
.padding([.leading, .trailing], 10)
}
var chatMessageIsValid: Bool {
return !chatMessage.isEmpty
}
var buttonColor: Color {
return chatMessageIsValid ? .accentColor : .gray
}
func sendMessage() {
chatMessage = ""
}
}
However, you shouldn't use the foregroundColor modifier at all here. You should use the accentColor modifier. Using accentColor has two benefits:
The Image will automatically use the environment's accentColor when the Button is enabled, and gray when the Button is disabled. You don't have to compute the color at all.
You can set the accentColor in the environment high up in your View hierarchy, and it will trickle down to all descendants. This makes it easy to set a uniform accent color for your whole interface.
In the following example, I put the accentColor modifier on the HStack. In a real app, you would probably set it on the root view of your entire app:
struct ContentView : View {
#State var chatMessage: String = ""
var body: some View {
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.textFieldStyle(.roundedBorder)
Button(action: sendMessage) {
Image(systemName: "arrow.up.circle")
}
.disabled(!chatMessageIsValid)
}
.padding([.leading, .trailing], 10)
.accentColor(.orange)
}
var chatMessageIsValid: Bool {
return !chatMessage.isEmpty
}
func sendMessage() {
chatMessage = ""
}
}
Also, Matt's idea of extracting the send button into its own type is probably smart. It makes it easy to do nifty things like animating it when the user clicks it:
Here's the code:
struct ContentView : View {
#State var chatMessage: String = ""
var body: some View {
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.textFieldStyle(.roundedBorder)
SendButton(action: sendMessage, isDisabled: chatMessage.isEmpty)
}
.padding([.leading, .trailing], 10)
.accentColor(.orange)
}
func sendMessage() {
chatMessage = ""
}
}
struct SendButton: View {
let action: () -> ()
let isDisabled: Bool
var body: some View {
Button(action: {
withAnimation {
self.action()
self.clickCount += 1
}
}) {
Image(systemName: "arrow.up.circle")
.rotationEffect(.radians(2 * Double.pi * clickCount))
.animation(.basic(curve: .easeOut))
}
.disabled(isDisabled)
}
#State private var clickCount: Double = 0
}
With these various solutions, you can use the \.isEnabled environment property instead of creating custom button styles or passing in disabled booleans yourself.
#Environment(\.isEnabled) private var isEnabled
If you want to customize the button even further with disabled (or any other) state, you can add variables to your custom ButtonStyle struct.
Swift 5.1
struct CustomButtonStyle: ButtonStyle {
var disabled = false
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.background(disabled ? .red : .blue)
}
}
// SwiftUI
#State var isDisabled = true
var body: some View {
Button().buttonStyle(CustomButtonStyle(disabled: self.isDisabled))
}
Try this
Spacer()
HStack {
TextField($chatMessage, placeholder: Text("Reply"))
.padding(.leading, 10)
.textFieldStyle(.roundedBorder)
Button(action: sendMessage) {
Image(systemName: "arrow.up.circle")
.foregroundColor(chatMessageIsValid ? Color.blue : Color.gray)
.padding(.trailing, 10)
}.disabled(!chatMessageIsValid)
}
I suppose, the reason TextField loses focus is that in your code the whole view hierarchy is changed depending on the value of chatMessageIsValid. SwiftUI doesn't understand that the view hierarchy in the then block is almost identical to the one in the else block, so it rebuilds it completely.
In this edited code it should see that the only thing that changes is the foregroundColor of the image and the disabled status of the button, leaving the TextField untouched. Apple had a similar examples in one of the WWDC videos, I believe.
All modifier arguments can be conditional:
.foregroundColor(condition ? .red : .yellow)
where the condition could be any true/false
var condition: Bool { chatMessage.isEmpty } // can be used inline
You can use any other condition you need