Posts

Post not yet marked as solved
4 Replies
686 Views
I have a multiplatform app where I support iOS, macOS and tvOS. There is one target which supports it all. In my assets catalog I have the AppIcon entry which holds the app icon for iOS and macOS. This works as expected. However the tvOS app icon is ignored. I added an "tvOS App Icon & Top Shelf Image" asset to my asset catalog and filled it with my icons for tvOS. Then I added it in the target’s general settings App Icon entry under App Icons and Launch Screen like shown in the screenshot. What am I missing? What needs to be done to make this work?
Posted
by RayWo.
Last updated
.
Post not yet marked as solved
2 Replies
605 Views
I have an app which uses SwiftUI and Mac Catalyst. When running on a Mac I want to provide a preferences menu entry with the usual keyboard shortcut Command + ,. An implementation via the Settings bundle is out of question since my preferences are too complex for this. Here is a reduced example of my implementation: import SwiftUI @main struct PreferencesMenuTestApp: App { @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate { override func buildMenu(with builder: UIMenuBuilder) { let preferencesCommand = UIKeyCommand(title: "Preferences…", action: #selector(showPreferences), input: ",", modifierFlags: .command) // let preferencesCommand = UIAction(title: "Preferences…") { action in // debugPrint("show preferences") // } let menu = UIMenu(title: "Preferences…", options: .displayInline, children: [preferencesCommand]) builder.insertSibling(menu, afterMenu: .about) } @objc func showPreferences() { debugPrint("show preferences") } } The problem is that the menu entry is disabled. Obviously the provided selector is not recognised. When I mark the AppDelegate with @main, then the menu entry is enabled. Of course then the app's window is empty. When I switch to the UIAction implementation (the out commented code) it works fine. But since one cannot provide a keyboard shortcut for UIActions this is not a good solution. What am I missing? How would one implement a preferences menu entry that actually works?
Posted
by RayWo.
Last updated
.
Post not yet marked as solved
0 Replies
352 Views
I want to recreate an user experience like in the settings app in tvOS. Therefore I have a HStack with some content on the left side and a List of NavigationLinks on the right side. However a focused link in the list gets clipped on the left side. I tried paddings and spacers and what not, but nothing helped. Is this is a bug or am I missing something? Here is some example code to show the problem: struct ContentView: View { var body: some View { NavigationStack { HStack(spacing: 20) { VStack(alignment: .center) { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } List { ForEach(SomeViewEnum.allCases) { someView in NavigationLink(someView.rawValue, value: someView) } } .navigationDestination(for: SomeViewEnum.self) { someView in Text(someView.rawValue) } } } } } And a screenshot to show the problem:
Posted
by RayWo.
Last updated
.
Post not yet marked as solved
1 Replies
488 Views
I am building a tvOS app which divides the screen into two parts horizontally. Both sides have buttons. I want a certain button on the right side to have focus. To achieve that I am marking the enclosing HStack with .focusScope() and then use .prefersDefaultFocus(in:) on the button which should have focus. This works fine, even if I add another focus scope inside (see source code). But as soon as I wrap the HStack in a NavigationView it falls apart. The scope is not set as intended, instead the left view always gets the focus. Am I missing something? Is this intended? I’m a little lost right now. I am targeting tvOS 16.4 and using Xcode 14.3.1. Fun fact: The simulator view inside Xcode displays the view correctly. The simulator and an actual device are not. Here is an example to showcase that: struct ContentView: View { @Namespace private var mainNamespace @Namespace private var rightNamespace var body: some View { // NavigationView { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) Text("Hello, world!") HStack { VStack { Button("left") {} } VStack { Button("right 1") {} Button("right 2") {} .prefersDefaultFocus(in: rightNamespace) } .focusScope(rightNamespace) .prefersDefaultFocus(in: mainNamespace) } .focusScope(mainNamespace) .padding(.top, 50) } .padding() // } } }
Posted
by RayWo.
Last updated
.
Post marked as solved
1 Replies
1.3k Views
I am using Xcode 15 and working on a localised app. I use the new String Catalogs feature which works great for my app. In my app I created some local package like Apple has done it in the Backyard Birds example. However the translations I did in the package's String Catalog won’t be used in the app. What am I doing wrong?
Posted
by RayWo.
Last updated
.
Post marked as solved
1 Replies
678 Views
For my app I use a struct for my CoreData persistence controller like so: struct PersistenceController { static var shared = PersistenceController() // more code omitted for brevity } As a development asset I made an extension to the PersistenceController to provide a special instance for previews with some sample data. extension PersistenceController { static var preview: PersistenceController = { let result = PersistenceController(inMemory: true) // create some sample data return result }() } Now I want to use the special preview instance with the new #Preview macro in Xcode beta 2. The deployment target for the app is set to iOS 17. #Preview { var context = PersistenceController.preview.container.viewContext return SpanList() .preferredColorScheme(.light) .environment(\.managedObjectContext, context) } But the preview fails to build due to the following error: Type 'PersistenceController' has no member 'preview'. My experiments show that everything I add to the PersistenceController via an extension is not available in the preview macro. This is no matter where I place the extension. Even when it’s placed in the same file as the PersistenceController itself. Am I missing something or is this intended behaviour?
Posted
by RayWo.
Last updated
.
Post not yet marked as solved
1 Replies
533 Views
I’ve got this simple view and it doesn’t work as expected. When clicking the button a new Eventis created and the flag used as binding for a sheet is set to true. This should show the sheet and give the sheet access to the Event. Instead I get the error message Fatal error: Unexpectedly found nil while unwrapping an Optional value. I can’t see the problem. As far as I see this the tmpEvent variable should have a valid value once the sheet is presented. import SwiftUI struct TestView: View {   @Environment(\.managedObjectContext) private var viewContext   @State private var showingSheet = false   @State private var tmpEvent: Event?     var body: some View {     VStack {       Button(action: showSheet) {         Label("Add event", systemImage: "plus.circle")       }     }     .sheet(isPresented: $showingSheet) {       Text("event title: \(tmpEvent!.descriptionString)"). // Fatal error occurs here     }   }    private func showSheet() {     tmpEvent = Event(context: viewContext)     showingSheet = true   } } Any ideas why this isn’t working?
Posted
by RayWo.
Last updated
.
Post not yet marked as solved
0 Replies
740 Views
In my app I’ve got a list view and a view for displaying the rows in the list. When I delete an item from that list the app crashes in the row view stating an illegal access. I don’t understand why the row view is rendered again for the deleted item. The list view: import Foundation import SwiftUI struct SpanList: View {   @Environment(\.managedObjectContext) private var viewContext    @FetchRequest(     sortDescriptors: [ NSSortDescriptor(keyPath: \Span.startDate,                                         ascending: true) ],     predicate: NSPredicate(format: "isArchived != YES"),     animation: .default)   private var activeSpans: FetchedResults<Span>   var body: some View {     NavigationView {       VStack {         List {           Section(header: ActiveSectionHeader()) {             ForEach(activeSpans, id: \.id) { item in               NavigationLink(destination: getConfiguredEditView(span: item)) {                 SpanRow(span: item)               }               .buttonStyle(PlainButtonStyle())               .listRowBackground(item.color.opacity(0.5))             }             .onDelete(perform: deleteItems)           }         }         .listStyle(GroupedListStyle())       }     }   }   private func getConfiguredEditView(span: Span) -> some View {     EditSpan(span: span)       .navigationTitle("Edit Span")       .navigationBarTitleDisplayMode(.inline)   }   private func deleteItems(offsets: IndexSet) {     withAnimation {       // Necessary due to a bug in the framework. Without this the app crashes       // with a bug stating multiple access from different threads is not       // allowed.       viewContext.perform {         offsets.map {           activeSpans[$0]         }.forEach(viewContext.delete)         PersistenceController.shared.save()       }     }   } } The row view: import SwiftUI import CoreData struct SpanRow: View {   @ObservedObject var span: Span   var body: some View {     HStack(alignment: .center) {       VStack(alignment: .leading) {         HStack {           if span.showOnStartPage && !span.isArchived {             Image(systemName: "paperclip")           }           Text(span.descriptionString)             .font(.headline)             .fontWeight(.bold)         }         Spacer()         HStack { // It crashes here           Text("\(span.startDate, formatter: dateFormatter)")           Text("–")           if span.usesEndDate {             Text("\(span.endDate, formatter: dateFormatter)")           } else {             Image(systemName: "infinity")           }         }       }       Spacer()       if let daysSinceStart = span.daysSinceStart {         VStack {           Text("\(daysSinceStart)")             .font(.largeTitle)             .fontWeight(.semibold)           Text("days")         }       }     }     .padding(.vertical, 5)   }   private let dateFormatter: DateFormatter = {     let formatter = DateFormatter()     formatter.dateStyle = .medium     return formatter   }() } What am I missing?
Posted
by RayWo.
Last updated
.