How to show a subview when clicking on a button in SwiftUI - swiftui

I need to show a subview with a button at the bottom of the main view when a button pressed ( this button is in the mainView ) in SwiftUI

You can use a ZStack to accomplish that.
I put together an example where the buttons trigger the appearance of the subview.
Main View:
struct MainView: View {
#State var isPressed = false
var body: some View {
ZStack(alignment: .bottom){
VStack{
Button {
isPressed = true
} label: {
Text("Button MainView")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
if isPressed == true {
VStack{
SubView(isPressed: $isPressed)
}
.frame(maxWidth: .infinity, maxHeight: 100)
}
}
}
}
SubView:
struct SubView: View {
#State var isPressed: Binding<Bool>
var body: some View {
Button {
isPressed.wrappedValue = false
} label: {
Text("Button Subview")
}
}
}
You can replace or extend the button actions and frame sizes as you like.
If you want the button in the MainView to show and hide the SubView, you can use .toggle()
Button {
isPressed.toggle()
} label: {
Text("Button MainView")
}

Related

NavigationLink in SwiftUI not worked as expected

I'm trying to make navigation link, here I'm creating NavigationLink with isActive based on State variable isLoggedIn. But without setting isLoggedIn true getting navigating to next screen.
also, it's navigating on tap of Email Textfield which is wrong.
My expectation is it should navigate only after isLoggedIn setting to true.
struct ContentView: View {
#State private var isLoggedIn = false
#State private var email = ""
var body: some View {
NavigationView {
NavigationLink(destination: Text("Second View"), isActive: $isLoggedIn) {
VStack {
TextField("Email", text: $email)
.frame(maxWidth: .infinity, alignment: .leading)
.border(.gray, width: 1)
.foregroundColor(.blue)
Button("Send") {
isLoggedIn = true
}
}
.padding()
}
}
}
}
The expectation is wrong, NavigationLink handles user input independently (but also, additionally, can be activated programmatically).
In this scenario, to leave only programmatic activation, we need to hide navigation link, like
NavigationView {
VStack {
TextField("Email", text: $email)
.frame(maxWidth: .infinity, alignment: .leading)
.border(.gray, width: 1)
.foregroundColor(.blue)
Button("Send") {
isLoggedIn = true
}
.background(NavigationLink(destination: // << here !!
Text("Second View"), isActive: $isLoggedIn) { EmptyView() })
}
.padding()
}
Here it's working fine with this
struct MoviesListView: View {
#State var navigate = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Hi"), isActive: $navigate) {
Button("Add") {
navigate.toggle()
}
}
}
}
}
}

Show BottomPlayerView above TabView in SwiftUI

I'm learning swiftUI and I want to make a music app.
I created a view which going to be above the tabView, but I want it to be shown only if user start playing a music.
My App, I use ZStack for bottomPlayer, and I share the bottomPlayer variable through .environmentObject(bottomPlayer) so the child views can use it:
class BottomPlayer: ObservableObject {
var show: Bool = false
}
#main
struct MyCurrentApp: App {
var bottomPlayer: BottomPlayer = BottomPlayer()
var audioPlayer = AudioPlayer()
var body: some Scene {
WindowGroup {
ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
TabBar()
if bottomPlayer.show {
BottomPlayerView()
.offset(y: -40)
}
}
.environmentObject(bottomPlayer)
}
}
}
The BottomPlayerView (above the TabView)
struct BottomPlayerView: View {
var body: some View {
HStack {
Image("cover")
.resizable()
.frame(width: 50, height: 50)
VStack(alignment: .leading) {
Text("Artist")
.foregroundColor(.orange)
Text("Song title")
.fontWeight(.bold)
}
Spacer()
Button {
print("button")
} label: {
Image(systemName: "play")
}
.frame(width: 60, height: 60)
}
.frame(maxWidth: .infinity, maxHeight: 60)
.background(Color.white)
.onTapGesture {
print("ontap")
}
}
}
My TabView:
struct TabBar: View {
var body: some View {
TabView {
AudiosTabBarView()
VideosTabBarView()
SearchTabBarView()
}
}
}
And In my SongsView, I use the EnvironmentObject to switch on the bottomPlayerView
struct SongsView: View {
#EnvironmentObject var bottomPlayer: BottomPlayer
var body: some View {
NavigationView {
VStack {
Button {
bottomPlayer.show = true
} label: {
Text("Show Player")
}
}
.listStyle(.plain)
.navigationBarTitle("Audios")
}
}
}
The problem is the bottomPlayer.show is actually set to true, but doesn't appear ...
Where I am wrong?
In your BottomPlayer add theĀ #Published attribute before the show boolean.
This creates a publisher of this type.
apple documentation

