SwiftUI tabview changes when made scrollable - swiftui

I have set up a tabview as shown below
var body: some View {
TabView(selection: $selection){
BookingView()
.tabItem{
selection == 0 ? Image("bookReaderGreen") :Image("bookReaderDark")
}
.tag(0)
ScriptsView()
.tabItem{
selection == 1 ? Image("storeGreen") :Image("storeDark")
}
.tag(1)
SettingsView(signInSuccess: $signInSuccess)
.tabItem{
selection == 2 ? Image("cogGreen") :Image("cogDark")
}
.tag(2)
}
// .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
}
it works as implemented however when I include the line .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) the view is displayed differently.
not-scrollable (want it to look like this):
Scrollable (changes images and collapses them together):
Is there any way of keeping the scrolling between the views functionality and my default images?

Related

SwiftUI TabView .tabItem image seems too high

Not sure if I am doing something wrong or if this is just how the TabView in SwiftUI appears now, but it seems to me that the images at the bottom seem much closer to the top border than in previous iOS versions (I am testing on iOS 16).
Here is the code I am using to render this:
struct MainView: View {
var body: some View {
TabView {
NavigationView {
ScrollView {
RoundedRectangle(cornerRadius: 16)
.frame(height: 1000)
.padding()
}
.background(.green)
}
.tabItem {
Image(systemName: "house.fill")
}
.toolbarBackground(.background, for: .tabBar)
NavigationView {
LikesView()
}
.tabItem {
Image(systemName: "heart")
}
NavigationView {
MatchesView()
}
.tabItem {
Image(systemName: "bubble.left")
}
NavigationView {
ProfileView()
}
.tabItem {
Image(systemName: "person")
}
}
.tint(.primary)
}
}
Am I missing something that would cause the images to be higher up in the tab bar or is that just how the standard tab bar looks now?
It looks default to me, just from a brief judgement between a couple other simulators and iOS versions. It does look maybe slightly higher on the 14 Pro Max, compared to something like the 11 Pro Max, since the symbols seem to be a little larger.
There is the ability to add text below the Image with a label which you may be used to seeing:
Label("Messages", systemImage: "bubble.left")

What is the height of the SwiftUI TabView index with PageTabViewStyle?

