hiding the navigation bar while scrolling SwiftUI - swiftui

Hello I am new to swiftUI and am making my first app, my current issue is that I am not sure if it is possible to hide my navigation bar while scrolling using the methods I got from a nav bar tutorial I found. I have tried to implement the UINavigationBar stuff but I don't think its at all compatable with what I have so far. I'm pretty sure if I wanted to go that route I would have to rewrite most of what I have to fit that format.
I was hoping that maybe I am missing a step when doing this so I don't have to rewrite what I have
here's my current code
import Foundation
import SwiftUI
struct ContentView: View{
#State var showMenu = false
#State var scoreUp = false
#State var commentP = false
#State var tags = false
#State var sauce = false
var body: some View{
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
let dragOpen = DragGesture()
.onEnded {
if $0.translation.width > 100 {
withAnimation {
self.showMenu = true
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
/// this is the main view for the second page allowing most of the app features
MainView(showMenu: self.$showMenu, scoreUp: self.$scoreUp, commentP : self.$commentP,tags : self.$tags,sauce :self.$sauce)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
.gesture(dragOpen)
if self.showMenu {
HamburgerMenuView(showMenu: self.$showMenu)
.frame(width: geometry.size.width/2, height: geometry.size.height)
.transition(.move(edge: .leading))
.gesture(drag)
}
if self.scoreUp {
// TO DO : MAke this button score up a post
Image(systemName: "arrow.up")
.animation(.interpolatingSpring(stiffness: 50, damping: 1), value: 10)
}
}
}
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
// this is the main view for both the content view and when the menu is displayed
struct MainView: View{
/// shows the hamburger menu when set to true
#Binding var showMenu : Bool
/// SCORES up posts when set to true
#Binding var scoreUp : Bool
/// allows the user to add a comment to the post if set to true
/// move the view up and give the user a text box
#Binding var commentP : Bool
/// allows the user to see the Tags on the image
#Binding var tags : Bool
/// allows the user to see the Source on the image
#Binding var sauce : Bool
var body: some View {
GeometryReader { geometry in
ZStack() {
Image("");
ScrollView {
VStack(spacing: 20) {
VStack(alignment: HorizontalAlignment.leading){
Text("Test")
.bold();
Image("Test")
.resizable()
.scaledToFit()
HStack() {
Button(action : {
withAnimation{
self.scoreUp = true
}
print("Score up")
}) {
Image(systemName: "arrow.up")
.padding(1)
};
Text("1025");
Button(action : {
withAnimation{
self.commentP = true
}
print("comment")
}) {
Image(systemName: "text.bubble")
.padding(1)
};
Button(action : {
withAnimation{
self.tags = true
}
print("Tags")
}) {
Image(systemName: "tag")
.padding(1)
};
Button(action : {
withAnimation{
self.sauce = true
}
print("sauce?")
}) {
Image(systemName: "eyes")
.padding(1)
};
}
Text("Test")
Image("Test")
.resizable()
.scaledToFit();
Text("Test")
Image("Test")
.resizable()
.scaledToFit()
}
Button(action: {
withAnimation {
self.showMenu = true
}
print("Open the side menu")
}) {
Text("Show Menu");
}
.hidden()
}
}
}
}
}
}
/// this is the hamburger menu itself
struct HamburgerMenuView : View {
#Binding var showMenu : Bool
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Profile")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "gear")
.foregroundColor(.gray)
.imageScale(.large)
Text("Settings")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Favorites")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Posts")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Comments")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Artists")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Tags")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("More")
.foregroundColor(.gray)
.font(.headline)
.padding(10)
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
Spacer()
}
}
//// TO DO
//class underImageBar {
//
//
//HStack() {
//Button(action : {
// withAnimation{
// self.scoreUp = true
//}
// print("Score up")
// }) {
// Image(systemName: "arrow.up")
// .padding()
//
//
// };
//Button(action : {
// withAnimation{
// self.commentP = true
//}
// print("comment")
// }) {
// Image(systemName: "text.bubble")
// .padding()
//
//
// };
//Button(action : {
// withAnimation{
// self.tags = true
//}
// print("Tags")
// }) {
// Image(systemName: "tag")
// .padding()
// };
//Button(action : {
// withAnimation{
// self.sauce = true
// }
// print("sauce?")
// }) {
// Image(systemName: "eyes")
// .padding()
//
// };
// }
//}
/// this previews the code
struct menuView: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.toolbar {
}
}
}
}
this was supposed to be a gif stack overflow just made it a jpeg lol?

