Set View Picker Header Color? - swiftui

Is it possible to add background color to the top of a picker view? I'm specifically interested in the area from the top of the view down to just below the navigation bar. I have made all my app tab and navigation views with the same header appearance but haven't had any luck with the pickers.
I would like to set the picker backgrounds to look like this:
Here is a picker view that I would like to add background color.
Here is what happens when adding call to DrawNavBox() just after the Picker (See code below). I need help pushing the green block to the top of the view.
The code below is what generates the Home Currency green header above
var body: some View {
GeometryReader { g in
VStack {
DrawNavBox(g: g) // draw green header
Text("Your home currency is:")
.padding(.top, g.size.height * 0.10)
Text("\(base.baseCur.baseCurrency) \(base.baseCur.baseCountry)")
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(15)
.background(Color("Dark Green"))
.cornerRadius(20)
.padding(.trailing, g.size.width * 0.19)
.padding(.leading, g.size.width * 0.22)
Form {
Section(header: Text("Select Home Currency")) {
Picker("Home Currency", selection: $gotBase) {
DrawNavBox(g: g)
ForEach(0 ..< currCountry.count, id: \.self) { item in
VStack(alignment: .leading) {
Text(currCountry[item])
.font(.subheadline)
.padding(.leading, 25)
HStack {
Text(currSymbol[item])
Text(currName[item])
}
.font(.caption)
.padding(.leading, 25)
}
}
}
}
}
}
.navigationBarTitle(Text("Home Currency"), displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
// search for new base currency
self.changeBaseCurrency()
}) {
Text ("Save").bold()
}
)}
}
}
This is the code I have been using to set the navigation bar area green
struct DrawNavBox: View {
var g: GeometryProxy
var body: some View {
ZStack (alignment: .top) {
Color(.systemGreen)
.frame(height: (g.safeAreaInsets.top), alignment: .top)
.ignoresSafeArea()
}
}
}

Related

how to use bottom sheet transparent background in swiftui like dialog?

I am using the bottom sheet. When the bottom opens, it covers the whole page over the parent. I want to use a bottom sheet like an alert dialog transparent. Thant means parent content not covered whole
here is the image:
The green portion is the content of the bottom sheet. But it covered the whole page.
Here is the code:
calling bottom sheet:
struct CustomDialogSV: View {
#State private var showSheet = false
var body: some View {
Button("Show Sheet") {
self.showSheet = true
}
.sheet(isPresented: $showSheet) {
ZStack {
Color.clear
.edgesIgnoringSafeArea(.all)
CustomAlert()
.background(Color.red)
.cornerRadius(10)
.shadow(radius: 10)
.padding()
// .center()
}
}
}
}
Bottom sheet:
struct CustomAlert: View {
var body: some View {
VStack(alignment: .leading) {
Text("Alert Title")
.font(.headline)
.padding(.bottom)
Text("This is the alert message. It can be any amount of text.")
.padding(.bottom)
HStack {
Button(action: {
// handle action for first button
}) {
Text("First Button")
}
.padding(.trailing)
Button(action: {
// handle action for second button
}) {
Text("Second Button")
}
}
}
.padding()
.background(Color.green)
.cornerRadius(10)
.shadow(radius: 10)
}
}
Is it possible to remove the background without green portions?

VStack space at top and bottom with lazyHGrid

I have this example. Why is there this space at top and bottom of the VStack? The borders of the inner views doesn't seem to be as high as the border of the VStack. I think it has something to do with the LazyHGrid!? Thanks for an answer.
struct ContentView: View{
let names = ["house","creditcard"]
var body: some View{
VStack{
Text("skldfjsjf")
Text("klsadjf")
Text("lksajf")
ScrollView(.horizontal){
LazyHGrid(
rows: [GridItem(.flexible(minimum: 40)),GridItem(.flexible(minimum: 40))],
spacing: 10) {
ForEach(names, id: \.self) { icon in
Image(systemName: icon)
.padding()
}
}
.border(Color.orange)
}
.border(Color.green)
}
.frame(width: UIScreen.main.bounds.width * 0.9)
.border(Color.black)
}
}
I'm new to SwiftUI, but I think you want the content in your VStack to be top hugging?
struct ContentView: View {
let names = ["house","creditcard"]
var body: some View{
VStack (spacing:0) { //THIS HERE FOR NO SPACE FOR VSTACK INTERNAL VIEWS
Text("skldfjsjf")
Text("klsadjf")
Text("lksajf")
ScrollView(.horizontal){
LazyHGrid(
rows: [GridItem(.flexible(minimum: 40)),GridItem(.flexible(minimum: 40))],
spacing: 10) {
ForEach(names, id: \.self) { icon in
Image(systemName: icon)
.padding()
}
}
.border(Color.orange)
}
.border(Color.green)
Spacer() //THIS HERE TO MAKE IT TOP HUGGING
}
.frame(width: UIScreen.main.bounds.width * 0.9)
.border(Color.black)
}
}

SwiftUI: Custom label in navigation link is greyed out