I am writing an application using Xcode 14.0.1, and testing on an iPhone 12 mini running iOS 16.0. The current project build is for iOS 14.7. Here is my TabView...
TabView {
ByEyeView()
.tabItem { Label("ByEye", systemImage: "eye") }
ChartView()
.tabItem { Label("Chart", systemImage: "square.grid.4x3.fill") }
ListView()
.tabItem { Label("List", systemImage: "list.bullet") }
EditView()
.tabItem { Label("Edit", systemImage: "square.and.pencil") }
CameraView()
.tabItem { Label("Camera", systemImage: "camera") }
SettingsView()
.tabItem { Label("Settings", systemImage: "gear") }
}
//.labelStyle(TitleAndIconLabelStyle())
//.padding(8)
//.ignoresSafeArea(edges: .bottom)
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
This gives a capsule at the bottom of the page with a small version of the icon and no text. I cannot enlarge the Label with .frame, and the .labelStyle() setting is ignored. I take it this is part of PageTabViewStyle() - the index is supposed to be small, and I can probably not change that. But the index sits over the view content, so I need its height if I am to keep buttons clear of it.
Can I find out the index height? Or does PageTabViewStyle assume that the index is small and you should work around it?
The commented-out .ignoreSafeArea() moves the index down while the page remains the same. The .padding() keeps it a bit clear of the bar at the bottom. This is what I am working with for now. This is foul: it will not work with other devices or screen orientations.
The bigger picture:
I have six entries. That does not fit in the default view, so I get a ... More tag which leads to an extra menu. Ugly. I like the PageTabViewStyle method of scrolling, but I want an index with a known height - preferably one that uses the full labels and sits at the bottom of the TabView layout, under the tabbed views.
This was one of those 'Magic Eye' things when you stare at it for days and it makes no sense, and suddenly everything rearranges itself...
Maybe TabPageViewStyle was intended to be for pages where there is no visible index, or overlaying a small index does no harm. This would work for browsing images. All the cunning has gone into making the index view unobtrusive. If you need to know how big it is, then perhaps TabPageViewStyle is not what you want.
What I said I wanted was actually a Scrollable horizontal list, followed by the currently selected list. Something like this...
let tabW = CGFloat(UIScreen.main.bounds.width / 5.0)
enum Page {
case ByEye
case Chart
case List
case Edit
case Camera
case Settings
}
#State private var page = Page.ByEye
func pageButton(_ select: Page, _ icon: String, _ title: String) -> some View {
return Button {
page = select
} label: {
VStack {
Image(systemName: icon)
Text(title)
} .frame(width: tabW)
} .foregroundColor( page == select ? Color.white : Color.gray )
}
var body: some View {
VStack() {
ScrollView(.horizontal) {
HStack() {
pageButton(Page.ByEye, "eye", "ByEye")
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart")
pageButton(Page.List, "list.bullet", "List")
pageButton(Page.Edit, "square.and.pencil", "Edit")
pageButton(Page.Camera, "camera", "Camera")
pageButton(Page.Settings, "gear", "Settings")
}
}
switch page {
case .ByEye:
ByEyeView()
case .Chart:
ChartView()
case .List:
ListView()
case .Edit:
EditView()
case .Camera:
CameraView()
case .Settings:
SettingsView()
}
Spacer()
}
It is not much longer than my original version. It is not as pretty is it could be - when you overflow the title bar you get half an icon, where an ellipsis would be better. But I can fix that later.
The other answer is to write your own index table....
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
pageButton(Page.ByEye, "eye", "ByEye", proxy)
pageButton(Page.List, "list.bullet", "List", proxy)
pageButton(Page.Camera, "camera", "Camera", proxy)
pageButton(Page.Settings, "gear", "Settings", proxy)
}
}
.onAppear { proxy.scrollTo(page, anchor: .center) }
.onChange(of: page) { page in
withAnimation {
proxy.scrollTo(page, anchor: .center)
}
}
}
This particular one has button-sized icons and text. 'page' is an enum, and also the tags of the TabView. If you stick it in the layout, you can make it fit around the TabView. You will want to hide the TabView index, which you can do with...
.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))

SwiftUI: List rows get highlighted when interacting with other views if inside a TabView