There is a view modifier for that.
.navigationBarHidden(true)
I hope this helps out.

Related

move content by scroll when overlay is presented

I created some MRE:
struct ContentView: View {
#State private var presentOverlay: Bool = false
var body: some View {
GeometryReader { geo in
List {
Section {
VStack(alignment: .center) {
Spacer()
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
.onTapGesture {
presentOverlay.toggle()
}
Text("Hello, world!")
}
.frame(height: geo.size.height * 0.8)
} header: {
Text("Header")
} footer: {
Text("Footer")
}
}
.overlay(alignment: .bottom) {
if presentOverlay {
Text("Some overlay")
.frame(width: geo.size.width, height: geo.size.height * 0.2)
.background(.yellow)
.transition(.move(edge: .bottom))
}
}
}
.animation(.default, value: presentOverlay)
}
}
Goal is to, when overlay appears, move content of list, so Image can be presented and clickable (currently overlay is going above it).
Is it possible to move content like it is scrolled using List component?

ScrollViewReader is not working and building is pretty slow

I couldn't figure out why it is not working my ScrollViewReader below my code. Please help me figure out the issue. I want to pass my zolyric.number into scrollToIndex and that scrollToIndex will set the number that I want to scroll in my HymnLyrics(). Also, although I couldn't find the error, the building is pretty slow.
Here ZolaiTitles() is the same list just sorting the song titles algebraically and HymnLyrics() is the one to display in the canvas.
struct ZolaiTitles: View {
#AppStorage("scrollToIndex") var scrollToIndex: Int?
#EnvironmentObject var tappingSwitches: TapToggle
let zoLyrics: [Lyric] = LyricList.hymnLa.sorted { lhs, rhs in
return lhs.zoTitle < rhs.zoTitle
}
var body: some View {
ScrollView {
ForEach(zoLyrics, id: \.id) { zoLyric in
VStack {
Button(action: {
//if let lyricNum = String(zoLyric) {
scrollToIndex = zoLyric.number // zolyric.number is already an interger.
//}
self.tappingSwitches.isHymnTapped.toggle()
}, label: {
HStack {
Text(zoLyric.zoTitle)
.foregroundColor(Color("bTextColor"))
.lineLimit(1)
.minimumScaleFactor(0.5)
Spacer()
Text("\(zoLyric.number)")
.foregroundColor(Color("bTextColor"))
}
})
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding([.leading, .bottom, .trailing])
}
}
}
}
struct HymnLyrics: View {
#AppStorage("scrollToIndex") var scrollToIndex: Int = 1
var lyrics: [Lyric] = LyricList.hymnLa
#AppStorage("fontSizeIndex") var fontSizeIndex = Int("Medium") ?? 18
#AppStorage("fontIndex") var fontIndex: String = ""
#AppStorage("showHVNumbers") var showHVNumbers: Bool = true
var body: some View {
ScrollViewReader { proxy in
List(lyrics, id: \.id) { lyric in
VStack(alignment: .center, spacing: 0) {
Text("\(lyric.number)")
.padding()
.multilineTextAlignment(/*#START_MENU_TOKEN#*/.leading/*#END_MENU_TOKEN#*/)
.id(lyric.number)
VStack {
VStack {
Text(lyric.zoTitle)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)
.autocapitalization(.allCharacters)
Text(lyric.engTitle)
.font(.title3)
.fontWeight(.medium)
.foregroundColor(.secondary)
.italic()
}
// Don't use Padding here!
}
.multilineTextAlignment(.center)
HStack {
Text(lyric.key)
.italic()
Spacer()
Text(lyric.musicStyle)
}
.foregroundColor(.blue)
.padding(.vertical)
}
//.id(lyric.number)
}
.onChange(of: scrollToIndex, perform: { value in
proxy.scrollTo(value, anchor: .top)
})
}
}
}

