Why does my custom tab bar get pushed up everytime the on screen keyboard pops up? - swift

I've made a custom tab bar and I'm trying to add a search bar on one of my views. For some reason half of the tab gets pushed up when the onscreen keyboard appears. Ive tried " .ignoresSafeArea(.keyboard)" literally everywhere and the only thing that happens is my icons for the tab disappear but the top of the tab still stays there. I've been trying to fix it for the last 24 hours but I'm getting nowhere, can somebody please help me with this. Thanks!
Code for Custom Tab Bar:
import SwiftUI
enum Tabs: Int {
case home = 0
case hot = 1
case favourites = 2
case settings = 3
}
struct TabBar: View {
#Binding var selectedTab : Tabs
var body: some View {
HStack (alignment: .center){
Button {
//switch to home
selectedTab = .home
} label: {
GeometryReader { geo in
if selectedTab == .home {
Rectangle()
// .ignoresSafeArea(.keyboard)
.foregroundColor(.red)
.frame(width: geo.size.width/2, height: 5)
.padding(.leading, geo.size.width/4)
}
VStack (alignment: .center){
Image(systemName: "house")
// .ignoresSafeArea(.keyboard)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.padding(.top, 40.0)
}
// .ignoresSafeArea(.keyboard)
.frame(width: geo.size.width, height: geo.size.height)
}
//.ignoresSafeArea(.keyboard)
.offset(y: -35)
//.ignoresSafeArea(.keyboard)
}
// .ignoresSafeArea(.keyboard)
.tint(Color.black)
Button {
//Switch views
selectedTab = .hot
} label: {
GeometryReader { geo in
if selectedTab == .hot {
Rectangle()
//.ignoresSafeArea(.keyboard)
.foregroundColor(.red)
.frame(width: geo.size.width/2, height: 5)
.padding(.leading, geo.size.width/4)
}
VStack (alignment: .center){
Image(systemName: "flame")
//.ignoresSafeArea(.keyboard)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.padding(.top, 40.0)
}
//.ignoresSafeArea(.keyboard)
.frame(width: geo.size.width, height: geo.size.height)
}
//.ignoresSafeArea(.keyboard)
.offset(y: -35)
}
//.ignoresSafeArea(.keyboard)
.tint(Color.black)
Button {
//Switch views
selectedTab = .favourites
} label: {
GeometryReader { geo in
if selectedTab == .favourites {
Rectangle()
//.ignoresSafeArea(.keyboard)
.foregroundColor(.red)
.frame(width: geo.size.width/2, height: 5)
.padding(.leading, geo.size.width/4)
}
VStack (alignment: .center){
Image(systemName: "star")
//.ignoresSafeArea(.keyboard)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.padding(.top, 40.0)
}//.ignoresSafeArea(.keyboard)
.frame(width: geo.size.width, height: geo.size.height)
}
//.ignoresSafeArea(.keyboard)
.offset(y: -35)
}
//.ignoresSafeArea(.keyboard)
.tint(Color.black)
Button {
//Switch views
selectedTab = .settings
} label: {
GeometryReader { geo in
if selectedTab == .settings {
Rectangle()
//.ignoresSafeArea(.keyboard)
.foregroundColor(.red)
.frame(width: geo.size.width/2, height: 5)
.padding(.leading, geo.size.width/4)
}
VStack (alignment: .center){
Image(systemName: "gear")
//.ignoresSafeArea(.keyboard)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.padding(.top, 40.0)
}//.ignoresSafeArea(.keyboard)
.frame(width: geo.size.width, height: geo.size.height)
}
//.ignoresSafeArea(.keyboard)
.offset(y: -35)
}
//.ignoresSafeArea(.keyboard)
.tint(Color.black)
}
//.ignoresSafeArea(.keyboard, edges: .all)
.frame(height: 20)
}
}
struct TabBar_Previews: PreviewProvider {
static var previews: some View {
TabBar(selectedTab: .constant(.home))
.ignoresSafeArea(.keyboard)
}
}
ignoresSafeArea is commented in all the places I tried to put it.
Here is also the code for the search bar, maybe I need to input ignoreSafeArea somewhere here?
import SwiftUI
struct SearchBarView: View {
#StateObject var im = SearchBarContents()
//#State var selectedTabs: SearchB = .search
#State private var query = ""
var body: some View {
NavigationView {
List {
ForEach(im.filteredData) { item in
HStack{
NavigationLink(destination: ItemView(item: item))
{
SBarView(item: item)
}
}
}
}
.navigationTitle("Items")
.searchable(text: $query,
placement: .navigationBarDrawer(displayMode: .always),
prompt: "Find an Item") {
}
.onSubmit(of: .search) {
im.search(with: query)
}
.onChange(of: query) { newQuery in
im.search(with: newQuery)
}
.onAppear {
im.search()
}
}
}
}
struct SearchBarView_previews: PreviewProvider {
static var previews: some View {
SearchBarView()
}
}
If there is anything missing please let me know.
Imgur link to see exactly what I mean
https://imgur.com/fv79bKh
https://imgur.com/a/Rx9Ki6c (after I add ignoresafearea)

