If I use DatePicker inside a Form, it will render just like I want. At first it is just one line with the label and the current value. When the user clicks, it will expand and show the date picker wheel.
Example:
Form {
DatePicker(selection: $dueDate, displayedComponents: .date) {
Text("Due date")
}
}
Expands to this on user click:
Now the question is; is it possible to get this behaviour outside a Form? - The Form makes things look ugly in my context. I do not want the gray background or the drop shadow.
Do anybody have a solution for this, or is a custom implementation needed?
My first attempt with Form just around the DatePicker, gave me this: :-)
Ok, now I have found something that seems to work.
var body: some View {
VStack(spacing: 0) {
VStack(alignment: .leading){
Text("Due date")
Text(formattedDate)
Divider().background(Color.gray)
}.frame(minWidth: 0, maxWidth: .infinity).onTapGesture {
withAnimation {
self.showSelect.toggle()
}
}
if showSelect {
DatePicker("", selection: $date, displayedComponents: [.date]).labelsHidden()
}
Spacer()
}
}
I have not included all the logic, but this is a good place to start.
Related
I really like the look of the navigation bar title in SwiftUI, and I like that it appears just below the safe area, but appears in the principal part of the toolbar when you scroll down. I'm wondering how to completely replicate this look and behavior but make it editable by the user (most likely through a textfield?)
I've tried
.toolbar {
ToolbarItem(placement: .principal) {
TextField("Navigation Title", text: $mainTitle)
}
}
But this simply places the title in the toolbar at all times, rather than only when you scroll slightly.
Any ideas?
First I explain why your code does not work:
Only the size of the navigationTitle changes when you start to scroll, not the size of the whole toolbar or its items.
But I think I have a solution:
import SwiftUI
struct ContentView: View {
#State private var title: String = "Title"
#State private var titleSmall: Bool = false
var body: some View {
NavigationView {
List {
GeometryReader { geo in
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
.onChange(of: geo.frame(in: .global).minY) { val in
if val <= 53.5 {
titleSmall = true
} else {
titleSmall = false
}
}
}
Text("Hello, world!")
}
.toolbar {
ToolbarItem(placement: .principal) {
TextField("Title", text: $title)
.multilineTextAlignment(.center)
.font(titleSmall ? .headline : .largeTitle.bold())
.accessibilityAddTraits(.isHeader)
}
}
}
}
}
What the code does is: It gets the top Y position from the first (in this example) list item.
Then it checks if the first list item is under the title bar and changes the font size of the title if necessary.
The only Problem I see is that there is a pretty rough transition between small and big title but I think you can figure out how to fix this.
If you have more questions how the code works just ask
I hope that solves your question.
And I would recommend to have a look at Paul Hudson’s video about the Geometry Reader (he’s a great YouTuber): https://youtu.be/kh9lnIYgW1E
I just realized that it says „OLD“ in the video title, so it may be outdated.
But he has some other videos about the Geometry Reader.
just search for „Paul Hudson Geometry Reader“
Very novice to the app development game. I am trying to put this toolbar above the .decimalPad and I cannot get this large gap to go away.
VStack {
Rectangle()
.foregroundColor(Color(UIColor.systemBackground))
.frame(height: 35)
.overlay {
HStack {
Spacer()
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
}
.offset(y: -3)
.padding(.trailing)
}
.opacity(isTextFieldFocused ? 1 : 0)
.ignoresSafeArea(.keyboard) //This makes sure the bottom tab bar stays below the keyboard.
}
I initially thought it was something in another view causing the spacing, but I managed to parse through the views in the canvas and it does it regardless.
Here is what I'd like it to look like, for reference.
What I want
To add a Button onto your keyboard, you use a .toolbar with the locations to .keyboard like this:
TextField("Enter Text", text: $text)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
// If you want it leading, then use a Spacer() after
Spacer()
}
}
You were overthinking it by adding the Rectangle. This is why we look for minimal reproducible examples. We can dial in the fix for your specific code.
I need a custom "label" with the behavior of opening the date picker in some kind of popover, even on the iPhone.
When I use the built in DatePicker with compact format, I have the behavior with opening the wheels in a popover, but I cannot customize the look of the "collapsed" version with the gray background (in fact, I can change it to some other color, but not to white e.g., it simply doesn't work).
Another option would be to have a custom Text wrapped inside a Button and then open the DatePicker, but I don't know how to achieve the popover effect on the iPhone. So I guess the first option would be the easier one, but how can I achieve a custom layout for the compact date picker? Nothing super fancy, just something like this:
instead of something like this
its more of a proof of concept, but this might be a way:
import SwiftUI
struct ContentView: View {
#State private var showPicker = true
#State var selectedDate = Date()
Button {
withAnimation {
showPicker.toggle()
}
} label: {
Text(Text(selectedDate.getFormattedDate(format:"HH:mm:")))
.padding()
.padding(.horizontal)
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray)
)
}
.background(
DatePicker("", selection: $selectedDate, displayedComponents: .hourAndMinute)
.datePickerStyle(.wheel)
.frame(width: 200, height: 100)
.clipped()
.background(Color.gray.cornerRadius(10))
.opacity(showPicker ? 1 : 0 )
.offset(x: 50, y: 90)
).onChange(of: selectedDate) { newValue in
withAnimation {
showPicker.toggle()
}
}
}
Note that DidSet or willSet is not possible for Objects, however, we had a nice workaround onChange. When date changed, just toggle, done.
I am getting a second back button when I do a form picker.
First view comes from another view. (so that makes it the second view?)
After I choose the picker I get this.
There is nothing fancy in the code.
How can I avoid this?
NavigationView {
Form {
Text("text")
Picker(selection: .constant(1) , label: Text("Picker") ) {
Text("1").tag(1)
Text("2").tag(2)
}
Spacer()
}
}
.navigationBarTitle("Settings")
Thank you.
It looks like parent view already has NavigationView, so, by concept there should be only one navigation view in view hierarchy, you should just remove second one
// NavigationView { // << remove this one
Form {
Text("text")
Picker(selection: .constant(1) , label: Text("Picker") ) {
Text("1").tag(1)
Text("2").tag(2)
}
Spacer()
}
.navigationBarTitle("Settings") // << specify on Form !!
I'm a new learner to programming and SwiftUI and I am trying to learn how create a personal diary app.
I want a text to show the current date on startup, but when you select a different date on the calendar, it'll show the selected date instead. I tried to use an if else condition but it doesn't work properly. I think it's because I used "if singleisPresented != false" so it only appears when I click the button. But what I should use instead? Many thanks guys.
I am using the RKCalendar (https://github.com/RaffiKian/RKCalendar) to help me with the calendar.
Button(action: { self.singleIsPresented.toggle() }) {
Image(systemName: "calendar")
.foregroundColor(.black)
.frame(width: 18, height: 25)
}
.sheet(isPresented: self.$singleIsPresented, content: {
RKViewController(isPresented: self.$singleIsPresented, rkManager: self.rkManager)})
.onAppear(perform: startUp)
.navigationViewStyle(StackNavigationViewStyle())
if singleIsPresented != false {
Text(self.getTextFromDate(date: self.rkManager.selectedDate))
.font(.headline)
.fontWeight(.bold)
} else {
Text(currentDate(date: Date()))
.font(.headline)
.fontWeight(.bold)
}
You could try something like this:
Button(action: { self.singleIsPresented.toggle() }) {
Text("Example 1 - Single Date Selection").foregroundColor(.blue)
}
.sheet(isPresented: self.$singleIsPresented, content: {
RKViewController(isPresented: self.$singleIsPresented, rkManager: self.rkManager1)})
if rkManager1.selectedDate != nil {
Text(self.getTextFromDate(date: self.rkManager1.selectedDate))
} else {
Text("\(Date())")
}
This checks if the user selected a date and will show the current date until the user selects one. This way, even if they look at the calendar and don't select a date, it'll still show the current date.