I’m building a SwiftUI app where the struct AppGroup is identified by a UUID and stored in a dictionary. My Task model has appGroupId: UUID?. In TaskDetailView, I create a custom Binding<AppGroup> from the store, then navigate to AppGroupDetailView. However, when I tap the NavigationLink, the console spams logs, CPU hits 100%, and it never stabilizes.
Relevant Code
AppGroupStore (simplified)
class AppGroupStore: ObservableObject {
@Published var appGroupsDict: [UUID: AppGroup] = [:]
func updateAppGroup(_ id: UUID, appGroup: AppGroup) {
appGroupsDict[id] = appGroup
}
// Returns a binding so views can directly read/write the AppGroup by id
func getBinding(withId id: UUID?) -> Binding<AppGroup> {
Binding(
get: {
if let id = id {
return self.appGroupsDict[id] ?? .empty
}
return .empty
},
set: { newValue in
print("New value set for \(newValue.name)")
self.updateAppGroup(newValue.id, appGroup: newValue)
}
)
}
// ...
}
AppGroup is a simple struct:
struct AppGroup: Identifiable, Codable {
let id: UUID
var name: String
var apps: [String]
static let empty = AppGroup(id: UUID(), name: "Empty", apps: [])
}
TaskDetailView (main part)
struct TaskDetailView: View {
@Binding var task: ToDoTask // has task.appGroupId: UUID?
@EnvironmentObject var appGroupStore: AppGroupStore
var body: some View {
let appGroup = appGroupStore.getBinding(withId: task.appGroupId)
print("Task load") // prints infinitely, CPU 100%
return List {
// ...
NavigationLink(destination: AppGroupDetailView(appGroup: appGroup)) {
Text(appGroup.wrappedValue.name)
}
}
.navigationTitle(task.name)
}
}
AppGroupDetailView (simplified)
struct AppGroupDetailView: View {
@Binding var appGroup: AppGroup
// ...
var body: some View {
List {
ForEach(appGroup.apps, id: \.self) { app in
Text(app)
}
}
.navigationTitle(appGroup.name)
}
}
Symptoms:
Tapping the NavigationLink leads to infinite “Task load” logs and 100% CPU usage.
The set closure (“New value set for...”) is never called, so it’s not repeatedly writing.
If I replace the Binding<AppGroup> with a read-only approach (just accessing the dictionary), it does not get stuck.
Question:
What might cause SwiftUI to keep re-rendering the body indefinitely, even if my custom get closure doesn’t explicitly mutate the state? Are there known pitfalls when using a dictionary-based store and returning a Binding like this?
Any help is much appreciated!
Thanks in advance for your insights!
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I must be missing something here. I want to put a landscape image in a geometry reader that contains a ZStack that contains an image and an overlay centred on top of the Image.
I would like the ZStack and GeoReader's sizes to be the size of Image. (ie I want geometry.size to be the size of the image, which can be used to control the offset of the overlay's position.)
Unfortunately the ZStack also includes the space above the image (ie the top safeArea) and the GeometryReader also includes all the space below the Image. (so geometry.size.height is greater than the height of Image)
I've gone down rabbit holes of adding other items above/below, but I don't seem to be able to prevent the GeometryReader from being vertically greedy.
eg the Text(" ") above the ZStack in the VStack solves the ZStack claiming the top safe area. But adding Text(" ") below the ZStack does not prevent the GeometryReader from claiming more vertical space below the image.
Any/all guidance greatly appreciated.
struct ContentView: View {
var body: some View {
VStack {
// Text(" ")
GeometryReader { geometry in
ZStack {
Image(
uiImage: .init(imageLiteralResourceName: "LandscapeSample")
)
.resizable()
.aspectRatio(contentMode: .fit)
Text("Hello, world!")
.background(.white)
}
.background(.red)
}
.background(.blue)
// Text(" ")
}
}
}
Hi,
I'd like to call an Async function upon a state change or onAppear() but I'm not sure how to do so. Below is my code:
.onAppear() {
if !subscribed {
await Subscriptions().checkSubscriptionStatus()
}
}
class Subscriptions {
var subscribed = UserDefaults.standard.bool(forKey: "subscribed")
func checkSubscriptionStatus() async {
if !subscribed {
await loadProducts()
}
}
func loadProducts() async {
for await purchaseIntent in PurchaseIntent.intents {
// Complete the purchase workflow.
await purchaseProduct(purchaseIntent.product)
}
}
func purchaseProduct(_ product: Product) async {
// Complete the purchase workflow.
do {
try await product.purchase()
}
catch {
// Add your error handling here.
}
// Add your remaining purchase workflow here.
}
}
All,
Thanks in advance! I'm having a very hard time increasing the MTU to any value beyond 20. The research I've done states iOS 16.1 and beyond supports up to 512 bytes. Yet, the peripheral device will only read 20 bytes.
It's to be noted that I'm using Expo SDK 51 Bare Workflow, and the react-native-ble-plx library. I have the app functioning as both Central and Peripheral on iOS 18.1 devices, and data is successfully being written and read to the characteristic.
Because the Expo app is Bare Workflow, I'm able to make any configurations via Xcode, and if there is any patches needed to the react-native-ble-plx library, we have the architecture to support that too. I wanted to provide that context before being recommended to go to the Expo forums (which I have/will be).
I also added the CoreBluetooth framework to the project in hopes that would overwrite the react-native-ble-plx imports, but I noticed react-native-ble-plx uses Swift while CoreBluetooth is Objective-C.
Looking forward to your responses!
I am trying to give bottom padding to tabbar i.e ** tabBarFrame.origin.y = view.frame.height - tabBarHeight - 30** but it is not moving up from bottom, it gets sticked to bottom = 0 and the tabbar content moving up taher than tabbar itself..
Code snippet is -
`i override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let tabBarHeight: CGFloat = 70 // Custom height for the capsule tab bar
var tabBarFrame = tabBar.frame
tabBarFrame.size.height = tabBarHeight
tabBarFrame.size.width = view.frame.width - 40
tabBarFrame.origin.y = view.frame.height - tabBarHeight - 30
tabBarFrame.origin.x = 20
tabBar.frame = tabBarFrame
tabBar.layer.cornerRadius = tabBarHeight / 2
tabBar.clipsToBounds = true
view.bringSubviewToFront(tabBar)
}`
Can anyone please help to resolve the issue for iOS 18, it is coming in iOS 18 rest previous versions are fine with the code.
I tried to update my ios from 17.2 to 18.1 on my iphone 14 pro. I use this device for testing my apps. when i go to my sdk, i got double back button and when i clicked the back button it will go to blank screen
here is the ss
double back button
got blank screen
its never happened on ios 17 and below
i use coordinator and UINavigationController
anyone have solutions?
I'm developing a grid of focusable elements in SwiftUI with different sizes for tvOS (similar to a tv channel grid).
Because the Focus Engine calculates the next view to focus based on the center of the currently focused view, sometimes it changes focus to an unexpected view. Here's an example:
Actual:
Expected:
Is it possible to customize the anchor point from which the focus engine traces a ray to the next view? I would prefer the leading edge in my case.
Below is my sample code. On the Home page, when I click "show sheet," the sheet page expands, and the StateObject inside the sheet is initialized once. However, when I click "show Fullscreen" and then click "show sheet" inside the fullscreen page, the sheet gets initialized twice.
However, if I remove navigationDestination, this issue does not occur.
This problem causes the network request in the sheet page to be triggered multiple times.
Can someone tell me the reason?
enum TestRouter: String, Hashable {
case test
var targetView: some View {
Text("test")
}
var title: String {
return "test title"
}
}
@MainActor
struct NavigationInnerView<Content>: View where Content: View {
var contentView: () -> Content
@MainActor public init(@ViewBuilder contentView: @escaping () -> Content) {
self.contentView = contentView
}
var body: some View {
NavigationStack() {
contentView()
.navigationDestination(for: TestRouter.self) { route in
route.targetView
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentView: View {
@State var showFullScreen: Bool = false
@State var showSheet: Bool = false
var contentView: some View {
VStack {
VStack {
Text("Home")
Button {
showFullScreen = true
} label: {
Text("show fullscreen")
}
Button {
showSheet = true
} label: {
Text("show sheet ")
}
}
}
}
var body: some View {
NavigationInnerView {
contentView
.fullScreenCover(isPresented: $showFullScreen) {
NavigationInnerView {
FullScreenContentView()
}
}
.sheet(isPresented: $showSheet) {
NavigationInnerView {
SheetContentView()
}
}
}
}
}
class FullScreenViewModel: ObservableObject {
@Published var content: Bool = false
init() {
print("Full Screen ViewModel init")
}
}
struct FullScreenContentView: View {
@Environment(\.dismiss) var dismiss
@State var showSheet: Bool = false
@StateObject var viewModel: FullScreenViewModel = .init()
init() {
print("Full screen view init")
}
var body: some View {
VStack {
Text("FullScreen")
Button {
dismiss()
}label: {
Text("dismiss")
}
Button {
showSheet = true
} label: {
Text("show sheet")
}
}
.sheet(isPresented: $showSheet) {
NavigationInnerView {
SheetContentView()
}
}
}
}
class SheetViewModel: ObservableObject {
@Published var content: Bool = false
init() {
print("SheetViewModel init")
}
}
struct SheetContentView: View {
@Environment(\.dismiss) var dismiss
@StateObject var viewModel = SheetViewModel()
init() {
print("sheet view init")
}
var body: some View {
Text("Sheet")
Button {
dismiss()
} label: {
Text("dismiss")
}
}
}
#Preview {
ContentView()
}
I'm working on a SwiftUI based application for MacOS. I have a TabView component with two child Tab components. These Tab components display a List, each derived from an array of elements.
While the application is running, clicking on the tabs in the TabView should switch between the views of different Lists. What I'm experiencing is that switching between the tabs causes a FAULT. With errors:
Row index 1 out of row range (numberOfRows: 1) for <SwiftUI.SwiftUIOutlineListView: 0x1299d2000>
Followed by:
(
0 CoreFoundation 0x000000019e096e80 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x000000019db7ecd8 objc_exception_throw + 88
2 AppKit 0x00000001a1c744e8 -[NSTableRowData _availableRowViewWhileUpdatingAtRow:] + 0
3 SwiftUI 0x00000001cd8953f4 $s7SwiftUI0A17UIOutlineListViewC11removeItems2at8inParent13withAnimationy10Foundation8IndexSetV_ypSgSo07NSTableeL7OptionsVtF + 1232
...
...
)
And finally:
FAULT: NSTableViewException: Row index 1 out of row range (numberOfRows: 1) for <SwiftUI.SwiftUIOutlineListView: 0x1299d2000>; (user info absent)
This error happens when switching between the two tabs, defined thusly:
@main
struct MyApp: App {
@State var rootDirectory: URL
@State var selectedItem: URL
@State var projectNavItems: [NavigationItem] = []
@State var jotNavItems: [NavigationItem] = []
@State var importerIsPresented: Bool = false
let fileManager = FileManager.default
init() {
rootDirectory = URL(string: FileManager.default.currentDirectoryPath)!
selectedItem = URL(string: FileManager.default.currentDirectoryPath)!.appendingPathComponent("README.md")
}
var body: some Scene {
WindowGroup {
NavigationSplitView {
TabView {
Tab("Projects", systemImage: "tray.and.arrow.down.fill") {
List(projectNavItems, selection: $selectedItem) {
// Changing this NavigationLink line to Text($0.title) makes no difference
NavigationLink($0.title, value: $0.id)
}
}
Tab("Jots", systemImage: "tray.and.arrow.up.fill") {
List(jotNavItems, selection: $selectedJot) {
// Can be written as Text($0.title) with no change in behavior
NavigationLink($0.title, value: $0.id)
}
}
}
} detail: {
Editor(for: selectedItem)
}
.fileImporter(
isPresented: $importerIsPresented,
allowedContentTypes: [UTType.folder],
allowsMultipleSelection: false
) { result in
// Code that gets a security scoped resource and populates the
// projectNavItems: [NavItem] and jotNavItems: [NavItem]
// arrays
}
}
.commands(content: {
CommandGroup (before: .newItem) {
Button("Open Journal...") {
importerIsPresented.toggle()
}
}
})
}
}
The error only happens when both Tab views are populated by a List. If the Tab view have different child components, say a List, and a ForEach of Text components, switching between the tabs doesn't produce this error. List views with Text child components also produce this error.
Here are screenshots of the running application
Once the user selects a directory, we see the first Tab > List component populated by contents from the projectNavItems array:
Clicking on the 'Jots' tab switches to the appropriate tab and correctly lists the items in the jotNavItems array, except there are additional lines, seemingly showing that there's an issue.
Clicking back on the 'Projects' tab switches back, but now the List shows only one of the items from the projectNavItems array.
Finally, clicking on 'Jots' again causes the errors to print in the console and interactivity with the tab components ceases. Last screenshot is representative of this state as the application FAULTS.
This seems like a bug in SwifUI, wondering what workarounds I might be able to implement.
I can provide the full backtrace, I cropped it for content length.
I’m experiencing significant performance and memory management issues in my SwiftUI application when displaying a large number of images using LazyVStack within a ScrollView. The application uses Swift Data to manage and display images.
Here’s the model I’m working with:
@Model
final class Item {
var id: UUID = UUID()
var timestamp: Date = Date.now
var photo: Data = Data()
init(photo: Data = Data(), timestamp: Date = Date.now) {
self.photo = photo
self.timestamp = timestamp
}
}
extension Item: Identifiable {}
The photo property is used to store images. However, when querying Item objects using Swift Data in a SwiftUI ScrollView, the app crashes if there are more than 100 images in the database.
Scrolling down through the LazyVStack loads all images into memory leading to the app crashing when memory usage exceeds the device’s limits.
Here’s my view: A LazyVStack inside a ScrollView displays the images.
struct LazyScrollView: View {
@Environment(\.modelContext) private var modelContext
@State private var isShowingPhotosPicker: Bool = false
@State private var selectedItems: [PhotosPickerItem] = []
@Query private var items: [Item]
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(items) { item in
NavigationLink {
Image(uiImage: UIImage(data: item.photo)!)
.resizable()
.scaledToFit()
} label: {
Image(uiImage: UIImage(data: item.photo)!)
.resizable()
.scaledToFit()
}
}
}
}
.navigationTitle("LazyScrollView")
.photosPicker(isPresented: $isShowingPhotosPicker, selection: $selectedItems, maxSelectionCount: 100, matching: .images)
.onChange(of: selectedItems) {
Task {
for item in selectedItems {
if let data = try await item.loadTransferable(type: Data.self) {
let newItem = Item(photo: data)
modelContext.insert(newItem)
}
}
try? modelContext.save()
selectedItems = []
}
}
}
}
}
Based on this:
How can I prevent SwiftUI from loading all the binary data (photo) into memory when the whole view is scrolled until the last item?
Why does SwiftUI not free memory from the images that are not being displayed?
Any insights or suggestions would be greatly appreciated. Thank you!
I will put the full view code in the comments so anyone can test if needed.
I have an app on the Mac App Store (so sandboxed) that includes a QuickLook Preview Extension that targets Markdown files. It established a QLPreviewingController instance for the macOS QuickLook system to access and it works.
I'm in the process of updating it so that it displays inline images referenced in the file as well as styling the file's text. However, despite setting Downloads folder read-only access permission (and user-selected, though I know that shouldn't be required: no open/save dialogs here) in the extension's entitlements, Sandbox refuses too allow access to the test image: I always get a deny(1) file-read-data error in the log.
FWIW, the test file is referenced in the source Markdown as an absolute unix file path.
I've tried different signings and no joy. I’ve tried placing the referenced image in various other locations. Also no joy. All I can display is the error-case bundle image for 'missing image'.
Question is, is this simply something that QuickLook extensions cannot do from within the sandbox, or am I missing something? Is there anything extra I can do to debug this?
Starting from Sequoia release 15.2 apps crash with following call stack, when adding static text controls. First call to [NSTextField setStringValue] causes following crash
0 libobjc.A.dylib 0x19f2f5820 objc_msgSend + 32
1 AppKit 0x1a3355460 -[NSCell _objectValue:forString:errorDescription:] + 144
2 AppKit 0x1a3355348 -[NSCell setStringValue:] + 48
3 AppKit 0x1a33af9fc -[NSControl setStringValue:] + 104
4 AppKit 0x1a3d1f190 -[NSTextField setStringValue:] + 52
It happens on specific MacBook Pro models(16 in MacBook Pro).
Crash analysis found that isa pointer of the object was corrupted for the object NSCell. Enabled Zombies in debugging, not found any issue. Also tried address sanitizer. Since the issue started with a recent release of macOS, any changes in the Appkit in the recent releases trigger the crash? Any help/suggestions would be appreciated.
Topic:
UI Frameworks
SubTopic:
AppKit
Hi! I'm trying to do a forEach loop on an array of objects. Here's my code :
ForEach($individus) { $individu in
if individu.reussite == true {
individu.score -= 10
} else {
individu.score = (individu.levees * 10) + 20 + individu.score
}
}
I have an error on the code in the 'if' saying that "Type '()' cannot conform to 'View'", but I have no idea on how solving this problem.
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hello.
I have a scenario where a hover effect is being shown for a button that is disabled. Usually this doesn't happen but when you wrap the button in a Menu it doesn't work properly.
Here is some example code:
struct ContentView: View {
var body: some View {
NavigationStack {
Color.green
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Menu("Menu") {
Button("Disabled Button") {}
.disabled(true)
.hoverEffectDisabled() // This doesn't work.
Button("Enabled Button") {}
}
}
}
}
}
}
And here is what it looks like:
This looks like a SwiftUI bug. Any help is appreciated, thank you!
I'm testing using Group Activities and having no trouble iOS<->iOS or starting an activity on macOS and joining via iOS. However, when I start an activity and then try to join it from another macOS client, the starting side joins the session just fine, but the receiving side acts like I don't have the required app, even when it is already running.
I see the active SharePlay icon in the menu bar, and the Current Activity is shown, but instead of an "Open" button there is a "MyApp Required" string and a "View" button that goes to the App Store. (Where the app is not available yet, as expected, since I'm still working on it.) There is no GroupSession started on that Mac yet, obviously.
I'm looking for any hints to help debug what is going on. How does Group Activities find the app for the activity on macOS and how can I figure out why it isn't finding mine?
Thanks!
I am trying to upload a ZIP file, created by compressing other files in iCloud Drive through the Files app, using UIDocumentPicker. However, errors like those described in Test 1 and Test 2 in the sample code are occurring.
import UIKit
class ViewController: UIViewController,UIDocumentPickerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func openDocumentPicker(_ sender: Any) {
// Test 1
// let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.item], asCopy: true)
// When clicking the "Open" button, there is no response, and the following error occurs.
/*
Failed to copy the imported file into the local container ((null))
Tried to call delegate -documentBrowser:didPickDocumentURLs: with an empty array of items. This indicates the items failed to be prepared and materialized on disk: (
"<DOCItemBookmark: 0x303ad2fc0> FPItem=(null)"
)
*/
// Test 2
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.item])
// When using the regular mode instead of the copy mode,
// the "Open" button works, and the selectedFileURL is correctly retrieved.
// However, even after using startAccessingSecurityScopedResource,
// attempting to read the file results in the following error.
/*
Error reading file: Error Domain=NSCocoaErrorDomain Code=257 "The file “archive 4.zip” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/samplefiles/archive 4.zip, NSURL=file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/%E1%84%89%E1%85%A2%E1%86%B7%E1%84%91%E1%85%B3%E1%86%AF%E1%84%91%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AF/%E1%84%8B%E1%85%A1%E1%84%8F%E1%85%A1%E1%84%8B%E1%85%B5%E1%84%87%E1%85%B3%204.zip, NSUnderlyingError=0x300bedbf0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}
*/
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = true
self.present(documentPicker, animated: true, completion: nil)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard urls.isEmpty == false else {
print("no urls")
return
}
//Test2
for selectedFileURL in urls {
// Start accessing the security-scoped resource
if selectedFileURL.startAccessingSecurityScopedResource() {
defer { selectedFileURL.stopAccessingSecurityScopedResource() }
// Perform operations on the file
do {
let fileCoordinator = NSFileCoordinator()
var error: NSError?
fileCoordinator.coordinate(readingItemAt: selectedFileURL, options: [], error: &error) { (newURL) in
do {
let fileData = try Data(contentsOf: newURL)
// Process the file data
print("File data read successfully")
} catch {
print("Error reading file: \(error)")
}
}
if let error = error {
print("Error coordinating file access: \(error)")
}
} catch {
print("Error reading file: \(error)")
}
} else {
print("Failed to access security-scoped resource")
}
}
}
It seems that there might be an issue with the compressed ZIP file. How can this be resolved?
Topic:
UI Frameworks
SubTopic:
General
I've been trying to add a header to the tabSection of the tabview in tvos 18+ .
init(
@TabContentBuilder<SelectionValue> content: () -> Content,
@ViewBuilder header: () -> Header
) where Header : View, Footer == EmptyView
Here the ehader clearly conforms to View but i cant quite fit the label with uiimage as the icon into this. This Label when i add it to any other view, the image is in the specified 50 x 50 size but inside header it functions weirdly to be of a huge size. but also to note, if i simply hav an icon here, it is correct. So what is the problem here.. can someone help me? im supposed to add the user profile and name in the header. I dont think there's any other way
Is there a way to observe the currentEDRHeadroom property of UIScreen for changes? KVO is not working for this property...
I understand that I can query the current headroom in the draw(...) method to adapt the rendering. However, our apps only render on-demand when the user changes parameters. But we would also like to re-render when the current EDR headroom changes to adapt the tone mapping to the new environment.
The only solution we've found so far is to continuously query the screen for changes, which doesn't seem ideal. It would be better if the property would be observable via KVO or if there would be a system notification to listen for.
Thanks!
I am really confused by some of the new data returned in WeatherKit for iOS 18.
The visibility (of an object) was already being returned in HourWeather as a Measurement.
iOS 18 added max/min visibility (of terrain) in DayWeather. BUT instead of a Measurement it's just a Double.
HourWeather:
/// The distance at which an object can be clearly seen.
///
/// The amount of light and weather conditions like fog, mist, and smog affect visibility.
public var visibility: Measurement<UnitLength>
DayWeather's comment:
/// The maximum distance at which terrain is visible for the day.
///
/// The amount of light, and weather conditions like fog, mist, and smog affect visibility.
@available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
public var maximumVisibility: Double
This makes it sound like the new items are also a distance and not a percentage.
Why wasn't Measurement used so the unit would be clear? Documentation doesn't explain this either. I'm hoping that this isn't being returned in the unit used by the current locale because my app lets you specify what unit to use for temperature, length, etc regardless of locale. Since all the temperature, length, etc data returned had used Measurement that was possible.
The iOS weather app refers to the lowest/highest visibility in my preferred unit, which is miles.
Hi,
I'm working on an app that will mostly live in the menu bar.
I'm trying to make a menu item that looks similar to the Tailscale app's menu:
Note: I'm inspired by how Tailscale's menu is rendered:
I have made a View that shows my avatar, name, and optionally the company I work for:
import SwiftUI
struct MenuWhoAmI: View {
var username: String
var binding: String?
var body: some View {
HStack {
AsyncImage(url: URL(string: "https://avatars.githubusercontent.com/u/76716")!){ image in
image.resizable().scaledToFit()
} placeholder: {
ProgressView()
}
.clipShape(Circle())
VStack(alignment: .leading) {
Text(username)
if let binding = binding {
Text("\(binding)").foregroundStyle(.secondary)
}
}
}
}
}
#Preview {
VStack(alignment: .leading) {
MenuWhoAmI(username: "grahamc").padding()
Divider()
MenuWhoAmI(username: "grahamc", binding: "DeterminateSystems").padding()
}.padding()
}
I tried using it in my menu bar:
import SwiftUI
@main
struct DeterminateApp: App {
var body: some Scene {
MenuBarExtra("Determinate", image: "MenuIcon") {
MenuWhoAmI(username: "grahamc")
Button("Two") {}
Button("Three") {}
Divider()
Button("Quit") {
NSApplication.shared.terminate(nil)
}.keyboardShortcut("q")
}.menuBarExtraStyle(.menu)
}
}
and it renders differently:
After reading the forums and documentation, I understood the MenuBarExtra only renders certain elements. I then tried to use an NSStatusBar with an AppDelegate:
import AppKit
import SwiftUI
@main
struct DeterminateApp: App {
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
var body: some Scene {
Window("Authentication", id: "login") {}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private var statusItem: NSStatusItem!
func applicationDidFinishLaunching(_ notification: Notification) {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem.button {
button.image = NSImage(named: NSImage.Name("MenuIcon"))
}
statusItem.menu = NSHostingMenu(rootView: Group {
Button(action: { print("hi") }) {
MenuWhoAmI(username: "grahamc")
}
})
}
}
and still, the avatar/name doesn't render like I'd expect, missing the circle clipping:
...and I'm a bit mystified.
How can I make this menu render the way I'm trying for?
Thank you!