Tab Bar Customisation (tab bar item positioning) with Tabview in SwiftUI - ios

I am trying to figure out how to use the functions from xcode with very limited information from the Apple documentation. How to use certain functions or variables or where to find examples and detailed explanations? The particular case that I am working on now is to offset the tab bar item images on the tab bar. I came across "itemPositioning", "stackedItemPositioning" but I have no clues about how to use them. Should I put it inside the initialiser function? And how to use it?
Also, a quick question. Is there any book or video covering like somethings that teach me how to explore unknown things on SwiftUI independently?
truct MainContentView: View {
#State private var navSelection = 0
init(){
UITabBar.appearance().backgroundColor = UIColor.clear
//stackedItemPositioning = UIOffset(horizontal: 0, vertical: -10)
}
var body: some View {
VStack(alignment: .center){
NavigationView{
TabView(selection: self.$navSelection){
Text("hey")
.tabItem{
Image(systemName: "heart.fill")
.offset(y:-15)
}
.tag(0)
Text("hey")
.tabItem{
Image(systemName: "magnifyingglass")
}
.tag(1)
Text("hey")
.tabItem{
Image(systemName: "capsule.portrait.fill")
}
.tag(2)
Text("hey")
.tabItem{
Image(systemName: "wand.and.stars")
}
.tag(3)
}
}
}
}
}

Related

SwiftUI TabView inside a NavigationView

So I know it's not really encouraged to put a TabView inside a NavigationView and that you're supposed to do it the other way around. But the way I want my app I don't really see how I can have it another way...
So I have a login screen in which a user inputs their username, then only once I verify that everything is okay with username I wanna bring them over to a TabView(the search button is a navlink) I don't really see any other way to implement this but the problem is with my implementation is once I switch tabs in the tab view, the navigation title doesn't seem to change, and there also doesn't seem to be a navigation bar because when I scroll the old NavigationTitle gets drawn over by a Text View I have.
I'm not sure if adding code would help in this case because it seems this is just kind of a problem with TabViews inside NavigationViews but if someone wants me to show some code I can add an edit with it. I was just wondering if anyone had any ideas for how I could fix something like this or some other way to implement this?
Edit:
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink{
TabView{
ScrollView{
Text("Some view")
}
.tabItem{
Text("New View")
}
ScrollView{
Text("Another view")
}
.tabItem{
Text("Another view")
}
}
} label: {
Text("Go to new view!")
}
}
}
}
It is perfectly fine to have TabView() inside a NavigationView. Every time we switch between pages in any app, the navigating is mostly expected, so almost every view is inside the NavigtionView for this reason.
You can achieve this design with something like this (see the bottom images also):
struct LoginDemo: View {
#State var username = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter your user name", text: $username)
.font(.headline)
.multilineTextAlignment(.center)
.frame(width: 300)
.border(.black)
NavigationLink {
GotoTabView()
} label: {
Text("search")
}
.disabled(username == "Admin" ? false : true)
}
}
.navigationTitle("Hey It's Nav View")
}
}
struct GotoTabView: View {
#State var temp = "status"
#State var selection = "view1"
var body: some View {
TabView(selection: $selection) {
Image("Swift")
.resizable()
.frame(width: 300, height: 300)
.tabItem {
Text("view 1")
}
.tag("view1")
Image("Swift")
.resizable()
.frame(width: 500, height: 500)
.tabItem {
Text("view 2")
}
.tag("view2")
}
.onChange(of: selection){ _ in
if selection == "view1" {
temp = "status"
}
else {
temp = "hero"
}
}
.toolbar{
ToolbarItem(placement: .principal) {
Text(temp)
}
}
}
}
NavigationView:
TabView:

SwiftUI - Views inside TabView jump after a second if TabView is nested in NavigationLink

