Last active
May 6, 2021 00:47
-
-
Save steventroughtonsmith/1ebb4ae7f621f4ad4cd21145ebf00ba3 to your computer and use it in GitHub Desktop.
Color Swatch view for Mac Catalyst that implements inter-app drag & drop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PSTLDraggableColorSwatchView.h | |
// Pastel | |
// | |
// Created by Steven Troughton-Smith on 06/03/2020. | |
// Copyright © 2020 Steven Troughton-Smith. All rights reserved. | |
// | |
@import UIKit; | |
NS_ASSUME_NONNULL_BEGIN | |
@interface PSTLDraggableColorSwatchView : UIView <UIDragInteractionDelegate, UIDropInteractionDelegate> | |
@property (nonatomic) UIColor *color; | |
@end | |
NS_ASSUME_NONNULL_END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PSTLDraggableColorSwatchView.m | |
// Pastel | |
// | |
// Created by Steven Troughton-Smith on 06/03/2020. | |
// Copyright © 2020 Steven Troughton-Smith. All rights reserved. | |
// | |
#import "PSTLDraggableColorSwatchView.h" | |
@import ObjectiveC.runtime; | |
@implementation NSObject (UINS) | |
-(NSUInteger)PSTLdraggingSession:(void *)arg2 sourceOperationMaskForDraggingContext:(long long)arg3 | |
{ | |
return 0xff; | |
} | |
@end | |
@implementation PSTLDraggableColorSwatchView | |
+(void)load | |
{ | |
{ | |
Method m1 = class_getInstanceMethod(NSClassFromString(@"UINSDragManager"), NSSelectorFromString(@"draggingSession:sourceOperationMaskForDraggingContext:")); | |
Method m2 = class_getInstanceMethod(NSClassFromString(@"UINSDragManager"), @selector(PSTLdraggingSession:sourceOperationMaskForDraggingContext:)); | |
method_exchangeImplementations(m1, m2); | |
} | |
} | |
- (instancetype)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
UIDragInteraction *dragInteraction = [[UIDragInteraction alloc] initWithDelegate:self]; | |
dragInteraction.enabled = YES; | |
[self addInteraction:dragInteraction]; | |
UIDropInteraction *dropInteraction = [[UIDropInteraction alloc] initWithDelegate:self]; | |
[self addInteraction:dropInteraction]; | |
} | |
return self; | |
} | |
- (void)setColor:(UIColor *)color | |
{ | |
_color = color; | |
self.backgroundColor = color; | |
} | |
#pragma mark - Drag & Drop | |
- (NSArray<UIDragItem *> *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id<UIDragSession>)session | |
{ | |
NSItemProvider *provider = [[NSItemProvider alloc] initWithObject:self.color]; | |
[provider registerDataRepresentationForTypeIdentifier:@"com.apple.cocoa.pasteboard.color" visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress * _Nullable(void (^ _Nonnull completionHandler)(NSData * _Nullable, NSError * _Nullable)) { | |
NSData *d = [NSKeyedArchiver archivedDataWithRootObject:self.color requiringSecureCoding:NO error:nil]; | |
completionHandler(d, nil); | |
return nil; | |
}]; | |
UIDragItem *dragItem = [[UIDragItem alloc] initWithItemProvider:provider]; | |
return @[dragItem]; | |
} | |
- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session | |
{ | |
NSItemProvider *item = [[[session items] firstObject] itemProvider]; | |
BOOL shouldHandle = NO; | |
for (NSString *type in [item registeredTypeIdentifiers]) | |
{ | |
if ([type isEqualToString:@"com.apple.cocoa.pasteboard.color"] || [type isEqualToString:@"com.apple.uikit.color"]) | |
{ | |
shouldHandle = YES; | |
} | |
} | |
return shouldHandle; | |
} | |
- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session | |
{ | |
/* | |
NSColorPanel's main swatch drag session is designated as 'local-only', which prevents inter-app drag and drop. Whyyy | |
We can override that by poking at some private properties… | |
*/ | |
@try { | |
[(NSObject *)session setValue:@(1) forKeyPath:@"_sessionDestination._outsideAppSourceOperationMask"]; | |
} @catch (NSException *exception) { | |
} | |
UIDropProposal *proposal = [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy]; | |
proposal.precise = YES; | |
return proposal; | |
} | |
- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session | |
{ | |
UIDragItem *item = session.items.firstObject; | |
NSItemProvider *p = item.itemProvider; | |
if ([p canLoadObjectOfClass:[UIColor class]]) | |
{ | |
[p loadObjectOfClass:[UIColor class] completionHandler:^(UIColor <NSItemProviderReading>* _Nullable object, NSError * _Nullable error) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
UIColor *oldColor = self.color; | |
[[self undoManager] registerUndoWithTarget:self handler:^(id _Nonnull target) { | |
self.color = oldColor; | |
}]; | |
self.color = object; | |
}); | |
}]; | |
} | |
else | |
{ | |
[p loadDataRepresentationForTypeIdentifier:@"com.apple.cocoa.pasteboard.color" completionHandler:^(NSData * _Nullable data, NSError * _Nullable error) { | |
id cc = [NSKeyedUnarchiver unarchivedObjectOfClass:NSClassFromString(@"UIColor") fromData:data error:nil]; | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
UIColor *oldColor = self.color; | |
[[self undoManager] registerUndoWithTarget:self handler:^(id _Nonnull target) { | |
self.color = oldColor; | |
}]; | |
self.color = (UIColor *)cc; | |
}); | |
}]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment