NavigationLink won't transition to new View upon selecting custom Button - swift

When I embed my Button inside a NavigationLink and I set the destination to a Text View, the view doesn't transition upon clicking the button, although it sometimes does transition if I just randomly click around the button. What am I doing wrong?
Here is what I'm working with...
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack{
Spacer()
VStack(spacing: 50){
NavigationLink(destination: Text("Testing")){
awButton(content: "Request Support", backColor: Color(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)))
}
}
Spacer()
}
.navigationBarTitle(Text("AccessWeb"))
.navigationBarItems(trailing: HStack{
Image(systemName: "bell")
.font(.system(size: 30))
Image(systemName:"person.circle")
.font(.system(size: 30))
Text("Tester")
.font(.system(size: 20))
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct awButton: View {
var content : String
var backColor : Color
var body: some View {
Button(action: {}, label: {
VStack {
HStack {
Image(uiImage: #imageLiteral(resourceName: "awText"))
.resizable()
.frame(width: 30, height: 20)
.padding(.leading)
Spacer()
}
.padding(.top)
HStack {
Text("\(content)")
.font(.title)
.fontWeight(.semibold)
.offset(y: 10.0)
}
Spacer()
}
})
.foregroundColor(Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)))
.frame(width: 300, height: 150, alignment: .center)
.background(backColor)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}

The NavigationLink wraps everything in a button already (or at least something that receives click/tap events), so by also including a Button in your custom view, you're preventing those tap events from getting to the original NavigationLink.
But, since your action is empty, you can just take out your Button wrapper:
struct awButton: View {
var content : String
var backColor : Color
var body: some View {
VStack {
HStack {
Image(systemName: "pencil")
.resizable()
.frame(width: 30, height: 20)
.padding(.leading)
Spacer()
}
.padding(.top)
HStack {
Text("\(content)")
.font(.title)
.fontWeight(.semibold)
.offset(y: 10.0)
}
Spacer()
}
.foregroundColor(Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)))
.frame(width: 300, height: 150, alignment: .center)
.background(backColor)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
}
}

Related

Can't display custom pop-up because my tab view is in NavigationView

I want to display a pop-up in my view, where I will be able to display a menu where I can choose how I fell, then it will show what I have chosen and close itself. If I am adding it to the view and it's presenting It shows wrong and Tab Bar does not disappear. Can someone provide a better way to show a pop-up over Tab Bar? The logic is something like this: button pressed -> shows pop-up -> choose status -> shows another pop-up -> disappears.
Code's provided below:
// Smile face that used in question
struct SmileFace: View {
var text: String
var image: String
#Binding var current: String
var body: some View {
Button {
withAnimation {
current = text
}
} label: {
VStack {
Image(image)
.resizable()
.scaledToFill()
.frame(width: 46, height: 46)
Text(text)
.foregroundColor(.white)
}
}
}
}
// Check in answer view
import SwiftUI
struct CheckInAskView: View {
let didClose: () -> Void
var didChose: Bool = false
#State var current: String
var emotions = [
"GREAT" : "good",
"GOOD" : "happy",
"OK" : "moderate",
"BAD" : "sad",
"TERRIBLE" : "verysad"
]
var body: some View {
VStack {
ZStack(alignment: .topLeading) {
Rectangle()
.fill(Color("navigation"))
.cornerRadius(15)
.frame(height: 601)
HStack(alignment: .top) {
ZStack(alignment: .top) {
Circle()
.fill(Color(red: 0.682, green: 0.384, blue: 0.486).opacity(0.10))
.frame(width: 173)
.offset(x: -173/2, y: -90/2)
.clipped()
}
Spacer()
Button {
didClose()
} label: {
ZStack {
Circle()
.fill(Color(red: 0.933, green: 0.933, blue: 0.933).opacity(0.30))
.frame(width: 24)
.clipped()
Image(systemName: "xmark")
.font(.system(size: 15))
.foregroundColor(Color(red: 0.762, green: 0.762, blue: 0.762))
}
.padding(10)
}
}
VStack(alignment: .center) {
Spacer()
Text("How do you feel now?")
.foregroundColor(.white)
.font(.custom("Manrope-Bold", size: 16))
HStack(spacing: 35) {
SmileFace(text: "GOOD", image: "good", current: $current)
}
.padding(.horizontal)
DoubleTextView(topText: "Recommendation for you", buttomText: "We have selected courses based on your goals and \nexperience", topTextSize: 16, buttomTextSize: 14)
BigFrameScrollViewHorizontal()
Spacer()
}
}
.frame(height: 601)
}
.frame(height: 601)
.transition(.move(edge: .bottom))
}
}
// View with all views
struct CheckInView: View {
#StateObject var sheetManager: SheetManager
var body: some View {
VStack {
HStack {
Text("How do you fell now?")
.foregroundColor(.white)
.font(.custom("Manrope-Bold", size: 16))
Spacer()
Button {
} label: {
ZStack {
HStack {
Text("Pass check in")
.padding([.top, .leading, .bottom])
.foregroundColor(.white)
.font(.custom("Manrope-Medium", size: 12))
Image(systemName: "chevron.right")
.foregroundColor(.white)
.font(.system(size: 15))
.padding(.trailing)
}
}
.background(Rectangle()
.fill(Color("active"))
.cornerRadius(100)
.frame(height: 26))
}
}
VStack {
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
Divider()
.background(Color("inactive"))
.padding(.vertical)
}
HStack {
Button {
} label: {
ZStack {
Circle()
.fill(Color("navigation"))
.frame(width: 26)
Image(systemName: "chevron.left")
.foregroundColor(.white)
.font(.system(size: 14))
}
}
Button {
} label: {
ZStack {
Circle()
.fill(Color("navigation"))
.frame(width: 26)
Image(systemName: "chevron.right")
.foregroundColor(Color("inactive"))
.font(.system(size: 14))
}
}
}
.padding(.top)
}
.padding(.horizontal, 15.0)
}
}
struct CheckInView_Previews: PreviewProvider {
static var previews: some View {
CheckInView(sheetManager: SheetManager())
.background(.yellow)
}
}
If I have all of this, how can I create a pop-up over Tab View? Maybe a link to a similar problem?