I'm trying to use my custom card view as label to navigation link. this navigation link is also a Grid item.
The result is a
My Card Code:
struct CityCard: View {
var cityData: CityData
var body: some View {
VStack {
Text("\(cityData.name)")
.padding([.top], 10)
Spacer()
Image(systemName: cityData.forecastIcon)
.resizable()
.frame(width: 45, height: 45)
Spacer()
HStack(alignment: .bottom) {
Text(String(format: "%.2f $", cityData.rate))
.padding([.leading, .bottom], 10)
Spacer()
Text(String(format: "%.2f °", cityData.degrees))
.padding([.trailing, .bottom], 10)
}
}.frame(width: 150, height: 150)
.background(Color.blue)
.cornerRadius(10)
.navigationTitle(cityData.name)
}
}
My List View:
struct CityList: View {
var cities: [CityData]
let columns = [
GridItem(.flexible(minimum: 150, maximum: 150)),
GridItem(.flexible(minimum: 150, maximum: 150))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(self.cities) { item in
NavigationLink(destination: Text(item.name), label: {
CityCard(cityData: item)
})
}
}
}
}
}
someone has a solution why it gives me this opacity?
Update:
The main contact view is:
It's greyed out because you don't yet have a NavigationView in your view hierarchy, which makes it possible to actually navigate to a new view.
You can, for example, wrap your ScrollView in a NavigationView:
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(self.cities) { item in
NavigationLink(destination: Text(item.name), label: {
CityCard(cityData: item)
})
}
}
}
}
}
Or, if it's only happening in your preview, add a NavigationView to your preview code:
NavigationView {
CityList(...)
}
Keep in mind that if you have the default accent color (blue, in light mode) that your links will become invisible (since they will be blue) on top of the blue background. You can set a custom accent color in your project or via the accentColor modifier:
NavigationView {
//content
}.accentColor(.black)

Why do the views extend wider than the screen?

Edit: Substitute your "system name:" of choice. "pencil.circle" works fine. "edit" is not a valid SF Symbol.
(I've simplified my code so you can cut and paste. That's why you see .frame, resizable, etc. where much simpler code might your first instinct.)
I have created a view which is a vertical list of row items (table view).
Each row item has a horizontal view with two images inside it.
The images take up too much space and do not fit correctly on the screen:
import SwiftUI
#main
struct StackOverflowDemoApp: App {
var body: some Scene {
WindowGroup {
TandemView()
}
}
}
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(height: 80)
.aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(width: 200, height: 80)
}
.padding()
.fixedSize()
}
}
struct TandemView_Previews: PreviewProvider {
static var previews: some View {
TandemView()
}
}
The above is the closest I can get to the desired layout (it just needs to fit horizontally). I experimented with GeometryReader but that did not produce desired results.
Here are some things I tried:
The code as provided
NoConstraintsOnPencilOrHStack
NoConstraintsOnTandemView
NoConstraintsOnImageInPaddedViewButWithFrameConstraint
I am trying to get a row view which consists of two Images (my actual source consists of UIImage objects) that fits within the width of the screen.
Edit:
After Accepting cedricbahirwe's spot-on response, I was able to simplify the code further. New results:
I added at the top level
TandemView()
.padding(.horizontal)
I removed:
// Spacer()
at the end of PaddedImageView
updated TandemView -- changed both frames and removed 3 lines:
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(width: 80, height: 80)
// .aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(height: 80)
}
// .padding()
// .fixedSize()
}
}
This is happening because of the layout of PaddedImageView View, you can actually remove the Spacer since it is not needed there.
So change
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
to
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
}
}
Note:
SwiftUI Engine infers the layout of your view from the implementation of the body property. It's recommended to have one Parent View inside the body property.

Navigate to new screen on button click SwiftUI

I have a login screen and wish to navigate to a new screen when the Login button is clicked.
I did try to wrap the button and the entire screen layout under NavigationView and embedded the button in a Navigation Link.
I am unable to figure out how to show the new screen when the button is clicked. Following is the code for the login screen.
ZStack {
Color.red
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center, spacing: 180.0) {
Text("SwiftUI")
.font(.largeTitle)
.bold()
.padding()
VStack(alignment: .center, spacing: 25) {
TextField("Username", text: $userName)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
TextField("Password", text: $userPassword)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
Toggle(isOn: $isFirstTimeUser) {
Text("First Time User")
.font(.headline)
.bold()
.padding(.horizontal, -10)
.foregroundColor(Color.white)
}.padding(.horizontal, 17)
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
}
})
{
Text(isFirstTimeUser ? "SignUp" : "Login")
.fontWeight(.medium)
.font(.title)
.foregroundColor(Color.red)
.padding(.horizontal, 10)
}.padding()
.background(Color.white)
.cornerRadius(10)
.alert(isPresented: $isAlertShown) {
() -> Alert in
Alert(title: Text("UserName Invalid"), message: Text("Username has to be more than 5 characters"), dismissButton:.default(Text("Got that!")))
}
}.padding(.horizontal, 17)
}
}
Here is possible approach
1) Add link tag state variable to your View
#State private var current: Int? = nil
2) Wrap your view hierarchy into NavigationView to make possible NavigationLink to work
3) Add tagged NavigationLink above your button
NavigationLink(destination: YourDestinationViewHere(), tag: 1, selection: $current) {
EmptyView()
}
4) Add link tag selection to button action
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
self.current = 1 // this activates NavigationLink with specified tag
}
})