SwiftUI: Why .prefersDefaultFocus modifier does not receive focus if there is a TabView on tvOS? - xcode

I am developing a tvOS application and I wanted to use view like split screen of course I need focus management in it. I've seen video from WWDC and my research that this can be done with the prefersDefaultFocus(_:in:) modifier. I even made a little demo. While it works fine if TabView is not used on the screen, prefersDefaultFocus(_:in:) does not work when TabView is added.
Have any of you encountered this situation? How can it be overcome?
Here is my code sample, you can test both case;
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
//ContentView()
ContentWithTabView()
}
}
}
struct ContentView: View {
#Environment(\.resetFocus) var resetFocus
#Namespace private var namespace
var body: some View {
HStack(spacing: 100) {
Group {
VStack {
Button ("1") {}
.prefersDefaultFocus(in: namespace)
Button ("2") {}
Button ("3") {}
}}
Group{
VStack {
Button ("1") {}
Button ("2") {}
Button ("3") {}
Button ("Reset to default focus") {
resetFocus(in: namespace)
}
}
}
}
.focusScope(namespace)
}
}
struct ContentWithTabView: View {
var body: some View {
NavigationView {
TabView {
ContentView()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
ContentView()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
}
}
}
}

Related

.navigationBarTitleDisplayMode(.inline) crashes Xcode preview

I am consistently coming across this issue whenever I use .navigationBarTitleDisplayMode(.inline). Is there a problematic use of this method, or is there something incorrect about my code ?
How to reproduce error:
In the preview, hit the "list.bullet.rectangle.fill" and dismiss the dialog. Do it a second time and now Xcode crashes the Preview.
Xcode Version 14.2 (14C18)
import SwiftUI
struct PDFTableContentsView: View {
var body: some View {
Text("PDF Table Contents View")
.bold()
.underline()
}
}
struct PDFContentView: View {
#State private var showContents: Bool = false
var body: some View {
VStack {
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
}
.navigationTitle("PDF Title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showContents.toggle()
} label: {
Image(systemName: "list.bullet.rectangle.fill")
}
}
}
.sheet(isPresented: $showContents) {
PDFTableContentsView()
}
}
}
struct PDFContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
PDFContentView()
}
}
}

Change size of Navigation Bar in SwiftUI

Hello SO community 👋🏼
I'm trying to recreate NavigationBar from Contact tab Apple's Phone in my SwiftUI app.
I played around with .toolbar and modifications but really can't recreate it. I wanna replace TextField to SegmentPicker with saving all behavior of .navigationBarTitleDisplayMode(.inline). But don't know is it possible to use SwiftUI only to get it or need to dive into UIKit. I'm not the expert of UIKit and I will be glad for any help. I want use this NavBar in a exact screen in the app and if possible do not change my preferences of NavBar on other part of app. My code:
import SwiftUI
struct NavBar: View {
#State var pickerOptions: Int = 0
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack {
Text("Hello, World!")
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
SegmentPicker
}
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "plus")
}
ToolbarItem(placement: .navigationBarLeading) {
Text("Groups")
}
}
}
}
private var SegmentPicker: some View {
VStack {
Text("Contacts").font(.body.weight(.semibold))
Picker("Options", selection: $pickerOptions) {
Text("Cons").tag(0)
Text("Prons").tag(1)
}
.pickerStyle(.segmented)
.frame(width: 200)
}
}
}

SwiftUI: animating tab item addition/removal in tab bar

In my app I add/remove a subview to/from a TabView based on some condition. I'd like to animate tab item addition/removal in tab bar. My experiment (see code below) shows it's not working. I read on the net that TabView support for animation is quite limited and some people rolled their own implementation. But just in case, is it possible to implement it?
import SwiftUI
struct ContentView: View {
#State var showBoth: Bool = false
var body: some View {
TabView {
Button("Test") {
withAnimation {
showBoth.toggle()
}
}
.tabItem {
Label("1", systemImage: "1.circle")
}
if showBoth {
Text("2")
.tabItem {
Label("2", systemImage: "2.circle")
}
.transition(.slide)
}
}
}
}
Note: moving transition() call to the Label passed to tabItem() doesn't work either.
As commented Apple wants the TabBar to stay unchanged throughout the App.
But you can simply implement your own Tabbar with full control:
struct ContentView: View {
#State private var currentTab = "One"
#State var showBoth: Bool = false
var body: some View {
VStack {
TabView(selection: $currentTab) {
// Tab 1.
VStack {
Button("Toggle 2. Tab") {
withAnimation {
showBoth.toggle()
}
}
} .tag("One")
// Tab 2.
VStack {
Text("Two")
} .tag("Two")
}
// custom Tabbar buttons
Divider()
HStack {
OwnTabBarButton("One", imageName: "1.circle")
if showBoth {
OwnTabBarButton("Two", imageName: "2.circle")
.transition(.scale)
}
}
}
}
func OwnTabBarButton(_ label: String, imageName: String) -> some View {
Button {
currentTab = label
} label: {
VStack {
Image(systemName: imageName)
Text(label)
}
}
.padding([.horizontal,.top])
}
}

Calling a sheet from a menu item

I have a macOS app that has to display a small dialog with some information when the user presses the menu item "Info".
I've tried calling doing this with a .sheet but can't get it to display the sheet. Code:
#main
struct The_ThingApp: App {
private let dataModel = DataModel()
#State var showsAlert = false
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.dataModel)
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
}
}
}
How would I display a sheet from a menu item?
However, if a sheet is not the way to do it (I think given the simplicity of what I need to show, it would be it), how would you suggest I do it? I tried creating a new view, like I did with the preferences window, but I can't call it either from the menu.
put the sheet directly on ContentView:
#main
struct The_ThingApp: App {
#State private var isShowingSheet = false
var body: some Scene {
WindowGroup {
ContentView()
// here VV
.sheet(isPresented: $isShowingSheet) {
VStack {
Text("Some stuff to be shown")
.font(.title)
.padding(50)
Button("Dismiss",
action: { isShowingSheet.toggle() })
}
}
}
.commands {
CommandMenu("Info") {
Button("Get Info") {
print("getting info")
isShowingSheet.toggle()
}
}
}
}
}

SwiftuUI NavigationLink inside touchBar

I'm trying to create NavigationLink in MacBook touchBar with help of SwiftUI. Actually with my piece of code, the button is shown in touchbar, but unfortunately the link doesn't work.
NavigationView {
.touchBar {
NavigationLink(destination: BookView()) {
Text("GoToBook")
}
}
}
struct BookView: View {
var body: some View {
Text("Hello")
}
}
Try instead with Button in touchBar activating NavigationLink programmatically, like below
#State private var isActive = false
...
// below in body
NavigationView {
SomeView() // << your view here
.background(NavigationLink(destination: BookView(), isActive: $isActive) {
EmptyView()
} // hidden link
)
.touchBar {
Button("GoToBook") { self.isActive.toggle() } // activate link
}
}

Resources