Only have one button active in dynamic list | SwiftUI

I have a list generated from a ForEach loop:
struct TrainingList: View {
#EnvironmentObject var trainingVM: TrainingViewModel
var body: some View {
VStack(alignment: .leading) {
Text("Your training sessions")
.font(.system(size: 35, weight: .semibold, design: .default))
.padding(.all, 10)
.foregroundColor(.white)
Divider()
ScrollView{
if(trainingVM.loading){
ProgressView("Loading training session").progressViewStyle(CircularProgressViewStyle(tint: .blue))
}
LazyVStack {
ForEach(trainingVM.trainingList) { training in
TrainingCell(training: training)
}
}
}
Spacer()
}
.background {
Rectangle()
.fill(Color(.sRGB, red: 41/255, green: 41/255, blue: 41/255))
.cornerRadius(10, corners: [.topRight, .bottomRight])
.shadow(color: .black.opacity(1), radius: 8, x: 6, y: 0)
}
.frame(width: 650)
.zIndex(.infinity)
}
}
Each TrainingCell has a button that opens an extra panel on the side of it. To indicate which row has the panel opened the button changes its styling:
struct TrainingCell: View {
#EnvironmentObject var trainingVM: TrainingViewModel
#State var showEvents = false
let training: Training
var body: some View {
HStack(spacing: 0) {
ZStack(alignment: .top) {
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(Color(.sRGB, red: 41/255, green: 41/255, blue: 41/255))
VStack {
HStack(spacing: 10) {
VStack(alignment: .leading, spacing: 5) {
HStack{
Text(training.readableDate, style: .date)
.font(.system(size: 25, weight: .semibold, design: .default))
.foregroundColor(.white)
Text(" | ")
Text(training.readableDate, style: .time)
.font(.system(size: 25, weight: .semibold, design: .default))
.foregroundColor(.white)
}
VStack(alignment: .leading,spacing: 5){
HStack {
HStack(spacing: 5) {
Image(systemName: "person.text.rectangle.fill")
.foregroundColor(Color(.sRGB, red: 10/255, green: 90/255, blue: 254/255))
Text(training.instructor.fullName)
.foregroundColor(.white)
}
}
HStack{
ForEach(training.students){ student in
HStack(spacing: 5) {
Image(systemName: "person")
.imageScale(.medium)
.foregroundColor(Color(.sRGB, red: 10/255, green: 90/255, blue: 254/255))
Text(student.fullName_shortenedFirstName)
.foregroundColor(.white)
}
}
}
}
.font(.system(size: 20, weight: .regular, design: .default))
.foregroundColor(.primary)
}
.frame(maxHeight: .infinity, alignment: .center)
.clipped()
Spacer()
View_Close_Button(showEvents: $showEvents)
}
.frame(maxWidth: .infinity, maxHeight: 80, alignment: .top)
.padding(.all)
.background {
RoundedRectangle(cornerRadius: 0, style: .continuous)
.fill(Color(.sRGB, red: 41/255, green: 44/255, blue: 49/255))
.shadow(color: .black.opacity(1), radius: 5, x: 0, y: 5)
}
}
}
}
}
}
The button code:
struct View_Close_Button: View {
#EnvironmentObject var trainingVM: TrainingViewModel
#Binding var showEvents: Bool
var body: some View {
HStack
{
Image(systemName: showEvents ? "xmark" : "eye")
.imageScale(.large)
.padding(.horizontal, 5)
.font(.system(size: 17, weight: .regular, design: .default))
Text(showEvents ? "Close" : "View")
.padding(.all, 10)
.font(.system(size: 25, weight: .regular, design: .default))
.multilineTextAlignment(.leading)
}
.onTapGesture {
withAnimation(.easeIn(duration: 0.3)) {
showEvents.toggle()
if(showEvents) {
trainingVM.showingEvents = true
}else{
trainingVM.showingEvents = false
}
}
}
.foregroundColor(.white)
.background {
Capsule(style: .continuous)
.foregroundColor(showEvents ? Color(.sRGB, red: 253/255, green: 77/255, blue: 77/255) : Color(.sRGB, red: 10/255, green: 90/255, blue: 254/255))
.clipped()
.frame(maxWidth: 180)
}
}
}
Which should result in this:
The only problem I have is that all button can be activated at the same time. How would I go about disabling the rest of the button when one is tapped?
I need the user to only be able to have on of the button displayed as "X Close"
I tought about looping trough other buttons to deactivate them programatically before activating the one that was tapped but I have no clue how
You could keep track of the activated button in the parent view.
If you have some kind of unique identifier per button you could make a variable in the parent view that contains the active identifier.
You can pass that variable as a binding into the button views and depending on that you can change the views appearance.
This way there is always just one active button. When a button is clicked you can set the value of the binding variable in the button view with the unique identifier of this button and the other views change automatically.
On the TrainingList you can define a variable with the active tab:
#State var activeTab: Int = 0
On TrainingCell you can add this variable as a binding.
#Binding var activeTab: Int
And you pass it like:
TrainingCell(training: training, activeTab: $activeTab)
Then on View_Close_Button you can add two variables:
#Binding var activeTab: Int
#State var training: Training
And pass it like this on the TrainingCell:
View_Close_Button(showEvents: $showEvents, activeTab: $activeTab, training: training)
In the View_Close_Button you can use this to get the value and set the styles accordingly:
Image(systemName: activeTab == training.id ? "xmark" : "eye")
Text(activeTab == training.id ? "Close" : "View")
And you can set it when the button it tapped:
.onTapGesture {
withAnimation(.easeIn(duration: 0.3) {
activeTab = training.id
}
}

NavigationView doesn't show up correctly in ScrollView

I have encountered this problem during UI building. Previously, I've been asking here about NavigationView and why NavigationLink wasn't working. I fixed this problem with NavigationView at the top of my ScrollView. Now FrameView works just fine: opens and closes it in preview, which means I'm doing it right.
Here's the catch: When I add this LongFrameScrollView to my main view HomeView, it appears in some sort of frame and doesn't open link in full screen.
What am I doing wrong? How to fix that? Providing the code:
// Long frame view
import SwiftUI
struct LongFrameView: View {
var body: some View {
NavigationLink {
PlayerView()
} label: {
ZStack {
Rectangle()
.fill(LinearGradient(gradient: Gradient(colors: [Color(red: 0.268, green: 0.376, blue: 0.587), Color(red: 0.139, green: 0.267, blue: 0.517)]),
startPoint: .leading,
endPoint: .trailing))
.frame(width: 310, height: 62)
.cornerRadius(8)
HStack {
Image("mountains")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 62)
.cornerRadius(8, corners: [.topLeft, .bottomLeft])
VStack(alignment: .leading) {
Text("Sense of anxiety")
.font(.custom("Manrope-Bold", size: 14))
.foregroundColor(.white)
Text("11 MIN")
.font(.custom("Manrope-Medium", size: 12))
.foregroundColor(.white)
}
Spacer()
}
}
.frame(width: 310, height: 62)
}
}
}
// Scroll View with LongFrame. Works fine in the preview
struct LongFrameScrollView: View {
let rows = Array(repeating: GridItem(.fixed(70), spacing: 10, alignment: .leading), count: 2)
var body: some View {
NavigationView {
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: rows, spacing: 10) {
// PLACEHOLDER UNTIL SERVER IS READY
LongFrameView()
LongFrameView()
LongFrameView()
LongFrameView()
}
}
.padding([.horizontal, .bottom], 10)
}
}
}
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center) {
///
// Long frame. There is more view, but we'll just ignore them
LongFrameScrollView()
.padding(.bottom)
///
}
.padding(.top, 10)
}
.background(
Image("background2")
.resizable()
.aspectRatio(contentMode: .fill)
)
}
}
The reason why it wasn't working correctly is because it was in TabView. So, in order to work, our TabView must be in NavigationView and we must delete from all child views NavigationView.

