Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ - (void)hideAllMenusSkipping:(CMPEditMenuView *)skipEditMenuView {

@end

typedef enum : NSUInteger {
CMPEditMenuStateHidden = 0,
CMPEditMenuStatePresenting,
CMPEditMenuStatePresented,
CMPEditMenuStateHiding,
} CMPEditMenuState;

@interface CMPEditMenuView() <UIEditMenuInteractionDelegate>

@property (weak, nonatomic, nullable) UIView *rootView;
Expand All @@ -82,7 +89,7 @@ @interface CMPEditMenuView() <UIEditMenuInteractionDelegate>
@property (strong, nonatomic, nullable) dispatch_block_t presentInteractionBlock;

@property (assign, nonatomic) CGRect targetRect;
@property (assign, nonatomic) BOOL isEditMenuShown;
@property (assign, nonatomic) CMPEditMenuState editMenuState;

@property (readwrite) UIEditMenuInteraction* editInteraction API_AVAILABLE(ios(16.0));

Expand Down Expand Up @@ -123,21 +130,56 @@ - (void)showEditMenuAtRect:(CGRect)targetRect

if (@available(iOS 16, *)) {
[[CMPEditMenuViewRegister shared] hideAllMenusSkipping:self];
if (self.editInteraction == nil || contextMenuItemsChanged || !self.isEditMenuShown) {
BOOL isFirstMenuPresentation = self.presentInteractionBlock == nil;
[self cancelPresentEditMenuInteraction];
NSTimeInterval delay = isFirstMenuPresentation ? 0 : [self editMenuDelay];
[self schedulePresentEditMenuInteractionWithDelay:delay];
} else if (positionChanged) {
[self.editInteraction updateVisibleMenuPositionAnimated:NO];

switch (self.editMenuState) {
case CMPEditMenuStateHidden:
case CMPEditMenuStateHiding:
[self cancelPresentEditMenuInteraction];
[self schedulePresentEditMenuInteractionWithDelay:[self editMenuDelay]];
self.editMenuState = CMPEditMenuStatePresenting;
break;

case CMPEditMenuStatePresenting:
if (contextMenuItemsChanged) {
[self cancelPresentEditMenuInteraction];
[self schedulePresentEditMenuInteractionWithDelay:[self editMenuDelay]];
} else if (positionChanged) {
if (self.presentInteractionBlock == nil) {
// View appearance already started - jsut set the new locaiton.
[self.editInteraction updateVisibleMenuPositionAnimated:NO];
} else {
[self cancelPresentEditMenuInteraction];
[self schedulePresentEditMenuInteractionWithDelay:[self editMenuDelay]];
}
}
break;

case CMPEditMenuStatePresented:
if (contextMenuItemsChanged) {
[self cancelPresentEditMenuInteraction];
[self schedulePresentEditMenuInteractionWithDelay:0];
self.editMenuState = CMPEditMenuStatePresenting;
} else if (positionChanged) {
[self.editInteraction updateVisibleMenuPositionAnimated:NO];
self.editMenuState = CMPEditMenuStatePresented;
}
break;
}
} else {
self.isEditMenuShown = YES;
if (contextMenuItemsChanged || positionChanged) {
[self hideEditMenu];
[self scheduleShowMenuController];
}
self.editMenuState = CMPEditMenuStatePresenting;
}
}

- (void)setEditMenuState:(CMPEditMenuState)editMenuState {
if (_editMenuState == editMenuState) {
return;
}

_editMenuState = editMenuState;
}

- (void)updateAvailableSystemActions:(void (^)(void))copyBlock
Expand All @@ -152,6 +194,15 @@ - (void)updateAvailableSystemActions:(void (^)(void))copyBlock
self.systemSelectAllBlock = selectAllBlock;
}

- (BOOL)isEditMenuShown {
if (@available(iOS 16, *)) {
return _editMenuState == CMPEditMenuStatePresenting || _editMenuState == CMPEditMenuStatePresented;
} else {
return _editMenuState == CMPEditMenuStatePresenting ||
(_editMenuState == CMPEditMenuStatePresented && [UIMenuController sharedMenuController].menuVisible);
}
}

