Skip to content

Commit 194aacf

Browse files
author
Semen Osipov
committed
fix: stabilize edit menu and gate Strikethrough on selection
- iOS 16+: defer unknown selectors in canPerformAction to super so UIKit no longer strips system menu items after display, which caused a visible jump of the Format submenu when tapped during the open animation. - Inject Strikethrough directly into the format -> text-style submenu with the matching SF Symbol icon, only when a selection exists, mirroring the existing gate used for Bold/Italic/Underline. Made-with: Cursor
1 parent efee92e commit 194aacf

1 file changed

Lines changed: 17 additions & 23 deletions

File tree

ARGrowingTextView/ARGrowingTextView/Source/ARTextViewInternalTextStyle.swift

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -245,51 +245,45 @@ extension ARTextViewInternalTextStyle {
245245
// MARK: - UIMenu
246246
@available(iOS 16.0, *)
247247
public override func editMenu(for textRange: UITextRange, suggestedActions: [UIMenuElement]) -> UIMenu? {
248+
let hasSelection = selectedRange.length > 0
248249
let title = NSLocalizedString("ARTextViewInternalTextStyle.strikethroughActionTitle", tableName: nil, bundle: .module, value: "Strikethrough", comment: "")
249-
let strikethrough = UIAction(title: title) { [weak self] _ in
250+
let strikethrough = UIAction(title: title,
251+
image: UIImage(systemName: "strikethrough")) { [weak self] _ in
250252
self?.didSelectStyle(.strikethrough)
251253
}
252-
let actions = suggestedActions.map { element -> UIMenuElement in
253-
guard let menu = element as? UIMenu else { return element }
254-
return Self.insertingAction(strikethrough, intoFormatMenu: menu)
254+
let actions: [UIMenuElement] = suggestedActions.map { element -> UIMenuElement in
255+
guard hasSelection,
256+
let menu = element as? UIMenu,
257+
menu.identifier == .format else { return element }
258+
let newFormatChildren: [UIMenuElement] = menu.children.map { child -> UIMenuElement in
259+
guard let sub = child as? UIMenu, sub.identifier == .textStyle else { return child }
260+
return sub.replacingChildren(sub.children + [strikethrough])
261+
}
262+
return menu.replacingChildren(newFormatChildren)
255263
}
256264
return UIMenu(children: actions)
257265
}
258266

259-
@available(iOS 16.0, *)
260-
private static func insertingAction(_ action: UIAction, intoFormatMenu menu: UIMenu) -> UIMenu {
261-
if menu.identifier == .textStyle || menu.identifier == .format {
262-
return menu.replacingChildren(menu.children + [action])
263-
}
264-
let updatedChildren = menu.children.map { child -> UIMenuElement in
265-
guard let submenu = child as? UIMenu else { return child }
266-
return insertingAction(action, intoFormatMenu: submenu)
267-
}
268-
return menu.replacingChildren(updatedChildren)
269-
}
270-
271267
public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
272268
if #unavailable(iOS 16) {
273269
customStyleMenu()
274-
}
275-
276-
guard EnabledMenuSelectors.contains(action) else {
277-
return false
270+
guard EnabledMenuSelectors.contains(action) else {
271+
return false
272+
}
278273
}
279274

280275
switch action {
281276
case EnabledMenuSelectors.paste:
282277
let canPaste = UIPasteboard.general.hasStrings || UIPasteboard.general.hasImages
283-
guard canPaste else {return false}
278+
return canPaste && super.canPerformAction(action, withSender: sender)
284279
case EnabledMenuSelectors.toggleBoldface,
285280
EnabledMenuSelectors.toggleItalics,
286281
EnabledMenuSelectors.toggleUnderline,
287282
EnabledMenuSelectors.toggleStrikethrough:
288283
return selectedRange.length > 0
289284
default:
290-
break
285+
return super.canPerformAction(action, withSender: sender)
291286
}
292-
return super.canPerformAction(action, withSender: sender)
293287
}
294288

295289
private func customStyleMenu() {

0 commit comments

Comments
 (0)