How do I add Navigation Buttons to SwiftUI app?

I'm very new to Swift and SwiftUI in Xcode and have been working on an app.
I'm trying to have three buttons at the top of the main screen: HOME, ABOUT, HELP.
They all should bring the user to different views (or screens) when tapped on.
I already have the basis of the app created: the background, image, text, text field, and button (will set up for action at a later time). I'm wondering how I can add these buttons to the top of the app on top of my ZStack (which includes a VStack and an HStack) for the background. Here is my code so far:
import SwiftUI
struct ContentView: View {
#State private var accession: String = ""
var body: some View {
ZStack {
// Background navy blue
Rectangle()
.foregroundColor(Color(red: 10/255, green: 77/255, blue: 174/255)).edgesIgnoringSafeArea(.all)
// Background light blue
Rectangle()
.foregroundColor(Color(red: 86/255, green: 118/255, blue: 255/255))
.frame(height: 200)
.offset(x: 0, y: -200)
VStack {
// Image of Logo
Logo()
.offset(x: 0, y: -98)
// Text for user input
Text("Please enter the gene accession number below")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.white)
.offset(x: -9, y: -10)
// Text field for user input Ω£
TextField("NG_017013", text: $accession)
.textFieldStyle(RoundedBorderTextFieldStyle())
.offset(x: 0, y: -8)
// Button
Button(action: {
// ParseXML()
}) {
Text("Find my gene! 🧬")
.font(.system(size: 23))
.foregroundColor(Color.white)
.bold()
.padding(.all, 10)
.background(Color(red: 86/255, green: 118/255, blue: 255/255))
.cornerRadius(20)
.offset(x: 0, y: 20)
}.padding()
}.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can wrap the ZStack in a NavigationView.
At the bottom inside the NavigationView add...
.navigationBarItems(
leading:
HStack {
NavigationLink(destination: Text("Home View")) {
Text("Home")
}
NavigationLink(destination: Text("About View")) {
Text("About")
}
},
trailing:
NavigationLink(destination: Text("Help View")) {
Text("Help")
}
)

Align rectangle to be within certain distance of safe area bottom edge SWIFTUI

I am used to Interface Builder and layout constraints but now I want to convert my app to Swift UI. What I am trying to do right now is align the top edge of the view marked with a 1 to be within a certain distance of the safe area bottom edge (marked with a 2) so that the top edge that is now at 1 will then be at position 3. I tried using spacers but then it will look different on smaller devices such as an iPhone 8. In IB I could have used a simple layout constraint. How does this work in Swift UI? I have attached the relevant code and an image. Thank you for your help.
struct ContentView: View {
init() {
UINavigationBar.appearance().backgroundColor = .orange
}
var body: some View {
NavigationView {
VStack{
Spacer()
ZStack{
Rectangle()
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
Image("")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 150)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
}.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width, alignment: .center)
Spacer(minLength: 100)
ZStack(alignment: .bottom){
ExtractedView()
.edgesIgnoringSafeArea(.bottom)
}
}
.navigationBarTitle(Text("You See"))
.navigationBarHidden(false)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice("iPhone")
}
}
struct ExtractedView: View {
#State private var name: String = ""
var body: some View {
VStack{
ZStack(alignment: .top){
VStack{
RoundedRectangle(cornerRadius: 50)
.frame(width: 60, height: 7)
.padding(.top)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Start advertising")
.font(.headline)
.foregroundColor(Color.black)
}.padding(.top)
TextField("Test", text: $name)
.padding(.all)
.background(Color.white.cornerRadius(20))
.padding()
}
RoundedRectangle(cornerRadius: 30)
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
.zIndex(-5)
}
}
}
}
Ok, so I was able to solve my problem. The trick is to create two VStacks with different frame alignments. The outer VStack has top alignment so that the Discover view can be at the top. The inner VStack has a bottom alignment so that the sheet can be pulled up from the bottom. Space will be filled from the bottom up in this case.
VStack{
Discover()
.padding(.top, 60.0)
VStack{
Text("Recent Messages:")
.font(.headline)
DetailSheet()
.offset(x: 0, y: 0)
}.frame(width: g.size.width, height: (g.size.height - g.size.width - 80 + 200), alignment: .bottom)
}
.frame(width: g.size.width, height: g.size.height, alignment: .top)
you can try this:
offset -10 is just your offset you want to have....
i hope i understood you right, i am not so sure...
var body: some View {
VStack{
// ZStack(alignment: .top){
VStack{
RoundedRectangle(cornerRadius: 50)
.frame(width: 60, height: 7)
.padding(.top)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Start advertising")
.font(.headline)
.foregroundColor(Color.black)
}.padding(.top)
TextField("Test", text: $name)
.padding(.all)
.background(Color.white.cornerRadius(20))
.padding()
}.background(RoundedRectangle(cornerRadius: 30)
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
).offset(y:-10)
}
// }