- (void)didMoveToWindow {
[super didMoveToWindow];

Expand All @@ -172,6 +223,7 @@ - (void)scheduleShowMenuController {
UIMenuController *controller = [UIMenuController sharedMenuController];
controller.menuItems = [self makeCustomMenuItems];
[self becomeFirstResponder];
self.editMenuState = CMPEditMenuStatePresented;
[controller showMenuFromView:self rect:self.targetRect];

self.showContextMenuBlock = nil;
Expand Down Expand Up @@ -249,23 +301,16 @@ - (void)setEditInteraction:(UIEditMenuInteraction *)editInteraction API_AVAILABL
_editInteraction = editInteraction;
}

- (void)presentEditMenuInteraction API_AVAILABLE(ios(16.0)) {
NSAssert(self.editInteraction != nil, @"Edit Interaction must be initialized");

UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil
sourcePoint:self.targetRect.origin];
[self.editInteraction presentEditMenuWithConfiguration:config];
}

- (void)schedulePresentEditMenuInteractionWithDelay:(NSTimeInterval)delay API_AVAILABLE(ios(16.0)) {
__weak __auto_type weak_self = self;
self.presentInteractionBlock = dispatch_block_create(0 ,^{
__auto_type self = weak_self;
if (self.editInteraction == nil) {
self.editInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self];
[self addInteraction:self.editInteraction];
}
[self presentEditMenuInteraction];
self.presentInteractionBlock = nil;
self.editInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self];
[self addInteraction:self.editInteraction];
UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil
sourcePoint:self.targetRect.origin];
[self.editInteraction presentEditMenuWithConfiguration:config];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
dispatch_get_main_queue(),
Expand All @@ -286,15 +331,25 @@ - (BOOL)canBecomeFirstResponder {
- (void)hideEditMenu {
if (@available(iOS 16, *)) {
[self cancelPresentEditMenuInteraction];

if (self.editInteraction != nil) {
[self.editInteraction dismissMenu];
[self removeInteraction:self.editInteraction];
self.editInteraction = nil;
switch (self.editMenuState) {
case CMPEditMenuStateHidden:
case CMPEditMenuStateHiding:
break;

case CMPEditMenuStatePresenting:
case CMPEditMenuStatePresented:
self.editMenuState = CMPEditMenuStateHiding;

if (self.editInteraction != nil) {
UIEditMenuInteraction *interaction = self.editInteraction;
[interaction dismissMenu];
self.editInteraction = nil;
[self removeInteraction:interaction];
}
}
} else {
self.isEditMenuShown = NO;
[self cancelShowMenuController];
self.editMenuState = CMPEditMenuStateHidden;
[[UIMenuController sharedMenuController] hideMenu];
}

Expand Down Expand Up @@ -447,11 +502,16 @@ - (CGRect)editMenuInteraction:(UIEditMenuInteraction *)interaction
- (void)editMenuInteraction:(UIEditMenuInteraction *)interaction
willDismissMenuForConfiguration:(UIEditMenuConfiguration *)configuration
animator:(id<UIEditMenuInteractionAnimating>)animator API_AVAILABLE(ios(16.0)) {
if (self.editInteraction != interaction) {
return;
}
self.editInteraction = nil;
self.editMenuState = CMPEditMenuStateHiding;
__weak __auto_type weak_self = self;
[animator addCompletion:^{
__auto_type self = weak_self;
if (self.editInteraction == interaction) {
self.isEditMenuShown = NO;
if (self.editInteraction == nil) {
self.editMenuState = CMPEditMenuStateHidden;
}
}];
}
Expand All @@ -463,7 +523,7 @@ - (void)editMenuInteraction:(UIEditMenuInteraction *)interaction
[animator addCompletion:^{
__auto_type self = weak_self;
if (self.editInteraction == interaction) {
self.isEditMenuShown = YES;
self.editMenuState = CMPEditMenuStatePresented;
}
}];
}
Expand Down
Loading