Swift UI with Nested Navigation Views - swift

I am new to SwiftUI and Stack Overflow as well, so any help here is appreciated. I am currently making an app that has nested Navigation Views. However, one issue I am having is that when I am using these nested Views, the Navigation Titles (ex. the back button) are lining below each other, forming a Navigation Bar that is quite tall. I looked at this link and it used a List, but I do not want my app to be in this format. I would like to use text elements that are formatted in different colors and font sizes (please see the attached screenshot), however, this thread only has this list function, not in the format I would like it. Please help me figure out how to get rid of these extra navigation bars!
Text Elements:
Nested Navigation Bar:
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Image("img6").resizable().aspectRatio(contentMode: .fill).ignoresSafeArea()
VStack {
Text("SLED").font(.largeTitle).fontWeight(.heavy).padding().background(Color.blue).cornerRadius(10.0)
Spacer()
Text("Shelf Life Expiry Date Tracker").font(.title).fontWeight(.bold)
Spacer()
ZStack {
Image(systemName: "cloud")
.padding()
.font(.system(size: 90))
//try to fix this
//Image("logo1")
Image(systemName: "clock.fill")
.padding()
.font(.system(size: 40))
}
NavigationLink(destination: SeeTestKit()) {
Text("See Test Kit")
}.foregroundColor(Color(red: 0.0, green: 0.0, blue: 0.0))
.padding(20)
.background(Color.pink)
.cornerRadius(10.0)
//Spacer()
HStack {
Spacer()
Image(systemName: "bag")
.padding()
.font(.system(size: 40))
Spacer()
Image(systemName: "alarm.fill")
.padding()
.font(.system(size: 40))
Spacer()
Image(systemName: "calendar")
.padding()
.font(.system(size: 40))
Spacer()
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}```
```import SwiftUI
struct SeeTestKit: View {
var body: some View {
NavigationView {
ZStack {
Image("background2")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack {
Spacer()
Image("testkit")
.resizable()
.aspectRatio(contentMode: .fit)
Spacer()
Text("The test kit serves as a supplement to the SLED Tracking System.")
.font(.title)
.multilineTextAlignment(.center)
Spacer()
Text("The test kit make the observation process more efficient by not requiring you to find your own materials.")
.font(.title2)
.multilineTextAlignment(.center)
Spacer()
Text("It also serves as an alternative to \npeople who may not have access \nto the technology required for the app.").font(.title2).multilineTextAlignment(.center)
Spacer()
Group {
NavigationLink(destination: TestKitMaterials()) {
Text("Materials")
}.foregroundColor(Color(red: 0.0, green: 0.0, blue: 0.0))
.padding(20)
.background(Color.blue)
.cornerRadius(10.0)
.navigationBarTitle("Test Kit", displayMode: .inline).padding()
Spacer()
}
}
}
}
}
}
struct SeeTestKit_Previews: PreviewProvider {
static var previews: some View {
SeeTestKit()
}
}```
```import SwiftUI
struct TestKitMaterials: View {
var body: some View {
ZStack {
Image("bckgrd")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack {
Text("Test Kit Materials")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.white)
.padding(.all, 9.0)
.background(Color(red: 0.5, green: 0.6, blue: 0.9, opacity: 1.0)) .cornerRadius(10.0)
//FIX this
//Text("Safety Glasses").bold()
Spacer()
Group {
Spacer()
Text("Safety glasses were included to prevent \ncontamination or illness from \npotentially spoiled foods.")
.multilineTextAlignment(.center)
.padding(.all, 3.0)
Text("A thermometer was included to test \ntemperature of foods to test for spoilage \nat a certain temperature.")
.multilineTextAlignment(.center)
.padding()
}
Group {
Text("pH strips were used to test the pH of \nthe milk and eggs.")
.multilineTextAlignment(.center)
.padding()
Text("The flashlight and ruler are included for \nthe user to measure the width of the air\n sac of the egg in a dark room.")
.multilineTextAlignment(.center)
.padding()
}
Group {
Text("A pipette is needed to extract samples \nof the food to test certain characteristics.")
.multilineTextAlignment(.center)
.padding()
Text("The test tube provides a separate vessel \nto hold the milk to make observations.")
.multilineTextAlignment(.center)
.padding()
Text("A checklist is included as a guideline for the \nuser to measure characteristics and to\n measure spoilage in the absence of the app.")
.multilineTextAlignment(.center)
.padding()
}
Spacer()
Image(systemName: "img")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
}
}
struct TestKitMaterials_Previews: PreviewProvider {
static var previews: some View {
TestKitMaterials()
}
}```
Thank you!

As stated earlier in the comments you need to remove a NavigationView. And almost always you'll want to remove any NavigationView on the children views.
Essentially what's happening is you are double stacking your NavViews and can cause some really funky behavior.
Read more on NavigationView

Related

Adjusting big or small screens using geometry reader

I am currently stuck in trouble i am not really able to resize image and texts to fit bigger or small screens. This is just a simple card that i want to be able to be resizable and should be in the same proportions. I have even tried geometry reader. I am having trouble as even if I have used geometry reader reader the white spaces I wanted are not there as the picture takes the entire width and height which looks bad not ideal. If there is a way for me to have the same proportion of the card for all different screen sizes then I am all open to ideas.
Image of my card:
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Image("shops")
.resizable()
.aspectRatio(contentMode: .fill)
VStack {
Spacer()
Text("Welcome to MarketInfo")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
}
.padding(.bottom, 30)
.frame(width: 400)
}
.frame(width: 380, height: 270)
.cornerRadius(20)
.clipped()
.shadow(radius: 8)
.padding(.top, 20)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
ContentView()
}
}
}
I think where you are running into issues is in using the fixed frame. The issue with using it is that it does not adjust for sizes. If you had something complicated, you could use a GeometryReader to resize the frame based on a proportion of the available space. However, the simplest implementation is setting your Text as an .overlay() on your image and setting the .aspectRatio(contentMode: .fit).
The code would now be:
struct ContentView: View {
var body: some View {
Image("shops")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(
VStack {
Spacer()
Text("Welcome to MarketInfo")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
}
)
.cornerRadius(20)
.clipped()
.shadow(radius: 8)
.padding(30)
}
}
The overlay is never bigger than the Image, which can be an issue using a ZStack, and it will move with the Image. I then just through some padding on the Image all around to keep it in from the sides, and away from other views.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack() {
Image("shops")
.resizable()
.aspectRatio(1.4, contentMode: .fit) // 380 / 270 = 1.4
.overlay(
VStack {
Spacer()
Text("Welcome to MarketInfo")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding()
})
}
// .frame is not required, but Change .infinity to a number will resize the card
.frame(maxWidth: .infinity)
.cornerRadius(20)
.shadow(radius: 8)
.padding(.horizontal, 20)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice("iPhone 13 Pro")
ContentView()
.previewDevice("iPod touch (7th generation)")
ContentView()
.previewDevice("iPad Pro (12.9-inch) (5th generation)")
}
}

Why do the elements inside my VStack inside a HStack still go vertical

So basically this is my code.
Text("Melbourne, Victoria")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.white)
.padding(.bottom, 30)
Image(systemName: "moon.fill")
.foregroundColor(Color.white)
.font(.system(size: 60))
Text("Today")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
Text("34°C")
.font(.title3)
.fontWeight(.medium)
.foregroundColor(Color.white)
Spacer()
HStack {
Image(systemName: "sun.max.fill")
.foregroundColor(Color.yellow)
.font(.system(size: 40))
.padding(.bottom, 550)
Text("Mon 34°C")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
.padding(.bottom, 550)
}
}
.background(
Image("night")
.ignoresSafeArea()
)
I want to make more of the days in this weather app each with it's on SF Symbol and everything is inside this VStack. And the days and SF symbols are inside a HStack to keep them horizontal. But if I want to put more of those the next time I do it they go next to each other but the symbol goes on top look at this.
This is the image with only 1 of those days
And this one is when I put more than 1 but its next to each other when I want them vertically aligned.
This is the one if I put them in another VStack which makes the SF symbol go above the text
IS there any solution to this?
The problem is your .padding(.bottom, 550).
Image(systemName: "sun.max.fill")
.foregroundColor(Color.yellow)
.font(.system(size: 40))
.padding(.bottom, 550) /// here!
It's currently attached to your first Image. As a result, the HStack is stretched vertically and it appears as though the first Image is higher up than second one.
To fix, move both .padding(.bottom, 550)s to outside your HStack.
You have quite a lot of arbitrary values for padding. You should have your layout adapt to various devices, rather than hard-coding that .bottom spacing for example.
Working example:
struct ContentView: View {
var body: some View {
VStack(spacing: 40) {
Text("Melbourne, Victoria")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.white)
VStack {
Image(systemName: "moon.fill")
.foregroundColor(Color.white)
.font(.system(size: 60))
Text("Today")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
Text("34°C")
.font(.title3)
.fontWeight(.medium)
.foregroundColor(Color.white)
}
VStack(spacing: 10) {
HStack {
Image(systemName: "sun.max.fill")
.renderingMode(.original)
.font(.system(size: 40))
Text("Mon 34°C")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
}
HStack {
Image(systemName: "cloud.sun.fill")
.renderingMode(.original)
.font(.system(size: 40))
Text("Tue 28°C")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
}
}
Spacer()
}
.frame(maxWidth: .infinity)
.background(
Color(red: 0.34, green: 0.36, blue: 0.71)//Image("night")
.ignoresSafeArea()
)
}
}
Result:
Although there is already an outer VStack, they are nested VStacks because they have different amounts of padding. Multiple views are grouped together, and then those are grouped together with larger paddings to separate them.
Bonus
SF Symbols colors
Use:
.renderingMode(.original)
Rather than:
.foregroundColor(Color.yellow)
To color the SF Symbols with the default colors, as shown in the above example.
Align days
You can align the days across each HStack with a custom alignment guide. Use the following:
extension HorizontalAlignment {
struct LeadingDay: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[.leading]
}
}
static let leadingDay = HorizontalAlignment(LeadingDay.self)
}
Making the other following changes:
VStack(alignment: .leadingDay, spacing: 10) { // <- HERE
HStack {
Image(systemName: "sun.max.fill")
.renderingMode(.original)
.font(.system(size: 40))
Text("Mon 34°C")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
.alignmentGuide(.leadingDay) { d in // <- HERE
d[.leading]
}
}
HStack {
Image(systemName: "cloud.sun.fill")
.renderingMode(.original)
.font(.system(size: 40))
Text("Tue 28°C")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color.white)
.alignmentGuide(.leadingDay) { d in // <- HERE
d[.leading]
}
}
}
Result:

SwiftUI TabView Embedded In Another View Problem

For a few time now, I have been playing with SwiftUI. I'd like to make it clear that I do have a Java and JS background, but no any Swift experience.
So the idea is:
I'd like to have a view (referred later on as main view) where the screen is divided into two parts (views). The top one will be used just like a header - picture with maybe a footer (the frosted footer in the screenshots).
Then the second part of the screen I want it to be another View that is actually a TabView. See attached screenshots of unscrolled view of the main view and
scrolled view of the main view
Currently, what I have now is:
struct CarDetailsView: View {
let car: Car
var body: some View {
ScrollView {
HeadingView(car: car)
CarDescriptionView(car: car)
.offset(y: -36)
}
.ignoresSafeArea()
}
}
Which works fine (as in the screenshots), where HeadingView is the picture with the footer, and then CarDescriptionView is the "second" view of the screen with the description. This is also the part that I'd like to make it TabView.
I thought, I can just wrap the CarDescription View inside a TabView, just like that:
struct CarDetailsView: View {
let car: Car
var body: some View {
ScrollView {
HeadingView(car: car)
TabView {
CarDescriptionView(car: car)
.offset(y: -36)
.tabItem {
Label("Description", systemImage: "car")
}
}
}
.ignoresSafeArea()
}
}
However, then I am getting this thing happening here
Note, the structure of my other View are:
struct HeadingView: View {
var car: Car
var body: some View {
VStack(spacing: 0) {
Text("25 300EUR")
.foregroundColor(.white)
.font(.title)
.bold()
.padding(.horizontal, 12)
.padding(.vertical, 4)
.background(Color.init(hexadecimal: "66B054"))
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading)
.padding(16)
HStack {
Image(systemName: "car")//todo: make logo go here
.padding(.leading, 16)
.padding(.trailing, 16)
VStack(alignment: .leading, spacing: 4) {
Text("\(car.make) \(car.model)")
.font(.title2)
.bold()
HStack {
Text("\(String(car.odometer / 1000))k km |")
.font(.caption)
Text("\(String(car.year)) |")
.font(.caption)
Text("\(String(car.engine.horsepower))bhp |")
.font(.caption)
Text("20km away")
.font(.caption)
}
}
Spacer()
}
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding(.top, 12)
.padding(.bottom, 38)
.background(
VisualEffectBlurView(blurStyle: .systemThinMaterialDark)
)
}
.frame(maxWidth: .infinity, minHeight: 500, maxHeight: 500)
.background(
Image(car.images[0])
.resizable()
.aspectRatio(contentMode: .fill)
)
.mask(
RoundedRectangle(cornerRadius: 0, style: .continuous)
)
}
}
and
struct CarDescriptionView: View {
let car: Car
var body: some View {
ScrollView {
Text("Description")
.font(.title)
.bold()
.padding(.top, 32)
.padding(.leading, 16)
.padding(.bottom, 16)
.frame(maxWidth: .infinity, alignment: .leading)
Text(car.description)
.font(.body)
.padding(.horizontal)
Text(car.description + "\n")
.font(.body)
.padding(.horizontal)
Text(car.description + "\n")
.font(.body)
.padding(.horizontal)
Text(car.description + "\n")
.font(.body)
.padding(.horizontal)
Text(car.description + "\n")
.font(.body)
.padding(.horizontal)
}
.background(.white)
.mask(
RoundedRectangle(cornerRadius: 30, style: .continuous)
)
}
}
Please note that I have also tried to place the TabView inside the CarDetailsView, but again with the same result.
Changing the ScrollView to VStack in the main component results in somewhat working view. However, then I do not get any scrolling of the header View (the car image), but only I am able to scroll within the tabbed view.
As I said at the beginning, I am really new to this, so I am not even sure if the concept is correct or not.

Large space above navigationTitle (Swift / SwiftUI) [duplicate]

Im currently working on a project for iOS using SwiftUI. I have 3 pages (MainMenu, CalendarList, and DateDetails.)
On the 2nd page (CalenderList) there is an empty space between the top of the screen and the actual NavigationBarTitle.
on the third page, you can see the back button (to the MainMenu) and there is two empty spaces at the top.
I've seen people use .navigationBarHidden to fix this, but i haven't been able to implement it in a way that fixes the problem.
Am i using NavigationView() incorrectly? or is there a special trick?
Here is the code for the MainMenu:
import SwiftUI
struct MainMenu: View {
var body: some View {
NavigationView {
VStack {
Text("Calendar")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(Color(red: 0.055, green: 0.173, blue: 0.322))
.padding(.top, 55.0)
Text("Main Menu")
.font(.headline)
.foregroundColor(Color(red: 0.635, green: 0.635, blue: 0.635, opacity: 1.0))
/*Image("Logo")
.resizable()
.frame(width: 150.0, height: 150.0)*/
Spacer()
HStack {
NavigationLink(destination: CalendarList()) {
Image(systemName: "calendar")
.resizable()
.frame(width: 75.0, height: 75.0)
.padding()
}
NavigationLink(destination: CalendarList()) {
Image(systemName: "gear")
.resizable()
.frame(width: 75.0, height: 75.0)
.padding()
}
}
HStack {
NavigationLink(destination: StudentInfo()) {
Image(systemName: "info.circle")
.resizable()
.frame(width: 75.0, height: 75.0)
.padding()
}
NavigationLink(destination: CalendarList()) {
Image(systemName: "exclamationmark.circle")
.resizable()
.frame(width: 75.0, height: 75.0)
.padding()
}
}
Spacer()
}
}
}
}
Here is the code for CalendarList (page 2):
import SwiftUI
struct CalendarList: View {
var body: some View {
NavigationView {
List(calendarData, id: \.date) { Calendar in
if Calendar.collab {
NavigationLink(destination: DateDetails(calendar: Calendar)) {
CalendarRow(calendar: Calendar)
}
} else {
CalendarRow(calendar: Calendar)
}
}
.navigationBarTitle(Text("Schedule"))
}
}
}
And here is the code for DateDetails (page 3):
import SwiftUI
struct DateDetails: View {
var calendar: Calendar
var body: some View {
NavigationView {
VStack (alignment: .center) {
//Image("Logo")
HStack {
Text(calendar.month.prefix(4) + ".")
.font(.largeTitle)
Text(String(calendar.date).suffix(1))
.font(.largeTitle)
Spacer()
}
HStack {
Text(calendar.schedule)
.font(.title)
Spacer()
}
Spacer()
.frame(height: 30.0)
Text(calendar.info)
.font(.body)
Spacer()
}
.navigationBarTitle(String(calendar.date).prefix(4).suffix(2) + "/" + String(calendar.date).suffix(2))
.padding()
}
}
}
Only use NavigationView at the top level, you don't need to add it in every subscreen, just remove it from CalendarList and DateDetails and it will fix your spacing issue
I think you can delete the NavigationView of DateDetails.
If you want to change the navigationbar, you may want to edit navigationBarItems or change navigationBarHidden to true and customize it.
https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-bar-items-to-a-navigation-view

My TextField in Swift UI is Unresponsive to taps and clicks, text cannot be input, is there an issue with the code?

Hi I'm an intermediate level engineer with swift and swift UI.
Currently, I am having problems with the TextField(), it is unresponsive to taps and clicks, and hence the keyboard does not show up,
However, it does show up when it is one of two main elements within a VStack, but anymore than that and it does not work,
Is there an issue with the code? and can anyone find a fix or a work around for this?
Thanks, Miles
{
#State var textField = ""
var body: some View {
returnJoinModalContent()
}
func returnJoinModalContent() -> some View{
let accentColor = Color.blue
return VStack(alignment: .center){
HStack{
Text("Join Game")
.bold()
.font(.largeTitle)
.padding()
Spacer()
Button(action:{
//self.hideModal()
}){
Image(systemName: "xmark.circle.fill")
.resizable()
.frame(width: 50,height: 50)
.foregroundColor(accentColor)
.padding()
}
}
.padding(.bottom,170)
VStack(alignment: .leading){
TextField("Enter Game Code",text: self.$textField)
.font(.largeTitle)
Rectangle()
.frame(height: 6)
.foregroundColor(accentColor)
}
.padding()
HStack{
Button(action:{
//self.joinGame()
}){
ZStack{
RoundedRectangle(cornerRadius: 90)
.frame(width: 200,height: 70)
.foregroundColor(accentColor)
Text("Ready!")
.bold()
.font(.title)
.foregroundColor(Color.white)
}
}
}
.padding(.vertical,20)
HStack{
Image(systemName: "info.circle")
.resizable()
.frame(width:60,height:60)
.foregroundColor(accentColor)
Text("Ask your host for the game code, it can be found at the bottom of the player lobby")
.padding()
Spacer()
}
.padding(.top,60)
.padding(.horizontal,20)
Spacer()
}
.padding()
}
}
This seems to be one of those things in swiftUI that is difficult to track down. I found the problem is your ready button. More specifically the label being a ZStack with a colored rounded rectangle as the background. If you replace your button's label with the following your text field should be selectable.
Text("Ready!")
.bold()
.font(.title)
.foregroundColor(Color.white)
.frame(width: 200,height: 70)
.background(accentColor)
.mask(RoundedRectangle(cornerRadius: 90))
I've found that overlay on the TextField or the container of TextField might cause it to be unresponsive
you should remove it and replace it with ZStack