If a List is placed along with other views within a VStack which defines one page within a TabView with PageTabViewStyle, interacting (tap, long pressing) with the other views causes all (visible) rows of the List to get highlighted.
The following View demonstrates this behaviour: tapping or long pressing the Button or the purple area (Color View) will cause the rows in the List to get highlighted (Xcode 12.1 & iOS 14.1).
struct ContentView: View {
var body: some View {
TabView {
VStack {
List {
Text("Row 0")
Text("Row 1")
Text("Row 2")
}
.listStyle(InsetGroupedListStyle())
Spacer()
Button(action: { print("tapped")}, label: { Text("Button") } )
.padding(.vertical, 80)
Spacer()
Color.purple
}
Text("Second Page")
}
.tabViewStyle(PageTabViewStyle())
}
}
I assume this is a bug and have already submitted feedback, but was wondering if there is a workaround while it's not fixed.
wondering if there is a workaround while it's not fixed.
After some investigation & testing the only workaround I see is to use scroll view instead
TabView {
VStack {
ScrollView { // << here
Text("Row 0")
Text("Row 1")
Text("Row 2")
}
Note: of course it might require some manual formatting & layout inside scroll view, but there is no such bug.

How can i create TabView with Headers on top not bottom in SwiftUI

How can i make a SwiftUI TabView with headers aligned to top rather than the bottom.
Thanks
This is not directly supported by SwiftUI but you could make your own custom view and here is a simple example on how to do that:
struct ContentView: View {
#State var selectedTab = Tabs.FirstTab
var body: some View {
VStack {
HStack {
Spacer()
VStack {
Image(systemName: "airplane")
.foregroundColor(selectedTab == .FirstTab ? Color.red : Color.black)
Text("First tab")
}
.onTapGesture {
self.selectedTab = .FirstTab
}
Spacer()
VStack {
Image(systemName: "person.fill")
.foregroundColor(selectedTab == .SecondTab ? Color.red : Color.black)
Text("Second tab")
}
.onTapGesture {
self.selectedTab = .SecondTab
}
Spacer()
VStack {
Image(systemName: "cart.fill")
.foregroundColor(selectedTab == .ThirdTab ? Color.red : Color.black)
Text("Third tab")
}
.onTapGesture {
self.selectedTab = .ThirdTab
}
Spacer()
}
.padding(.bottom)
.background(Color.green.edgesIgnoringSafeArea(.all))
Spacer()
if selectedTab == .FirstTab {
FirstTabView()
} else if selectedTab == .SecondTab {
SecondTabView()
} else {
ThirdTabView()
}
}
}
}
struct FirstTabView : View {
var body : some View {
VStack {
Text("FIRST TAB VIEW")
}
}
}
struct SecondTabView : View {
var body : some View {
Text("SECOND TAB VIEW")
}
}
struct ThirdTabView : View {
var body : some View {
Text("THIRD TAB VIEW")
}
}
enum Tabs {
case FirstTab
case SecondTab
case ThirdTab
}
NOTE: I haven't put much effort in aligning the tabs perfectly and used Spacers for simplicity (because this is not relevant to the question). Also I have put all the code so that you could create new empty project and copy-paste it to try and understand how it works.
Lets go through it:
The Tabs enum is created for simplicity
The selectedTab variable is keeping track of which tab is currently selected
I am using if-else construction to display the selected view (as of SwiftUI 2.0 there is also a switch-statement, but since you didn't specify what versions are you using I am using if-else)
The tab view itself is nothing more than Image & Text views aligned to look like the TabView
The foregroundColor modifier using ternary operator is simulating TabView's accent color
When tapped, each Text + Image combination (which replaces buttons on the normal TabView) changes the state to select its view as visible (you could also use buttons with action instead of .onTapGesture I used this for the simplicity of the example)
Here is the result:

How to save List state for View in TabView SwiftUI

I have a TabView in SwiftUI with tabs. When I scroll list from one FirstView and tap another tab, and switch back to FirstView, my List in FirstView automatically redraws and scrolls to top. How to fix that.
enter link description here
This is FirstView
var body: some View {
NavigationView {
List {
ForEach(feed) { game in
FeedCardItem(gameModel: game)
.frame(width: UIScreen.main.bounds.width - 30, height: 400)
}
}
.navigationBarTitle(Text("Новое сегодня"))
}
}
This is TabView implementation
TabView (selection: $currentTab) {
FeedView().tabItem {
Image(systemName: currentTab == 0 ? "house.fill" : "house")
.renderingMode(.original)
.imageScale(.large)
}.tag(0)
RecommendationsView().tabItem {
Image(systemName: currentTab == 1 ? "gamecontroller.fill" : "gamecontroller")
.renderingMode(.original)
.imageScale(.large)
}.tag(1)
SearchView().tabItem {
Image(systemName: currentTab == 2 ? "flame.fill" : "flame")
.renderingMode(.original)
.imageScale(.large)
}.tag(2)
NotificationsView().tabItem {
Image(systemName: currentTab == 3 ? "bell.fill" : "bell")
.renderingMode(.original)
.imageScale(.large)
}.tag(3)
ProfileView().tabItem {
Image(systemName: currentTab == 4 ? "person.fill" : "person")
.renderingMode(.original)
.imageScale(.large)
}.tag(4)
}.edgesIgnoringSafeArea(.top)
}
I don't think it is possible via SwiftUI. The alternate to do this is to create the tabview in UIKit and use it in SwiftUI.
I have created a quick sample of how this can be achieved and how it solves your issue. Let me know if you have any other questions.
I have uploaded the code to GitHub.
https://github.com/subhan5/TabTest
In the uploaded code, the folder UITab contains the tabview created via UIKit.
In ContentView, I convert the same as UIViewControllerRepresentable and use it.
I have the same problem, and found this solution: https://kavsoft.dev/SwiftUI_2.0/Tab_Bar
Basically, you could just hide the content of one tab instead of replace it with another content view. I thought this solution is a little tricky but works.
it is possible with the UITableViewConfigurator which is described here: Scroll SwiftUI List to new selection
But of course...this uses UIViewControllerRepresentable....