SwiftUI: Show specific views with an animation delay

I'm trying to achieve the following animation: When I tap on a rectangle the rectangle should be expanded to the full width with an close button in the corner and below this rectangle a ScrollView should appear. This works so far without any problems. Now I would like to display the ScrollView a little bit later then the expanded rectangle. So when I tap on the rectangle: First the expanded rectangle with the close button should appear and 3 seconds later the ScrollView.
struct Playground: View {
#Namespace var namespace
#State var show = false
private let gridItems = [GridItem(.flexible())]
var body: some View {
if show {
VStack{
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)){
Rectangle()
.matchedGeometryEffect(id: "A", in: namespace, isSource: show)
.frame(height: 300)
.frame(maxWidth: .infinity)
Image(systemName: "xmark")
.font(.system(size: 25))
.foregroundColor(.white)
.background(Color.red)
.padding(20)
.onTapGesture {
withAnimation(.spring()){
self.show = false
}
}
}
// SHOW THIS SCROLLVIEW 3 SECONDS LATER
ScrollView{
LazyVGrid(columns: gridItems){
ForEach(0..<10){ cell in
Text("\(cell)")
}
}
}
.animation(Animation.spring().delay(3)) // doesn't work!
}
} else {
Rectangle()
.matchedGeometryEffect(id: "A", in: namespace, isSource: !show)
.frame(width: 100, height: 100)
.onTapGesture {
withAnimation(.spring()){
self.show = true
}
}
}
}
}
We need to make separated animation (and related state) for ScrollView in this scenario.
Here is possible approach. Tested with Xcode 12.1 / iOS 14.1
struct Playground: View {
#Namespace var namespace
#State var show = false
private let gridItems = [GridItem(.flexible())]
#State private var showItems = false
var body: some View {
if show {
VStack{
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)){
Rectangle()
.matchedGeometryEffect(id: "A", in: namespace, isSource: show)
.frame(height: 300)
.frame(maxWidth: .infinity)
Image(systemName: "xmark")
.font(.system(size: 25))
.foregroundColor(.white)
.background(Color.red)
.padding(20)
.onTapGesture {
withAnimation(.spring()){
self.show = false
}
}
}
VStack {
if showItems {
ScrollView{
LazyVGrid(columns: gridItems){
ForEach(0..<10){ cell in
Text("\(cell)")
}
}
}
} else {
Spacer()
}
}
.onAppear { showItems = true }
.onDisappear { showItems = false }
.animation(Animation.spring().delay(3), value: showItems)
}
} else {
Rectangle()
.matchedGeometryEffect(id: "A", in: namespace, isSource: !show)
.frame(width: 100, height: 100)
.onTapGesture {
withAnimation(.spring()){
self.show = true
}
}
}
}
}

self.isLinkActive = true will not load the model view