Depends on how you have set up your project. If you have a RootView which is controlling the View to be displayed based on the selection of your Tab Bar and is also the location in the project that you would add the Custom Tab Bar, then on that root View add the modifier .ignoresSafeArea(.keyboard)
For example:
enum Tabs: Int {
case home = 0
case hot = 1
case favourites = 2
case settings = 3
}
struct RootView: View {
#State private var selectedTab = Tabs.home
var body: some View {
VStack {
switch selectedTab {
case .home:
HomeView()
case .hot:
HotView()
case .favourites:
FavouritesView()
case .settings:
SettingsView()
}
Spacer()
CustomTabBar(selectedTab: $selectedTab)
}
.ignoresSafeArea(.keyboard)
}
}

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?

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

How to expand Detail View to full screen with SwiftUI?

I have a list view embedded in a Navigation View, however, this Navigation View is only about the screen height. This list links to a detailed view, and when a row is tapped, the Detail View only takes up half of the screen. I would like it to open a completely new window.
Screenshots:
The code is used is the following:
import SwiftUI
import CoreData
extension UIScreen{
static let screenWidth = UIScreen.main.bounds.size.width
static let screenHeight = UIScreen.main.bounds.size.height
static let screenSize = UIScreen.main.bounds.size
}
let topCardHeight: CGFloat = 350
struct HomeView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems : FetchedResults<SavedPoem>
var body: some View {
VStack {
VStack (alignment: .center){
Text("Today's Poem, November 18th...")
.font(.subheadline)
.foregroundColor(.white)
.padding(.bottom)
.padding(.top, 75)
Text("No Man Is An Island")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.white)
.padding(.bottom,1)
Text("by John Donne")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.white)
.padding(.bottom, 35)
Button(action: {}) {
Text("Read Now")
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.font(.subheadline)
.foregroundColor(.white)
.padding(15)
.border(Color.white, width: 3)
}
}
.frame(width: UIScreen.screenWidth, height: topCardHeight, alignment: .top)
.background(Color.black)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
.padding(.bottom, 0)
NavigationView{
List{
ForEach(savedpoems, id:\.title) {SavedPoem in
NavigationLink (destination: ContentView()){
ZStack {
Rectangle().fill(Color.white)
.frame(width: UIScreen.main.bounds.width - 32, height: 70)
.cornerRadius(10).shadow(color: .gray, radius: 4)
HStack {
VStack (alignment: .leading){
Text("\(SavedPoem.title ?? "")").font(.headline)
.lineLimit(1)
Text("\(SavedPoem.author ?? "")").font(.subheadline)
.foregroundColor(.secondary)
}
Spacer()
}.padding()
}
// }.onDelete(perform: remove)
}
}
}
.navigationTitle("My Saved Poems")
.navigationBarHidden(true)
.edgesIgnoringSafeArea(.top)
.padding(.top, 0)
}
}
// func remove(at offsets : IndexSet) {
// for index in offsets {
// let delete = SavedPoem[index]
// self.moc.delete(delete)
// }
// try? self.moc.save()
// }
}
Any ideas? Thanks in advance.
If you need the same UI:
(The navigation view at the bottom of your top view) , here is a solution for it .
var body: some View {
#EnvironmentObject var sharedViewModel : SharedViewModel
VStack {
VStack (alignment: .center){
if sharedViewModel.currentPageIsHome {
// your top view body here ..
}
}
NavigationView{\*....*\}
}
}.onAppear {
sharedViewModel.currentPageIsHome = true
}.onDisappear {
sharedViewModel.currentPageIsHome = false
}
And you need to create an Observable object
class SharedViewModel: ObservableObject {
#Published var currentPageIsHome = false
}
And don't forget to initialize it in your SceneDelegate
ContentView().environmentObject(SharedViewModel())
Or
Clear version :
change your view hierarchy to :
NavigationView {
List{
Section(header: YourTopView()) {
// ... your list content
}
}
}

problems in displaying different titles in navigation bar

