iOS 15 navigation bar transparency problem with TabView - swiftui

New iOS 15 makes navigation bar background completely transparent if there is no element behind, if there is a List and you scroll the elements to be behind the navigation bar this obtains a white translucent background, but if I use a TabView where every TabItem have a List inside the navigation bar background did not update correctly when switching between tab items, the navigation bar keep always transparent background.
Im using SwiftUI and my basic code looks like this:
struct Main: View {
var body: some View {
WindowGroup {
NavigationView {
TabView {
TabElement()
TabElement()
TabElement()
TabElement()
TabElement()
}.navigationBarTitle(Text("Main"), displayMode: .inline).navigationBarBackButtonHidden(true)
}
}
}
}
struct TabElement: View {
var body: some View {
VStack {
List {
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
Text("empty")
}.listStyle(InsetGroupedListStyle())
}.tabItem {
Image(systemName: "star.fill")
Text("dummy")
}
}
}
So, this code makes a tabed view with five tabs, each tab have a list of ten Text views, if I switch to any other tab and scroll the elements to the top, the list can be seen through navigation bar instead of behind.
What is causing this behavior? It's some kind of bug or my code is wrong? This problem is not happening in iOS 14.* since navigation bar always have white background.
NOTE:
I found that it is possible to use:
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
}
...in my ApDelegate but it's look like a tricky way to get rid of the transparency in navigation bar. If Apple decide to use this new design in iOS 15 I wanna implement it in my app, but only if the transparency update correctly.

Yes apple has changed that in iOS 15.
If you want to change the appearance of the navigation bar in a single ViewController, you can use this code: https://stackoverflow.com/a/69493819/9263676

Related

SwiftUI - How to make navigation bar title editable (without changing any other behavior)?

I really like the look of the navigation bar title in SwiftUI, and I like that it appears just below the safe area, but appears in the principal part of the toolbar when you scroll down. I'm wondering how to completely replicate this look and behavior but make it editable by the user (most likely through a textfield?)
I've tried
.toolbar {
ToolbarItem(placement: .principal) {
TextField("Navigation Title", text: $mainTitle)
}
}
But this simply places the title in the toolbar at all times, rather than only when you scroll slightly.
Any ideas?
First I explain why your code does not work:
Only the size of the navigationTitle changes when you start to scroll, not the size of the whole toolbar or its items.
But I think I have a solution:
import SwiftUI
struct ContentView: View {
#State private var title: String = "Title"
#State private var titleSmall: Bool = false
var body: some View {
NavigationView {
List {
GeometryReader { geo in
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
.onChange(of: geo.frame(in: .global).minY) { val in
if val <= 53.5 {
titleSmall = true
} else {
titleSmall = false
}
}
}
Text("Hello, world!")
}
.toolbar {
ToolbarItem(placement: .principal) {
TextField("Title", text: $title)
.multilineTextAlignment(.center)
.font(titleSmall ? .headline : .largeTitle.bold())
.accessibilityAddTraits(.isHeader)
}
}
}
}
}
What the code does is: It gets the top Y position from the first (in this example) list item.
Then it checks if the first list item is under the title bar and changes the font size of the title if necessary.
The only Problem I see is that there is a pretty rough transition between small and big title but I think you can figure out how to fix this.
If you have more questions how the code works just ask
I hope that solves your question.
And I would recommend to have a look at Paul Hudson’s video about the Geometry Reader (he’s a great YouTuber): https://youtu.be/kh9lnIYgW1E
I just realized that it says „OLD“ in the video title, so it may be outdated.
But he has some other videos about the Geometry Reader.
just search for „Paul Hudson Geometry Reader“

SwiftUI: Change navigation bar title in the more tab?

