Obrazki w pozycjach menu znikają w macOS 27 i iPadOS 27
Pozycja menu z obrazkiem i bez tytułu w ogóle się nie wyrenderuje w macOS 27, jeśli aplikacja została zlinkowana względem SDK macOS 27. Na wcześniejszych SDK Apple chroni ten przypadek, automatycznie pokazując obrazek wtedy, gdy tytuł pozycji menu i jej tytuł atrybutowany są puste.1 Wystarczy przebudować projekt względem 27, a ochrona przestaje obowiązywać.
macOS 27 i iPadOS 27 domyślnie ukrywają większość obrazków w pozycjach menu, a to, co znika, zależy od tego, względem którego SDK aplikacja została zlinkowana. Apple opisuje efekt jako „podobny do zachowania sprzed macOS 26.0”, co w praktyce znaczy tyle, że macOS 26 wprowadził obrazki w menu na szeroką skalę, a 27 większość z nich zabiera z powrotem.1
Zmiana dotyczy trzech frameworków i każdy z nich ma inny sposób wyłączenia tego zachowania. Jeśli ktoś szukał preferredImageVisibility i pisze w SwiftUI — ta właściwość dla niego nie istnieje.
W skrócie
macOS 27 domyślnie ukrywa obrazki symboli w pozycjach menu w aplikacjach zlinkowanych względem macOS 26.0 i nowszych; aplikacje zlinkowane względem SDK macOS 27 tracą również obrazki niebędące symbolami.12 Pozycje menu złożone wyłącznie z ikony są automatycznie chronione na SDK sprzed 27 i tracą tę ochronę po przebudowie względem 27.3 iPadOS 27 domyślnie ukrywa obrazki ustawione na elementach menu.4 AppKit i UIKit udostępniają preferredImageVisibility z wartościami .automatic, .visible i .hidden; SwiftUI używa w zamian labelStyle(.titleAndIcon).5 Pozycje Settings, Share i Print zachowują swoje obrazki systemowo, więc część ikon pozostanie widoczna — i łatwo wtedy uznać, że to własne zasoby przestały działać.
Co dokładnie się zmienia, zależnie od SDK
Zachowanie AppKit opisano w trzech osobnych wpisach w release notes, z czego dwa zgłoszono jako poprawki — dlatego streszczenia tej zmiany bywają ze sobą sprzeczne.
| Zlinkowane względem | Obrazki symboli | Obrazki niebędące symbolami | Pozycje tylko z ikoną |
|---|---|---|---|
| SDK sprzed macOS 26 | bez zmian | bez zmian | bez zmian |
| macOS 26.0 do 26.x | ukryte | widoczne | pokazywane automatycznie |
| SDK macOS 27 | ukryte | ukryte | ukryte |
Wpis bazowy mówi, że NSMenu domyślnie ukrywa wszystkie obrazki symboli w pozycjach menu, podczas gdy obrazki niebędące symbolami pozostają widoczne, oraz że zmiana obejmuje aplikacje zlinkowane względem macOS 26.0 i nowszych.1
Drugi wpis rozszerza zakres ukrywania: „W aplikacjach zlinkowanych względem SDK macOS 27 automatycznie ukrywane są zarówno obrazki symboli, jak i obrazki niebędące symbolami. W aplikacjach zlinkowanych względem wcześniejszych SDK obrazki niebędące symbolami pozostają automatycznie widoczne, co zachowuje zgodność z dotychczasowym zachowaniem aplikacji.”2
Trzeci wpis wart jest przeczytania dwa razy:3
„W aplikacjach zlinkowanych względem SDK sprzed macOS 27
NSMenuautomatycznie pokazuje teraz obrazki pozycji menu, jeżeli tytuł pozycji i jej tytuł atrybutowany są puste. Zachowuje to dotychczasowe zachowanie aplikacji w sytuacji, gdy obrazek jest jedyną reprezentacją treści pozycji menu. Przy linkowaniu względem SDK macOS 27 obrazki te będą automatycznie ukrywane; menu o takiej konstrukcji powinno użyć APIpreferredImageVisibility, aby obrazki pozycji menu pozostały widoczne.”
Apple zbudowało siatkę bezpieczeństwa dla pozycji menu złożonych wyłącznie z ikony, a następnie udokumentowało, że siatka ta nie obejmuje nowego SDK. Pozycja menu, której całą treść stanowi obrazek, po przebudowie renderuje się jako pusta. W kodzie źródłowym nic się nie zmienia. Wyzwalaczem jest SDK, względem którego skompilowano aplikację.
Pozycje menu złożone wyłącznie z ikony są mniej egzotyczne, niż się wydaje. Próbki kolorów w menu formatowania, gdzie to właśnie próbka jest wyborem. Selektory urządzeń albo kont, w których każdy wpis rozpoznaje się po awatarze lub glifie statusu. Wiersze reakcji i emoji zbudowane jako pozioma listwa pozycji zawierających wyłącznie obrazki. Listy ostatnich dokumentów pokazujące ikony typu pliku, przy czym nazwa renderowana jest osobno. W każdym z tych przypadków obrazek nie jest ozdobą doklejoną do etykiety — on jest etykietą.
Takie menu degradują się do kolumny pustych wierszy, które nadal reagują na kliknięcia. Menu zachowuje swoją wysokość, separatory i obszary trafienia, więc dla automatu nie wygląda to na błąd renderowania. Test UI sprawdzający, że menu ma sześć pozycji, wciąż przechodzi. Porównanie zrzutów ekranu wychwyci problem; asercja na liczbie pozycji — nie.
Rodzinne podobieństwo widać właśnie w tej ciszy. macOS 27 odmawia też dostępu do kontenerów innego zespołu bez pytania — tam API zwraca poprawnie wyglądający URL, a awaria czeka aż do momentu odczytu. Obie zmiany zastępują widoczny sygnał ciszą i obie ujawniają się jako coś innego, niż są w rzeczywistości.
Trzy frameworki, trzy rozwiązania
| Framework | API | Wartości |
|---|---|---|
| AppKit | NSMenuItem.preferredImageVisibility |
.automatic, .visible, .hidden |
| UIKit | UIMenuElement.preferredImageVisibility |
.automatic, .visible, .hidden |
| SwiftUI | labelStyle(.titleAndIcon) |
styl etykiety, nie typ wyliczeniowy |
AppKit i UIKit mają identyczny kształt rozwiązania. Obie właściwości przyjmują typ wyliczeniowy ImageVisibility z wartościami .automatic, .visible i .hidden, obie są RawRepresentable nad NSInteger i obie są Sendable.67
// AppKit
menuItem.preferredImageVisibility = .visible
// UIKit — also available on the updated initializers
// for UIMenu, UIAction, UICommand, and UIKeyCommand
let action = UIAction(title: "Export", image: exportImage) { _ in export() }
action.preferredImageVisibility = .visible
SwiftUI idzie inną drogą. Jego wpis w release notes mówi, aby „użyć modyfikatora widoku labelStyle(_:) ze stylem .titleAndIcon, aby wskazać, że ikona etykiety Label w pozycji menu powinna być zawsze widoczna.”5
Menu("File") {
Button {
openDocument()
} label: {
Label("Open Document", systemImage: "doc")
}
.labelStyle(.titleAndIcon)
}
Warto też odnotować, że domyślne zachowanie SwiftUI odpowiada wariantowi bazowemu AppKit, a nie zachowaniu SDK 27: obrazki symboli ukryte, obrazki niebędące symbolami nadal widoczne.5 Powyższa tabela powiązań z SDK opisuje AppKit. Nie należy zakładać, że przenosi się na SwiftUI.
Zakres jest węższy, niż się wydaje
Sformułowania dotyczące platform trzeba czytać uważnie, bo poszczególne wpisy się różnią.
Wpis UIKit obejmuje „pasek menu i menu kontekstowe” w iPadOS 27.0 i macOS 27.0.4 Wpis SwiftUI jest bardziej konkretny: pasek menu w iPadOS 27.0 i macOS 27.0 „oraz menu kontekstowe w macOS 27.0”.5 Menu kontekstowe SwiftUI w iPadOS nie zostały wymienione.
UIMenuElement.preferredImageVisibility jest dostępne w iOS, iPadOS, Mac Catalyst, tvOS i visionOS 27.0.7 To, że API pojawia się na danej platformie, nie oznacza jeszcze, że działa tam mechanizm ukrywania. Release notes Apple opisują to zachowanie dla iPadOS i macOS. tvOS i visionOS należy traktować jako przypadek nieopisany, a nie potwierdzony.
Warstwa Interface Buildera
Jeden szczegół nie ma odpowiednika w kodzie. W przypadku pozycji menu tworzonych z pliku xib NSMenu bierze pod uwagę pole wyboru „macOS 26.0 only” w inspektorze pozycji menu: odznaczone zachowuje obrazek widoczny, zaznaczone go ukrywa.1
Właściwość w Interface Builderze zmienia teraz widoczność obrazka w czasie działania aplikacji. Jeżeli menu pochodzą z xibów, audyt nie sprowadza się do przeszukania kodu. Ktoś musi otworzyć inspektor.
Dlaczego niektóre ikony przetrwały
Zarówno AppKit, jak i UIKit nadal dostarczają domyślnie widoczne obrazki dla typowych, ogólnosystemowych pozycji menu, takich jak Settings, Share i Print.14 SwiftUI robi to samo.5
Praktyczny skutek to zamieszanie diagnostyczne. Po aktualizacji otwiera się menu i widać ikony obok Settings i Share, ale nie obok własnych pozycji. Rozsądny wniosek brzmi: obrazki się nie wczytały, katalog zasobów jest uszkodzony albo nazwy symboli są błędne. Nic z tego nie ma miejsca. System stosuje politykę, która wyjmuje spod niej garstkę powszechnie znanych pozycji.
Jak zdecydować, które ikony zachować
Wszystkie pięć wpisów w release notes odsyła programistów do zaktualizowanych Human Interface Guidelines po rozstrzygnięcie, które pozycje menu powinny nadal wyświetlać obrazki.145 Nie udało mi się zweryfikować, co mówi ta wytyczna: strona HIG poświęcona menu renderuje się po stronie klienta i praktycznie nie zwraca narzędziu pobierającemu żadnego tekstu, a dla treści HIG nie istnieje odpowiednik punktu końcowego JSON, jaki jest dostępny dla dokumentacji API. Lepiej sprawdzić to samodzielnie, niż wierzyć czyjemuś streszczeniu — łącznie z tym.
Jedyną konkretną wskazówką wprost w release notes jest sugestia z wpisu SwiftUI: ikonę warto pokazać wtedy, gdy pozycja menu „reprezentuje obiekt lub pojęcie, a nie akcję”.5
Ta reguła jest użyteczna. Menu wyliczające otwarte dokumenty, dostępne urządzenia czy zapisane filtry nazywa obiekty, a ikona niesie tam tożsamość. Menu złożone z czasowników — czyli większość menu — niewiele zyskuje na ikonie przy każdej pozycji. Wygląda na to, że Apple uznało, iż macOS 26 przesadził z obrazkami, a 27 jest korektą.
Co zrobić przed wydaniem na SDK 27
Najpierw odnaleźć pozycje menu złożone wyłącznie z ikony. To one psują się najdotkliwiej, bo obrazek stanowi całą treść. Każdy NSMenuItem z obrazkiem i pustym tytułem oraz każdy element UIMenu zbudowany w ten sam sposób wymaga jawnego ustawienia .visible.
Decydować dla każdej pozycji osobno, nie globalnie. Ustawienie .visible wszędzie cofa świadomą zmianę platformy i przywraca stan z macOS 26. Reguła „obiekt kontra akcja” jest lepszym filtrem niż hurtowe nadpisanie.
Xiby przeszukać osobno. Pola wyboru „macOS 26.0 only” nie da się wyłapać grepem tak jak przypisania właściwości, a zmienia ono zachowanie.
Testować na obu generacjach SDK, jeśli obie są wspierane. Kompilacja względem 26 i kompilacja względem 27 renderują się inaczej mimo identycznego kodu źródłowego. To sprawia, że zrzuty ekranu i testy UI stają się zależne od SDK w sposób, w jaki wcześniej nie były.
Powiązanie zachowania z SDK staje się w tym wydaniu powracającym motywem. Wycofanie canOpenURL w iOS 27 obraca się wokół tej samej osi, a praktyczny wniosek się powtarza: o tym, co zobaczy użytkownik, decyduje wybór dokonany w czasie kompilacji, a nie kod źródłowy.
Liczyć się z tym, że release notes jeszcze się zmienią. Dwa z trzech wpisów AppKit zgłoszono jako poprawki, co oznacza, że zachowanie zmieniło się już raz w trakcie cyklu bety. Ponownie zweryfikowałem wszystkie pięć wpisów względem notatek do Beta 4 dnia 1 sierpnia 2026 i brzmią one dokładnie tak, jak je tutaj zacytowano. Przed podjęciem jakichkolwiek działań warto potwierdzić je w bieżących notatkach.
Audyt istniejącej bazy kodu
Zmiana jest na tyle mechaniczna, że audyt da się przeprowadzić systematycznie — i warto to zrobić przed przebudową, a nie po zgłoszeniu pustego menu przez testera.
Zacząć od najpoważniejszego przypadku. NSMenuItem niosący obrazek przy pustym tytule to jedyna awaria, która daje menu widocznie zepsute, a nie jedynie uboższe. W kodzie należy szukać przypisania obrazka bez odpowiadającego mu tytułu:
# Menu items constructed with an image and no title argument
rg 'NSMenuItem\(' --type swift -A3 | rg -B1 'image ='
rg 'setImage|\.image\s*=' --type swift | rg -i 'menu'
Żaden z tych wzorców nie jest wyczerpujący, bo tytuł można ustawić trzy linijki dalej albo odczytać z tabeli lokalizacyjnej. Wyniki należy traktować jako listę kandydatów do przejrzenia, a nie jako ustalenie.
Potem xiby. Żaden grep nie pomoże przy polu wyboru „macOS 26.0 only”, które żyje w inspektorze pozycji menu, a nie w atrybucie widocznym dla systemu budowania. Jeżeli menu pochodzą z Interface Buildera, audyt oznacza otwarcie każdego menu i sprawdzenie każdej pozycji.
Potem konstruktory menu w UIKit. UIMenu, UIAction, UICommand i UIKeyCommand zyskały zaktualizowane inicjalizatory przyjmujące preferredImageVisibility, więc poprawkę można wprowadzić już przy tworzeniu obiektu, a nie jako osobne przypisanie później.4
Potem użycia Label w SwiftUI wewnątrz menu. Te najłatwiej przeoczyć, bo Label w menu wygląda w kodzie identycznie jak Label w dowolnym innym miejscu. Modyfikator trafia na etykietę — i tylko tam, gdzie ikona ma zostać zachowana.
Zbudować dwa razy i porównać. Najpewniejsza kontrola w ogóle nie polega na czytaniu kodu. Wystarczy zbudować projekt względem SDK 26 i SDK 27, otworzyć te same menu i sfotografować oba warianty. Identyczny kod dający dwa różne menu to sedno tej zmiany, a zestawienie obok siebie znajdzie to, czego grep nie znajdzie.
Ten ostatni krok chroni również zrzuty ekranu. Materiały marketingowe, dokumentacja i zasoby dla App Store pokazujące menu powstawały pod tym SDK, który był aktualny w chwili ich wykonania. Jeżeli widać na nich ikony, których wydawana wersja już nie renderuje, są od tej chwili nieaktualne — i nic w procesie budowania tego nie zasygnalizuje.
Najważniejsze wnioski
Dla programistów AppKit:
- Linkowanie względem SDK macOS 27 ukrywa również obrazki niebędące symbolami, nie tylko symbole. Audyt ograniczony do SF Symbols pomija połowę problemu.
- Pozycje menu złożone wyłącznie z ikony tracą automatyczną ochronę na SDK 27 i renderują się jako puste. Każdej z nich trzeba ustawić preferredImageVisibility = .visible.
- Menu zdefiniowane w xibach niosą pole wyboru „macOS 26.0 only”, które zmienia widoczność poza jakąkolwiek ścieżką w kodzie.
Dla programistów UIKit:
- preferredImageVisibility znajduje się na UIMenuElement oraz w zaktualizowanych inicjalizatorach UIMenu, UIAction, UICommand i UIKeyCommand.
- Właściwość istnieje w tvOS i visionOS, ale Apple dokumentuje to zachowanie dla iPadOS i macOS. Zanim się cokolwiek założy, trzeba to zweryfikować.
Dla programistów SwiftUI:
- preferredImageVisibility nie jest API dla SwiftUI. Należy użyć labelStyle(.titleAndIcon) na Label.
- Domyślne zachowanie SwiftUI ukrywa obrazki symboli i zachowuje obrazki niebędące symbolami, co odpowiada wariantowi bazowemu AppKit, a nie zachowaniu SDK 27.
FAQ
Dlaczego Settings i Share nadal pokazują ikony?
System zwalnia z tej reguły niektóre typowe pozycje menu. AppKit, UIKit i SwiftUI nadal dostarczają domyślnie widoczne obrazki dla pozycji takich jak Settings, Share i Print.145 Widok tych ikon przy jednoczesnym ukryciu własnych to zachowanie oczekiwane, a nie błąd wczytywania.
Czy pozostanie przy starszym SDK pozwala tego uniknąć?
Częściowo — i rozróżnienie ma tu znaczenie. Aplikacje zlinkowane względem macOS 26.0 i nowszych już ukrywają obrazki symboli.1 Pozostanie poniżej SDK macOS 27 zachowuje widoczność obrazków niebędących symbolami oraz automatyczną ochronę pozycji złożonych wyłącznie z ikony.23 Nie przywraca natomiast obrazków symboli.
Co dzieje się z pozycją menu, która ma tylko obrazek i nie ma tytułu?
Na SDK sprzed macOS 27 NSMenu pokazuje obrazek automatycznie, ponieważ tytuł i tytuł atrybutowany są puste.3 Na SDK macOS 27 ta ochrona nie obowiązuje i obrazek zostaje ukryty, co pozostawia pozycję menu bez widocznej treści. Należy ustawić preferredImageVisibility = .visible.
Czy w tvOS i visionOS jest tak samo?
UIMenuElement.preferredImageVisibility jest dostępne w obu systemach od wersji 27.0.7 Wpisy w release notes opisują mechanizm ukrywania dla iPadOS i macOS. Istnienie API na danej platformie nie jest potwierdzeniem, że zachowanie jest tam aktywne.
Które ikony zachować?
Release notes odsyłają do Human Interface Guidelines, których nie udało mi się przeczytać bezpośrednio. Wskazówka podana przez Apple we wpisie SwiftUI mówi, aby pokazać ikonę wtedy, gdy pozycja menu „reprezentuje obiekt lub pojęcie, a nie akcję”.5
Źródła
-
Apple, “macOS 27 Golden Gate Beta 4 Release Notes,” AppKit. Radar 170477566:
NSMenudomyślnie ukrywa wszystkie obrazki symboli w pozycjach menu, podczas gdy obrazki niebędące symbolami pozostają widoczne; dotyczy aplikacji zlinkowanych względem macOS 26.0 i nowszych; zachowanie pola wyboru „macOS 26.0 only” w xib; właściwośćpreferredImageVisibility; domyślnie widoczne obrazki dla Settings, Share i Print. Kopia do odczytu maszynowego poddeveloper.apple.com/tutorials/data/documentation/macos-release-notes/macos-27-release-notes.json. Zweryfikowano ponownie 1 sierpnia 2026. ↩↩↩↩↩↩↩↩↩ -
Apple, macOS 27 Golden Gate Beta 4 Release Notes, AppKit, Resolved Issues. Radar 179374305 (FB23070183): „W aplikacjach zlinkowanych względem SDK macOS 27 automatycznie ukrywane są zarówno obrazki symboli, jak i obrazki niebędące symbolami. W aplikacjach zlinkowanych względem wcześniejszych SDK obrazki niebędące symbolami pozostają automatycznie widoczne, co zachowuje zgodność z dotychczasowym zachowaniem aplikacji.” ↩↩↩
-
Apple, macOS 27 Golden Gate Beta 4 Release Notes, AppKit, Resolved Issues. Radar 179936632: automatyczne wyświetlanie obrazka dla pozycji menu, których tytuł i tytuł atrybutowany są puste, na SDK sprzed 27 oraz usunięcie tego zachowania przy linkowaniu względem SDK macOS 27. ↩↩↩↩
-
Apple, “iOS & iPadOS 27 Beta 4 Release Notes,” UIKit. Radar 170479084: pasek menu i menu kontekstowe w iPadOS 27.0 oraz macOS 27.0 domyślnie nie wyświetlają obrazków ustawionych na elementach menu;
preferredImageVisibilitynaUIMenuElementoraz zaktualizowane inicjalizatoryUIMenu,UIAction,UICommandiUIKeyCommand. Ten sam radar pojawia się w notatkach macOS 27. ↩↩↩↩↩↩ -
Apple, iOS & iPadOS 27 Beta 4 Release Notes, SwiftUI. Radar 170480710: SwiftUI domyślnie ukrywa w większości kontekstów wszystkie obrazki symboli w pozycjach menu, podczas gdy obrazki niebędące symbolami pozostają widoczne;
labelStyle(_:)ze stylem.titleAndIcon; wskazówka, aby pokazywać ikonę, gdy pozycja menu „reprezentuje obiekt lub pojęcie, a nie akcję”; domyślnie widoczne obrazki dla typowych pozycji systemowych. ↩↩↩↩↩↩↩↩↩ -
Apple, “NSMenuItem.preferredImageVisibility” oraz “NSMenuItem.ImageVisibility.” Przypadki
.automatic,.visible,.hidden;RawRepresentablenadNSInteger; dostępne od macOS 27.0. ↩ -
Apple, “UIMenuElement.preferredImageVisibility” oraz “UIMenuElement.ImageVisibility.” Przypadki
.automatic,.visible,.hidden; dostępne od iOS 27.0, iPadOS 27.0, Mac Catalyst 27.0, tvOS 27.0, visionOS 27.0. ↩↩↩