in my code I have this:
var body: some View {
NavigationView {
VStack(spacing: 0) {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 18) {
ScrollView(.horizontal, showsIndicators: false){
HStack(spacing: 20){
Text("teste")
.frame(height: 180)
.frame(width: 330)
.background(Color.blue)
.cornerRadius(15)
Text("teste")
.frame(height: 180)
.frame(width: 330)
.background(Color.green)
.cornerRadius(15)
Text("teste")
.frame(height: 180)
.frame(width: 330)
.background(Color.pink)
.cornerRadius(15)
}.padding(.horizontal, 12)
}
ForEach(specialtyList, id: \.type){ specialty in
NavigationLink (destination: SearchBar(item: specialty)){
VStack(spacing: 18) {
HStack{
Text(specialty.type).foregroundColor(.white)
specialty.image
.renderingMode(.original)
.resizable()
.frame(width: 35, height: 35)
}.frame(minWidth: 0, idealWidth: 350, maxWidth: .infinity)
.frame(height: 100)
}
}.padding(.horizontal)
.background(specialty.color).cornerRadius(45)
}.padding(.horizontal)
}
.padding(.top)
.padding(.bottom)
}
}.navigationBarTitle("Buscar")
}.accentColor(.black)
}
I want that, according to the button that is pressed in ForEach, the tittle of the next view is the name of the button. Now, the name displaying in every button is "Buscar"
I've tried to implement, after ForEach and before NavigationLink, a NavigationView, but the hole ForEach disappears. Basically, I want that the Text(specialty.type) that is pressed is the name of the navigation bar back button. Any ideas?
Here is a demo of possible approach - the idea is to change navigation bar title on the navigation link activation and reset on back.
Demo prepared & tested with Xcode 12 / iOS 14
struct DemoView: View {
#State private var barTitle = ""
#State private var selectedType = ""
#State private var isActive = false
let types = ["Type1", "Type2", "Type3"]
var body: some View {
NavigationView {
VStack {
ForEach(types, id: \.self) { type in
Button(type) {
self.isActive = true
self.selectedType = type
self.barTitle = type // comment for variant 2
}
}
}
.background(
NavigationLink(destination:
Text(selectedType)
// .onAppear { self.barTitle = self.selectedType } // variant 2
, isActive: $isActive) { EmptyView() }
)
.navigationBarTitle(barTitle)
.onAppear {
self.barTitle = "Buscar"
}
}
}
}

Make SwiftUI Rectangle same height or width as another Rectangle

For a SwiftUI layout in a macOS app, I have three Rectangles as shown below:
The code to produce this layout is:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
HStack {
ZStack {
Rectangle()
.fill(Color.purple)
.frame(width: 20)
Text("1")
.font(.subheadline)
.foregroundColor(.white)
}
ZStack {
Rectangle()
.fill(Color.orange)
Text("2")
.font(.subheadline)
.foregroundColor(.white)
}
}
HStack {
ZStack {
Rectangle()
.fill(Color.red)
.frame(height: 20)
Text("3")
.font(.subheadline)
.foregroundColor(.white)
}
}
}
.frame(minWidth: 400, minHeight: 250)
}
}
My objective is for Rectangle 1 to be the same height as Rectangle 2 and for Rectangle 3 to be the same width as Rectangle 2. The size relationships between the rectangles should stay the same as the window size is changed. When done correctly, the final result should look like the following:
How can I accomplish this in SwiftUI?
Here is a working approach, based on view preferences. Tested with Xcode 11.4 / macOS 10.15.6
struct ViewWidthKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
struct ContentView: View {
#State private var boxWidth = CGFloat.zero
var body: some View {
VStack {
HStack {
ZStack {
Rectangle()
.fill(Color.purple)
.frame(width: 20)
Text("1")
.font(.subheadline)
.foregroundColor(.white)
}
ZStack {
Rectangle()
.fill(Color.orange)
Text("2")
.font(.subheadline)
.foregroundColor(.white)
}
.background(GeometryReader {
Color.clear.preference(key: ViewWidthKey.self,
value: $0.frame(in: .local).size.width) })
}
HStack {
ZStack {
Rectangle()
.fill(Color.red)
.frame(height: 20)
Text("3")
.font(.subheadline)
.foregroundColor(.white)
}.frame(width: boxWidth)
}.frame(maxWidth: .infinity, alignment: .bottomTrailing)
}
.onPreferenceChange(ViewWidthKey.self) { self.boxWidth = $0 }
.frame(minWidth: 400, minHeight: 250)
}
}