I am begining to get my haead around swiftUI. So I have a simple tabView inside a Navigation View as below.
import SwiftUI
struct BasicView: View {
var climbList: [ClimbDetail]
var body: some View {
NavigationView{
VStack{
Text("").navigationBarTitle("Climbers Log", displayMode:.inline)
TabView {
Text("Search").tabItem{
Image(systemName:"magnifyingglass")
Text("Search")
}
Text("Stats").tabItem{
Image(systemName:"list.dash")
Text("Stats")
}
ClimbList(climbs: climbList).tabItem{
Image(systemName: "square")
Text("Climbs")
}
Text("Log").tabItem{
Image(systemName:"square.and.pencil")
Text("Log")
}
Text("Profile").tabItem{
Image(systemName:"person.circle")
Text("Profile")
}
Text("Settings").tabItem{
Image(systemName:"gear")
Text("Settings")
}
Text("Add Climb").tabItem{
Image(systemName:"plus")
Text("Add Climb")
}
}
}
}
}
}
Genrally it works as expected, however as I have 7 tabs it defults to the 'More' tab for the 5th tab. This is fine and good for the user.
However my issue is when you click the 'More' tab you get the a title bar and edit button with 'More' as the title. Which appears below the title bar I have set above.
So my question is how can I hide my titleBar when the user is on the 'More' tab and only show it inside the other tabs?
First off all, thanks for the question. I didn't know Apple provides More page by default for too long TabBars. I always needed that.
Back to you question, you just need to rearrange things. First off all, make the TabBar top level. Then a NavigationView and here comes your content. Later on you might outsource every view, to an own file.
TabView {
NavigationView
{
VStack
{
Text("Search")
}
.navigationBarTitle("Climbers Log", displayMode:.inline)
}.tabItem {
Image(systemName:"magnifyingglass")
Text("Search")
}
Every view takes it own NavigationView.. and then it works.

Handling focus event changes on tvOS in SwiftUI

How do I respond to focus events on tvOS in SwiftUI?
I have the following SwiftUI view:
struct MyView: View {
var body: some View {
VStack {
Button(action: {
print("Button 1 pressed")
}) {
Text("Button 1")
}.focusable(true) { focused in
print("Button 1 focused: \(focused)")
}
Button(action: {
print("Button 2 pressed")
}) {
Text("Button 2")
}.focusable(true) { focused in
print("Button 2 focused: \(focused)")
}
}
}
Clicking either of the buttons prints out correctly. However, changing focus between the two buttons does not print anything.
This guy is doing the same thing with rows in a list & says it started working for him with the Xcode 11 GM, but I'm on 11.5 and it's definitely not working (at least not for Buttons (or Toggles - I tried those too)).
Reading the documentation, this appears to be the correct way to go about this, but it doesn't seem to actually work. Am I missing something, or is this just broken?
In case anybody else stumbles upon this question, the answer is to make your view data-focused
So, if you have a list of movies in a scrollview, you would add this to your outer view:
var myMovieList: [Movie];
#FocusState var selectedMovie: Int?;
And then somewhere in your body property:
ForEach(0..<myMovieList.count) { index in
MovieCard(myMovieList[i])
.focusable(true)
.focused($selectedMovie, equals: index)
}
.focusable() tells the OS that this element is focusable, and .focused tells it to make that view focused when the binding variable ($selected) equals the value passed to equals: ...
For example on tvOS, if you wrap this in a scrollview and press left/right, it will change the selection and update the selected index in the $selectedMovie variable; you can then use that to index into your movie list to display extra info.
Here is a more complete example that will also scale the selected view:
https://pastebin.com/jejwYxMU

SwiftUI ScrollView renders sub views too early before they enter screen

I recently started studying ios/swiftui by developing a small application, which has a list of cards to display picture and message loaded from server.
I was using List to hold those cards but the default list 'decoration' (divider, arrow, tapping effect) looks not very good together with my card view. I disable them by adding below code into SceneDelegate:
UITableView.appearance().separatorColor = .clear
UITableViewCell.appearance().selectionStyle = .none
And having some sort of hack to hide the arrow:
List {
ForEach(0..<self.store.data.count, id: \.self) { idx in
NavigationLink(destination: DetailView(item: self.store.data[idx])) {
EmptyView()
}.frame(width: 0).opacity(0)
ItemCard(item: self.store.data[idx])
}
BottomLoader().onAppear {
if self.store.status != .loading {
self.store.load()
}
}
}
But, the problem is that hiding List's selection style and separator color in SceneDelegate applies to all the lists in App, and I do have couple lists (such as the one in settings) need those style.
Then I tried to change the card holder from List to ScrollView but it leads to another trouble, the onAppear callback of last view (BottomLoader) gets called at the same time with ScrollView onAppear gets called.
As far as I understand, onAppear is supposed to be called when view is rendered and shown. List as the item holder, does not have to calculate the full height of all items, that is probably why List renders only the items about to enter screen. But ScrollView does need to know the full height of all items and that is why all the items get rendered.
Below are two small segments to show different behaviors of List and ScrollView:
List {
ForEach(0..<100) { idx in
Text("item # \(idx)").padding().onAppear { print("\(idx) on appear") }
}
}
// only 18 logs printed
And:
ScrollView {
ForEach(0..<100) { idx in
Text("item # \(idx)").padding().onAppear { print("\(idx) on appear") }
}
}
// all 100 logs printed
Is there any way, to let ScrollView acts like List, to render its subviews when they about to enter screen.
Or, is there any way, to disable those default styles to a specific List, not globally?

SwiftUI:- Image won't show on the View

I am playing around with SwiftUI and I am stuck on this View. everything is working fine but this little bug is very frustrating.I am trying to display the images as a vertical view and it won't show on the view . I know the Images are loaded but the view is not showing it . Its covered in blue color.
import SwiftUI
struct PlanetHome : View {
var planets : [Planet]
var body : some View {
NavigationView {
ScrollView {
ZStack {
Color.black .edgesIgnoringSafeArea (.all)
VStack (alignment: .center)
{
ForEach (self.planets.identified(by: \.imageName))
{
planet in NavigationLink (destination: PlanetDetail(planets: planet))
{
PlanetsView (planets: planet)
.frame (width: 500, height: 500)
.padding (.vertical, 5)
}
}
}
}
}
.navigationBarTitle (Text("Planets"))
}
}
}
I tried to put the NavigationView under the ZStack but it did not work.I have no Idea what I did wrong on the code. No error message on the debugger. just doesn't show the images.
The NavigationLink applies a button style to the objects it holds. Button does the same. To remove the blue color, add the buttonStyle modifier:
NavigationLink(destination: ...) {
...
}
.buttonStyle(PlainButtonStyle())
You can create and apply your own button style as well.
Its covered in blue color.
You can change blue color to whatever you want with .foregroundColor(.YourColor) or just change Render as Default to Render as Original Image at Assets.xcassets -> Pick Any Image -> Show the Attribute inspector
To fix other problem you should include PlanetsView because when i put Image(systemName: "photo") instead your view it's shows correctly