My setup
Entry point (ContentView):
struct ContentView: View {
var body: some View {
NavigationView {
FirstView()
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}
}
FirstView:
struct FirstView : View {
var body : some View {
NavigationLink(destination:
SecondView()
.navigationBarTitle("")
.navigationBarHidden(true)) {
Text("Fourth View")
}
}
}
SecondView:
struct SecondView : View {
var body : some View {
TabView {
FirstTabView()
.tabItem {
Image(systemName: "play.circle.fill")
Text("First Tab")
}
.navigationBarTitle("First Tab")
.navigationBarHidden(true)
SecondTabView()
.tabItem {
Image(systemName: "bell.fill")
Text("Second Tab")
}
.navigationBarTitle("Second Tab")
.navigationBarHidden(true)
}
}
}
FirstTabView
struct FirstTabView : View {
var body : some View {
VStack {
HStack(spacing: 10) {
Spacer()
Text("FIRST")
Spacer()
Text("SECOND")
Spacer()
}
.padding(.all)
Spacer()
}
.border(Color.red, width: 5)
}
}
And SecondTabView is very similar to the first one so I won't include it.
The issue
The issue I am having right now is that the content of my TabViews jump up ignoring safe areas (For clarification see the attached screenshots). The view actually displays perfectly fine and as expected for a brief moment and then immediately jumps up breaking everything.
I have found that this is only happening if the TabView is loaded via NavigationLink as I do right now. If I directly load the SecondView() inside my NavigationView on ContentView (or in other words its the first view that I load) then the issue is gone and everything works as expected.
I am sorry that I wasn't able to include GIF of the problem - the View starts off as the expected result and then after a seconds jumps up and becomes what you see on the actual result.
Expected Result
Actual Result
NOTE: On iPhones with notch my text is not even visible. Also I've put thick border in order to make my point more clear.
EDIT
I just found out that locking the device and then unlocking it brings the screen back to normal.

SwiftUI - TabView initial tabItem not displaying text

Summary of the problem
I have HomeView which contains the TabView (which is inside a NavigationView, see the code below). If I am to load the HomeView from another view (LoginView) it loads as expected and everything works. If I try to load the HomeView directly like this (code is in my ContentView):
if authService.isLoggedIn {
HomeView()
} else {
LoginView()
}
it again loads the HomeView with the tabs at the bottom but my first tab is missing text and only displays its image. Strangely if I switch to a tab (i.e. click Account) the text on the first tab appears again.
Here is the code for my TabView:
NavigationView {
TabView(selection: $selected) {
PlayView()
.tabItem {
Image(systemName: "play.circle.fill")
.font(.system(size: 24))
Text("Play")
}
.navigationBarHidden(true)
.navigationBarTitle("")
.tag(1)
AccountView()
.tabItem {
Image(systemName: "person.circle.fill")
.font(.system(size: 24))
Text("Account")
}
.tag(3)
NotificationsView()
.tabItem {
Image(systemName: "bell.fill")
.font(.system(size: 24))
Text("Notifications")
}
.tag(4)
}
.accentColor(Color(K.Colors.Secondary))
.navigationBarTitle("")
.navigationBarHidden(true)
}
Expected Result
Actual Result
Note: Notice how the icon is displayed at lower level than other icons, so the text is actually not displaying at all
What I Tried So Far
I did try only a few things, because the TabView is not very well documented on Apple's official documentation.
I tried moving the NavigationView both down and up the view hierarchy
Setting another tab for initial selection
Switching places of Image() and Text() inside the .tabItem section
Searching for a similar problem through the internet
I have found a solution! Turns out what was showing at the bottom was that navigation bar title, which I was setting to an empty string. So I changed my navigation bar titles on every View of the TabView elements. The code now looks like this:
NavigationView {
TabView(selection: $selected) {
PlayView()
.tabItem {
Image(systemName: "play.circle.fill")
.font(.system(size: 24))
Text("Play")
}
.navigationBarHidden(true)
.navigationBarTitle("Play")
.tag(1)
AccountView()
.tabItem {
Image(systemName: "person.circle.fill")
.font(.system(size: 24))
Text("Account")
}
.navigationBarHidden(true)
.navigationBarTitle("Account")
.tag(3)
NotificationsView()
.tabItem {
Image(systemName: "bell.fill")
.font(.system(size: 24))
Text("Notifications")
}
.navigationBarHidden(true)
.navigationBarTitle("Notifications")
.tag(4)
}
.accentColor(Color(K.Colors.Secondary))
.navigationBarTitle("")
.navigationBarHidden(true)
}
and It works as expected.

SwiftUI Bring Down Navigation Items

In my SwiftUI app, I would like to bring the navigation bar items down like in Apple's own UIKit apps.
Seen below is a screenshot from the Health app. Notice how the profile picture is in line with the 'Summary' text. This is what I am looking to achieve.
I have tried using .padding(.top, 90) but this has not worked as it does not bring down the virtual box that allows the button to be clicked. Using padding means that you have to tap the button above the image/text.
Thank you.
Unfortunately I didn't find any solution for changing navigation bar height in iOS 13 with SwiftUI, and had the same issues earlier. Solution below will fit you, if your navigation bar is always only black and you're ok with gap on the top:
struct NavBarCustomItems: View {
init() {
setNavigationBarToBlackOnly()
}
func setNavigationBarToBlackOnly() {
let blackAppearance = UINavigationBarAppearance()
blackAppearance.configureWithOpaqueBackground()
blackAppearance.backgroundColor = .black
blackAppearance.shadowColor = .clear // to avoid border line
UINavigationBar.appearance().standardAppearance = blackAppearance
UINavigationBar.appearance().scrollEdgeAppearance = blackAppearance
}
var body: some View {
NavigationView {
VStack {
NavigationBarMimicry()
// here is your content
HStack {
Text("Favorites")
Spacer()
Button(action: {}) { Text("Edit") }
}
.padding()
Spacer()
VStack {
Text("Main screen")
}
// you need spacer(s) to be sure, that NavigationBarMimicry is always on the top
Spacer()
}
}
}
}
// MARK: here is what you need in navigation bar
struct NavigationBarMimicry: View {
var body: some View {
HStack {
Text("Summary")
.bold()
.font(.system(size: 40))
.foregroundColor(.white)
.padding(.horizontal)
Spacer()
Rectangle()
.foregroundColor(.white)
.frame(width: 40)
.padding(.horizontal)
}
.background(Color.black)
.frame(height: 40)
.navigationBarTitle("", displayMode: .inline)
// you can add it to hide navigation bar, navigation will work via NavigationLink
// .navigationBarHidden(true)
}
}
struct NavBarCustomItems_Previews: PreviewProvider {
static var previews: some View {
NavBarCustomItems().environment(\.colorScheme, .dark)
}
}
the result should be like this:
P.S. maybe the other ways are:
Put views in this order: VStack { NavigationBarMimicry(); NavigationView {...}};
uncomment line of code: .navigationBarHidden(true);

Change UIView background color in SwiftUI

Hello everyone. I'm creating a simple iOS app with SwiftUI, and I'd like to change my view's background color to a custom one I have.
This is something extremely easy to do but it seems that it's impossible to achieve in SwiftUI without using ZStacks or workarounds like that, which if you use a List, for example, don't work.
I want to change the color of the view, not use a ZStack with a custom color and then put the rest of the views on top of it. I tried using UIView.appearance().backgroundColor = color when initializing my view, but then all the view is hidden and the screen is filled with the color chosen.
As I'm not good at explaining, here you have some images describing the problem:
Without color change
With color change
My code
import SwiftUI
struct TabController: View {
#State private var selection = 0
init() {
UIView.appearance().backgroundColor = UIColor(named: "backgroundColor")
}
var body: some View {
TabView(selection: $selection) {
HomePageView()
.tabItem {
Image(systemName: "house.fill")
.font(.title)
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
Image(systemName: "bell.fill")
.font(.title)
}
.tag(1)
}.edgesIgnoringSafeArea(.top)
}
}
Hope this will help to understand:
var body: some View {
Color.purple
.overlay(
VStack(spacing: 20) {
Text("Overlay").font(.largeTitle)
Text("Example").font(.title).foregroundColor(.white)
})
.edgesIgnoringSafeArea(.vertical)
}
Another If you use the views in Group
var body: some View {
Group {
Text("Hello SwiftUI!")
}
.background(Color.black)
}
For changing the background color, I think the current method most people, and myself, are using is using a ZStack. I haven't seen many problems with putting a UIViewRepresentable on top of it.
var body: some View {
ZStack {
Color.blue
.edgesIgnoringSafeArea(.all)
VStack {
Text("Hello World!")
}
}
}

Resources