How can I remove the toggle button in Swiftui and how can I show a sidebar on the iPad when this is shown high up

I want a sidebar to be displayed on the iPad. However, what bothers me about the Swiftui Navigazion View is that I have this ugly toggle button. Furthermore I would like to show a sidebar when the iPad is held horizontally. Can I change the Navigation View component so that this works?
no, but you can custom build your own:
struct ContentView: View {
#State private var selection: Int? = nil
var body: some View {
HStack {
List {
Button { selection = 1
} label: {
Text("Item 1")
}
Button { selection = 2
} label: {
Text("Item 2")
}
Button { selection = 3
} label: {
Text("Item 3")
}
}
.frame(width: 200)
.frame(maxHeight: .infinity)
.background(.gray.opacity(0.3))
VStack {
if selection != nil {
// Detail View
Text("Your detail view \(selection!)")
.font(.title)
} else {
Text("Select an item")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
}
}
}
However, what bothers me about the Swiftui Navigazion View is that I have this ugly toggle button
The possible workaround to avoid button is to hide navigation bar, then in landscape (aka horizontal) you will see just sidebar
NavigationView {
VStack {
Text("Header")
.padding()
List(0..<100, id: \.self) { i in
NavigationLink(
tag: i,
selection: $activeLink,
destination: { Text("Details for \(i)") }
) {
Text("Row #\(i)")
}
}
}
.navigationBarHidden(true) // << here !!
Text("Default Details")
}

SwiftUI do not push navigation with keyboard with Xcode 12

I made a simple app to illustrate the issue. I have a custom Tab view, however when I click on text field and open the keyboard and my navigation tabs is being pushed up as well. I would like for navigation to be not visible (as one would expect for any app).
This image illustrates the problem:
This issue does not happen if I use native TabView, however I would like to use custom tabs. Does anyone figured out how to deal with it? Providing the code below.
struct ContentView: View {
#State var menu = 0
var body: some View {
VStack {
VStack {
if menu == 0 {
FirstPage()
} else if menu == 1 {
Text("all is good")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
CustomTabs(menu: $menu)
}
}
}
struct CustomTabs: View {
#Binding var menu: Int
var body: some View {
HStack {
Spacer()
Text("Nav1")
.onTapGesture{
self.menu = 0
}
Spacer()
Text("Nav2")
.onTapGesture{
self.menu = 1
}
Spacer()
}
.padding()
.background(Color(.red))
}
}
struct FirstPage: View {
#State var mood: String = ""
var body: some View {
VStack {
Text("How are you?")
TextField("answer textfield", text: $mood)
}
}
}
A possible solution/work-around would be to embed the CustomTabs in a VStack with a Spacer which pushes the tabs down even when the keyboard appears and then put this Stack in front of your view via ZStack.
struct ContentView: View {
#State var menu = 0
var body: some View {
ZStack {
VStack {
if menu == 0 {
FirstPage()
} else if menu == 1 {
Text("all is good")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
VStack {
Spacer(minLength: UIScreen.main.bounds.height - 70)
CustomTabs(menu: $menu)
.frame(height: 70)
}
}
}
}
For simplicity I just decided to set the tabs height to 70. A more beautiful solution would be to use GeometryReader.

How do I dismiss a popUp from within the popUp itself?

I have a button that displays a PopUp when pressed, and on the PopUp is a button that is supposed to dismiss the PopUp itself.
I am unsure as to how to use #Binding variable here (if I am correct in assuming that's what I'm supposed to use to communicate between different structs)
struct TESTSTSTSTS: View {
#State var showPopUp = false
var body: some View {
VStack {
Button(action: {
self.showPopUp = true
}) {
Text("Show PopUp Button")
}
Spacer()
if self.showPopUp == true {
PopUp()
}
}
}
}
struct PopUp: View {
var body: some View {
ZStack {
Color.orange
Button(action: {
//Unsure what code to use here.
}) {
Text("Hide PopUp Button")
}
}.frame(width: 300, height: 500, alignment: .center)
}
}
#Binding is indeed a possibility to solve this.
It works like this:
struct ContentView : View {
#State var showPopUp = false
var body: some View {
VStack {
Button(action: {
self.showPopUp = true
}) {
Text("Show PopUp Button")
}
Spacer()
if self.showPopUp == true {
PopUp(showPopUp: $showPopUp)
}
}
}
}
struct PopUp: View {
#Binding var showPopUp: Bool
var body: some View {
ZStack {
Color.orange
Button(action: {
self.showPopUp.toggle()
}) {
Text("Hide PopUp Button")
}
}.frame(width: 300, height: 500, alignment: .center)
}
}