Why can SwiftUI not just load the view it shouldn't b e this hard to do something that could be done in UIKit in one stupid line. the button should simply load what its told to load in this case VideoList(viewModel: .init()) there should be no problem.
import Foundation
import SwiftUI
struct SwiftUIView: View {
#State private var isLinkActive = false
let layout = [
GridItem(.adaptive(minimum: 150))
]
var body: some View {
///Top Bar layout
ZStack {
HStack {
Button(action: {
}, label: {
Image("Home")
.resizable()
.frame(width: CGFloat(30), height: CGFloat(30), alignment: .leading)
}).padding(.leading, CGFloat(20))
.padding(.top, 15)
Spacer()
}
HStack {
Text("app")
.frame(alignment: .center)
.font(.title3)
.foregroundColor(Color("Menu"))
.multilineTextAlignment(.center)
.padding(.top, 15)
}
}
///Setup app FEED LINE
HStack{
Text("app FEED")
.padding(.leading, 20.0)
.padding(.top, 100.0)
.font(.largeTitle)
.foregroundColor(Color("Menu"))
Spacer()
}
///Setup Welcom messages
HStack{
Text("Good morning")
.padding(.bottom, 20)
.padding(.leading, 20.0)
.font(.subheadline)
.foregroundColor(Color("Menu"))
Spacer()
Text("...")
.foregroundColor(/*#START_MENU_TOKEN#*/.blue/*#END_MENU_TOKEN#*/)
.font(.largeTitle)
.padding(.trailing, 20.0)
.padding(.bottom, 20)
}
/// Setup navigation home
ScrollView {
LazyVGrid(columns: layout, spacing: 5) {
Button(action: {
print("Button action")
}) {
Image("app")
}
.padding(.vertical, 2.5)
.padding(.horizontal, 2.5)
Button(action: {
print("Button action")
}) {
Image("app")
}
.padding(.vertical, 2.5)
.padding(.horizontal, 2.5)
Button(action: {
self.isLinkActive = true
}) {
Image("app")
}
.padding(.vertical, 2.5)
.padding(.horizontal, 2.5)
Button(action: {
print("Button action")
}) {
Image("app")
}
Button(action: {
print("Button action")
}) {
Image("app")
}
.padding(.vertical, 2.5)
.padding(.horizontal, 2.5)
Button(action: {
print("Button action")
}) {
Image("app")
}
//Add correct images Direction and Contact us
Button(action: {
print("Button action")
}) {
Image("app")
}
Button(action: {
print("Button action")
}) {
Image("app")
}
.padding(.vertical, 2.5)
.padding(.horizontal, 2.5)
}
}
.padding(.horizontal, 2.5)
.padding(.vertical, 2.5)
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
.background(
NavigationLink(destination: VideoList(viewModel: .init()), isActive: $isLinkActive) {
EmptyView()
}
.hidden()
)
}
}
struct VideoList: View {
#Environment(\.presentationMode) private var presentationMode
#ObservedObject private(set) var viewModel: ViewModel
#State private var isRefreshing = false
var body: some View {
NavigationView {
List(viewModel.videos.sorted { $0.id > $1.id}, id: \.id) { video in
NavigationLink(
destination: VideoDetails(viewModel: VideoDetails.ViewModel(video: video))) {
VideoRow(video: video)
}
}
.onPullToRefresh(isRefreshing: $isRefreshing, perform: {
self.viewModel.fetchVideos()
})
.onReceive(viewModel.$videos, perform: { _ in
self.isRefreshing = false
})
.navigationBarTitle(viewModel.navigationBarTitle)
}
.onAppear(perform: viewModel.fetchVideos)
}
}
#if DEBUG
struct VideoList_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
VideoList(viewModel: .init())
}
}
}
#endif
The button on line 109 does not load the VideoList(viewModel: .init()) ViewModel when requested to do so.
NavigationView {} was forgotten however the UI is still messed up currently
and adding an HStack fixed.... ugh hope this helps the next guy not have to spend 3 days on it

Using SwiftUI. My Slider/Side-menu launches new Views just fine when clicked but click <back> button and now all the options are 'dead'

Using SwiftUI and a slider/side menu tutorial that I have augmented in order to put actions on each of the side menu selections.
When the side menu is displayed and I tap a menu option, it works great and takes me to a new view with a menu item. But when i tap on and see the side menu still in place, all the menu items are not dead. The menu items still animate a click (with a flicker) but nothing happens. I have to close the side menu, reopen it, and then the menu items work once again - one time.
Can anyone tell me why this is happening?
Here is the pretty contentview, the mainview, and the sidemenu view.
//ContentView.swift
import SwiftUI
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView()
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag)
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here is the sidemenu view.
//MenuView.swift
import SwiftUI
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#State var showMenu = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}
struct MenuView_Previews: PreviewProvider {
static var previews: some View {
MenuView()
}
}
enter code here
1) Binding var in the MenuView
2) OnAppear{} with Zstack to turn off the showMenu
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView(showMenu: self.$showMenu)
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag).onAppear {
self.showMenu = false
}
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#Binding var showMenu: Bool // = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}