Hello! I am trying to center the DatePicker inside the Section in SwiftUI. The screenshot shows the current display and desired display.
I've attempted the following and it doesn't seem to work:
putting DatePicker in VStack with .alignment as centered,
using .frame(alignment: .center) on the section,
and attempting to place center decorators to the DatePicker itself.
The first 2^ center the text within the section, but not the actual DatePicker object itself on the screen.
An explanation on how to do this, and what to know for future cases like these would be super helpful. Thank you!
NavigationView {
Form {
Section {
VStack(alignment: .center) {
DatePicker("Please enter a time", selection: $wakeUpTime, displayedComponents: .hourAndMinute)
.labelsHidden()
}
} header: {
Text("When do you want to wake up?")
.font(.headline)
}
.frame(alignment: .center)
.navigationTitle("BetterRest")
.toolbar {
Button("Calculate", action: calculateBedtime)
}
You can use a HStack with two Spacer to acomplish this output in SwiftUI.
NavigationView {
Form {
Section {
HStack{ //Convert to HStack
Spacer() //add this
DatePicker("Please enter a time", selection: $wakeUpTime, displayedComponents: .hourAndMinute)
.labelsHidden()
Spacer() // add this
}
} header: {
Text("When do you want to wake up?")
.font(.headline)
}
.frame(alignment: .center)
.navigationTitle("BetterRest")
.toolbar {
Button("Calculate", action: calculateBedtime)
}
}
}
Output:
Try following code:
var body: some View {
NavigationView {
Form {
Section {
DatePicker("Please enter a time",
selection: $wakeUpTime,
displayedComponents: .hourAndMinute)
} header: {
Text("When do you want to wake up?")
.font(.headline)
}
.frame(alignment: .center)
.navigationTitle("BetterRest")
.toolbar {
Button("Calculate") {
print("Calculate")
}
}
}
}
}
Related
I've encountered the following issue with a .toolbar in NavigationView. It seems Spacer() doesn't work properly there. Here's a screenshot Issue
And here's the code
NavigationView {
...
.toolbar {
// bottom
ToolbarItemGroup(placement: .bottomBar) {
HStack {
NavigationLink {
...SomeView...
} label: {
VStack {
Image(systemName: "person.circle.fill")
Text("Contacts")
}
}.padding()
Spacer()
NavigationLink {
...SomeView...
} label: {
VStack {
Image(systemName: "message.fill")
Text("Chats")
}
}.padding()
Spacer()
NavigationLink {
...SomeView...
} label: {
VStack {
Image(systemName: "pencil.circle")
Text("Settings")
}
}.padding()
}
.font(.caption)
.foregroundColor(.gray)
}
}
Interestingly enough, it start working when I'm switching to another view (using NavigationLink), and then go back.
Another note that it shown correctly in preview in xcode, but behave differently on the phone (iPhone 12, iOS 15.4)
I've tried everything I can think of but I can't get this button to fire consistently when nested inside a ScrollView. If I change the ScrollView to a List, it works fine, but I don't want the UI of a list on the Watch, I would prefer to use a ScrollView. I think it's related to the system detecting taps to scroll and taps on the button but not sure how to fix or any workarounds?
struct WatchWorkoutSummaryView: View {
#EnvironmentObject var workoutManager: WorkoutManager
var body: some View {
ScrollView {
EffortLevelRingViewForWatch(workoutState: WatchWorkoutState.After)
Divider()
.padding(.horizontal)
IntensityViewForWatch()
Divider()
.padding(.horizontal)
HeartRateZonesViewForWatch(workoutState: WatchWorkoutState.After)
Divider()
.padding(.horizontal)
AerobicAnaerobicWatchMasterView()
Divider()
.padding(.horizontal)
VStack {
SummaryView().environmentObject(workoutManager)
Button(action: {
print("button is tapped")
workoutManager.dismissLiveWorkoutTabViewSheet()
workoutManager.resetWorkoutManager()
}, label: {
Text("Done")
.foregroundColor(.white)
})
.background(
Color.buttonColor
.cornerRadius(10)
)
.buttonStyle(BorderedButtonStyle(tint: .clear))
.padding(.horizontal)
.animation(nil)
}
.padding(.vertical)
}
//This doesn't work.
.animation(nil)
}
}
How can I add a subtitle under a large title that was defined this way?
NavigationView{
VStack(){
//"Some code"
}
.navigationTitle("Analytics")
}
You could try something like this:
NavigationView{
VStack(){
//"Some code"
}
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
VStack {
Text("Title").font(.largeTitle).bold()
Text("Subtitle").font(.subheadline)
}
}
}
}
Seems "navigationSubtitle" is only for macos, this is another way to do it in ios:
.navigationBarItems(leading: VStack {
Text("Analytics").font(.largeTitle).bold()
Text("Subtitle")
})
Edit update:
you could also try this, with some loss of font choice:
.navigationTitle("Subtitle")
.navigationBarItems(leading: Text("Analytics").font(.largeTitle).bold())
I have found a solution to what I was looking for: basically create the top of the screen as it would normally be without a navigationView and then add a scrollview underneath.
HStack {
VStack(alignment: .leading, spacing: 5) {
Text("Title")
.font(.largeTitle)
.foregroundColor(Color(UIColor.label))
.fontWeight(.bold)
Text("subtitle")
.font(.subheadline)
.foregroundColor(Color(UIColor.label))
}
Spacer()
}
.padding()
ScrollView(.vertical, showsIndicators: false) {}
I had my app working fine with SwiftUI and iOS13, and now I'm trying to add support to 14 and part of the app looks like this:
That gray are in the background shouldn't be there. I tried, setting the background to white inside the picker, removing some components, removing the frame and nothing works.
Here's the code:
ZStack {
if showingNewMealTime {
ZStack {
VStack(alignment: .center) {
ZStack{
Color.black.opacity(0.4)
.edgesIgnoringSafeArea(.vertical)
VStack(spacing: 20) {
Text("Change Time")
.bold().padding()
.frame(maxWidth: .infinity)
.background(Color.yellow)
.foregroundColor(Color.white)
HStack {
VStack {
Picker("", selection: $pickerHour){
ForEach(1..<12, id: \.self) { i in
Text("\(i)").tag(i)
}
}
.frame(width: 50, height: 120)
.clipped()
}
VStack {
Picker("", selection: $pickerMinutes){
ForEach(0..<60, id: \.self) { i in
Text("\(i)").tag(i)
}
}
.frame(width: 50, height: 120)
.clipped()
}
VStack {
Picker("", selection: $pickerAmOrPm[pickerAmOrPmSelection]){
ForEach(pickerAmOrPm, id: \.self) { i in
Text("\(i)").tag(i)
}
}
.frame(width: 50, height: 120)
.clipped()
}
}
Spacer()
Button(action: {
...
}){
Text("Save")
}
.padding(.horizontal)
.padding(.bottom, 30)
}
.frame(width:300, height: 300)
.background(Color.white)
.mask(RoundedRectangle(cornerRadius: 20))
.shadow(radius: 20)
}
}
}
}
}
What's changed and what do I have to fix to correct that gray area?
Thanks
Use the native DatePicker instead.
import SwiftUI
struct DatePickerView: View {
#State var date: Date = Date()
var body: some View {
VStack {
DatePicker("Date", selection: $date,
displayedComponents: .hourAndMinute)
.frame(height: 50)
Spacer()
}
.padding()
}
}
struct DatePickerView_Previews: PreviewProvider {
static var previews: some View {
DatePickerView()
}
}
Image showing the result of the above code
For more info:
SwiftUI DatePicker - swiftcompiled.com
Selecting dates and times with DatePicker - hackingwithswift.com
How to create a date picker and read values from it - hackingwithswift.com
DatePicker - Documentation
(Oct 11, 2020 Edit):
Edited adding links to more information.
I have a NavigationView and then a VStack. The issue is that everything inside VStack is being displayed in the middle of the screen and not on top. Why is that?
var body: some View {
VStack {
VStack(alignment: .leading) {
if (request?.receiverID ?? "") == userDataViewModel.userID {
Text("\(sendertFirstName) wants to ship your package").font(.title)
} else {
Text("You want to ship \(recieverFirstName)'s package").font(.title)
}
HStack{
Image(systemName: "clock.fill").font(.subheadline)
Text("\(createdAt, formatter: DateService().shortTime)").font(.subheadline).foregroundColor(.secondary)
}
HStack {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person.fill").font(.subheadline)
Text(sendertFirstName).font(.subheadline).foregroundColor(.secondary)
}
}
Spacer()
VStack(alignment: .leading) {
HStack {
Image(systemName: "star.fill")
Text(rating).font(.subheadline).foregroundColor(.secondary)
}
}
}
}
VStack(alignment: .center) {
HStack {
Button("Accept") {
//print(self.request.createdAt)
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "accepted")
}
.padding()
Button("Decline") {
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "declined")
}
.foregroundColor(.red)
.padding()
}
}
ScrollView {
ForEach(messages) { (message: Message) in
if message.senderId == self.userDataViewModel.userID {
HStack {
Spacer()
Text(message.text).font(.subheadline).padding(7.5).background(self.blue).cornerRadius(30).foregroundColor(.white)
}.padding(.bottom, 2)
} else {
HStack {
Text(message.text).font(.subheadline).padding(7.5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Spacer()
}.padding(.bottom, 2)
}
}
}
HStack {
TextField("Message", text: $inputMessage).padding(5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Button(action: {
let msg: [String: Any] = [
"text": self.inputMessage,
"createdAt": Timestamp(),
"senderId": self.userDataViewModel.userID
]
self.reference.updateData([
"messages": FieldValue.arrayUnion([msg])
])
self.messages.append(Message(dictionary: msg))
self.inputMessage = ""
}) {
Image(systemName: "arrow.up.circle.fill").foregroundColor(self.blue).font(.title)
}
}
Spacer()
}
.padding()
.border(Color.red)
}
I want the content to start on top not on the middle. What am I doing wrong here?
You don't need the NavigationView - the way you have everything set up now you are wrapping the VStack in a NavigationView twice and the massive white space is the second empty navigation bar.
By default most Views take only the amount of space they need and they are placed in the center of their parent view. So if you want your content to be placed at the top, you need to add Spacer() as the last element in your VStack:
var body: some View {
VStack {
/* ... */
Spacer()
}
}
Spacer is one of the rare Views that takes all space offered to it by the parent view and will push all of your content upwards.
Maybe you can try this way:
NavigationView {
VStack {
...
}.frame(maxHeight:.infinity, alignment: .top)
.padding()
.border(Color.red).navigationBarTitle("", displayMode: .inline)
}
Add a .navigationBarHidden(true) to your VStack