Skip to content

Commit 10af839

Browse files
Ubaxkkafar
andauthored
feat(iOS): add missing features to bar button items (#3396)
## Description Some features are missing for header items. **Menu**: - display inline (only for submenu) - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayinline?language=objc - display as palette - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayaspalette?language=objc - single selection - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/singleselection?language=objc - destructive (only for submenu) - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/destructive?language=objc **MenuAction**: - subtitle - https://developer.apple.com/documentation/uikit/uimenuelement/subtitle?language=objc ## Changes Add missing properties. <img height="900" alt="simulator_screenshot_0286E34E-D82F-4377-9923-7909026EBC84" src="https://github.com/user-attachments/assets/30fbe67b-83a7-4081-ba9a-cea775b9ee27" /> ## Test code and steps to reproduce <!-- Please include code that can be used to test this change and short description how this example should work. This snippet should be as minimal as possible and ready to be pasted into editor (don't exclude exports or remove "not important" parts of reproduction example) --> ## Checklist - [x] Included code example that can be used to test this change - [x] Updated TS types - [ ] Updated documentation: <!-- For adding new props to native-stack --> - [x] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [ ] Ensured that CI passes --------- Co-authored-by: Kacper Kafara <[email protected]>
1 parent fda4a9c commit 10af839

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

apps/src/screens/BarButtonItems.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,13 +437,16 @@ export default function BarButtonItemsExample() {
437437
},
438438
{
439439
label: 'Submenu',
440+
displayInline: true,
441+
destructive: true,
440442
icon: { type: 'sfSymbol', name: 'star' },
441443
type: 'submenu',
442444
items: [
443445
{
444446
label: 'Sub Action 1',
445447
state: 'mixed',
446448
type: 'action',
449+
subtitle: 'With subtitle',
447450
onPress: () => Alert.alert('Sub Action 1 pressed'),
448451
destructive: true,
449452
keepsMenuPresented: true,
@@ -456,6 +459,26 @@ export default function BarButtonItemsExample() {
456459
},
457460
],
458461
},
462+
{
463+
label: 'Palette',
464+
displayInline: true,
465+
displayAsPalette: true,
466+
destructive: true,
467+
type: 'submenu',
468+
items: [
469+
{
470+
state: 'on',
471+
icon: { type: 'sfSymbol', name: '0.circle.fill' },
472+
type: 'action',
473+
onPress: () => Alert.alert('Sub Action 1 pressed'),
474+
},
475+
{
476+
icon: { type: 'sfSymbol', name: '1.circle.fill' },
477+
type: 'action',
478+
onPress: () => Alert.alert('Sub Action 2 pressed'),
479+
},
480+
],
481+
},
459482
],
460483
},
461484
},

guides/GUIDE_FOR_LIBRARY_AUTHORS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,12 +624,17 @@ menu?: {
624624
label?: string;
625625
type: 'submenu';
626626
icon?: PlatformIconIOSSfSymbol;
627+
displayInline?: boolean; // Whether to display submenu inline - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayinline
628+
destructive?: boolean; // Attribute indicating destructive style. Read more: https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/destructive
629+
singleSelection?: boolean; // Whether submenu allows single selection - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/singleselection
630+
displayAsPalette?: boolean; // Whether to display submenu as palette - https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayaspalette
627631
items: menu['items']; // References itself so you can create inifinte deep menus. So either actions or more submenus
628632
}
629633
>
630634
}
631635
```
632636

637+
633638
#### The spacing item supports:
634639

635640
`type: 'spacing'` — Type of the item.

ios/RNSBarButtonItem.mm

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#import "RNSDefines.h"
55
#import "RNSImageLoadingHelper.h"
66

7+
static UIMenuOptions RNSMakeUIMenuOptionsFromConfig(NSDictionary *config);
8+
79
@implementation RNSBarButtonItem {
810
NSString *_buttonId;
911
RNSBarButtonItemAction _itemAction;
@@ -160,10 +162,11 @@ + (UIMenu *)initUIMenuWithDict:(NSDictionary<NSString *, id> *)dict menuAction:(
160162
}
161163
NSString *title = dict[@"title"];
162164
NSString *sfSymbolName = dict[@"sfSymbolName"];
165+
163166
return [UIMenu menuWithTitle:title
164167
image:sfSymbolName ? [UIImage systemImageNamed:sfSymbolName] : nil
165168
identifier:nil
166-
options:0
169+
options:RNSMakeUIMenuOptionsFromConfig(dict)
167170
children:elements];
168171
}
169172

@@ -223,6 +226,11 @@ + (UIAction *)createActionItemFromConfig:(NSDictionary *)dict menuAction:(RNSBar
223226
#endif
224227
}
225228

229+
NSString *subtitle = dict[@"subtitle"];
230+
if (subtitle != nil) {
231+
actionElement.subtitle = subtitle;
232+
}
233+
226234
return actionElement;
227235
}
228236

@@ -323,3 +331,30 @@ - (void)setBadgeFromConfig:(NSDictionary *)badgeObj
323331
#endif
324332

325333
@end
334+
335+
UIMenuOptions RNSMakeUIMenuOptionsFromConfig(NSDictionary *config)
336+
{
337+
UIMenuOptions options = 0;
338+
NSNumber *singleSelection = config[@"singleSelection"];
339+
NSNumber *displayAsPalette = config[@"displayAsPalette"];
340+
NSNumber *displayInline = config[@"displayInline"];
341+
NSNumber *destructive = config[@"destructive"];
342+
343+
if (singleSelection != nil && [singleSelection boolValue]) {
344+
options |= UIMenuOptionsSingleSelection;
345+
}
346+
#if RNS_IPHONE_OS_VERSION_AVAILABLE(17_0)
347+
if (@available(iOS 17.0, *)) {
348+
if (displayAsPalette != nil && [displayAsPalette boolValue]) {
349+
options |= UIMenuOptionsDisplayAsPalette;
350+
}
351+
}
352+
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(17_0)
353+
if (displayInline != nil && [displayInline boolValue]) {
354+
options |= UIMenuOptionsDisplayInline;
355+
}
356+
if (destructive != nil && [destructive boolValue]) {
357+
options |= UIMenuOptionsDestructive;
358+
}
359+
return options;
360+
}

src/types.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,7 @@ export interface HeaderBarButtonItemWithAction
11061106
export interface HeaderBarButtonItemMenuAction {
11071107
type: 'action';
11081108
title?: string;
1109+
subtitle?: string;
11091110
onPress: () => void;
11101111
icon?: PlatformIconIOSSfSymbol;
11111112
/**
@@ -1151,13 +1152,19 @@ export interface HeaderBarButtonItemSubmenu {
11511152
title?: string;
11521153
icon?: PlatformIconIOSSfSymbol;
11531154
items: HeaderBarButtonItemWithMenu['menu']['items'];
1155+
displayInline?: boolean;
1156+
destructive?: boolean;
1157+
singleSelection?: boolean;
1158+
displayAsPalette?: boolean;
11541159
}
11551160

11561161
export interface HeaderBarButtonItemWithMenu extends SharedHeaderBarButtonItem {
11571162
type: 'menu';
11581163
menu: {
11591164
title?: string;
11601165
items: (HeaderBarButtonItemMenuAction | HeaderBarButtonItemSubmenu)[];
1166+
singleSelection?: boolean;
1167+
displayAsPalette?: boolean;
11611168
};
11621169
/**
11631170
* A Boolean value that indicates whether the button title should indicate selection or not.

0 commit comments

Comments
 (0)