// // 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 *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id)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)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)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)session { UIDragItem *item = session.items.firstObject; NSItemProvider *p = item.itemProvider; if ([p canLoadObjectOfClass:[UIColor class]]) { [p loadObjectOfClass:[UIColor class] completionHandler:^(UIColor * _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