stephan-tolksdorf / STULabel

A faster and more flexible label view for iOS
Other
120 stars 29 forks source link

Add UIContextMenuInteraction Support #18

Closed halleygen closed 2 years ago

halleygen commented 3 years ago

Hi,

This PR adds support for context menus to STULabel. I tried to mimic the UIDragInteraction implementation as much as possible.

This is not ready to be merged because there appears to be a bug with UIDragPreview that causes visual glitches. You've probably noticed the following message in console when using the existing drag features in STULabel:

CLIENT APP ERROR - Neither the view or container of the UITargetedPreview is currently in a window. This is in violation of UIDragInteraction API contract and can cause a severe visual glich. THIS IS A CLIENT APP BUG and will soon be a hard assert. PLEASE FIX ME

This seems to be caused by a bug when instantiating a UIDragPreview using previewForURL:title:. Up until now this hasn't actually caused a visual glitch; however, when a context menu interaction transitions to a drag interaction there is a glitch.

I'm assuming this is a bug and Apple's fault because drag previews initialised any other way do not glitch out or cause the message in the console to appear.

A temporary workaround could be to recreate the preview created by the previewForURL:title: method. Eg:

NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:title.string attributes:@{ NSFontAttributeName : [UIFont preferredFontForTextStyle:@"UICTFontTextStyleEmphasizedCaption1"] }];
[attrStr appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
[attrStr appendAttributedString:[[NSAttributedString alloc] initWithString:url.absoluteString ?: @"" attributes:@{ NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1], NSForegroundColorAttributeName : UIColor.secondaryLabelColor }]];
UILabel *titleLabel = [UILabel new];
titleLabel.attributedText = attrStr;
titleLabel.numberOfLines = 4;
[titleLabel sizeToFit];

UIDragPreviewParameters *params = [[UIDragPreviewParameters alloc] init];
params.visiblePath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(titleLabel.bounds, -10, -10) cornerRadius:10];
return [[UIDragPreview alloc] initWithView:titleLabel parameters:params];

Let me know your thoughts :)