Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Color Swatch view for Mac Catalyst that implements inter-app drag & drop
//
// 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
//
// 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
You can’t perform that action at this time.