Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
WIP tooltips for Mac Catalyst
//
// UIView+Tooltips.h
// Crossword
//
// Created by Steven Troughton-Smith on 13/09/2019.
// Copyright © 2019 Steven Troughton-Smith. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UITooltipView : UIView
@property BOOL visible;
+ (instancetype)sharedInstance;
-(void)showFromRect:(CGRect)windowRect;
-(void)hide;
@property UILabel *tooltipLabel;
/* Private */
-(void)_sizeToFit;
@end
@interface UIView (Tooltips)
@property NSString *toolTip;
/* Private */
@property BOOL _tooltipMouseOver;
@property UIHoverGestureRecognizer *_tooltipRecognizer;
@end
NS_ASSUME_NONNULL_END
//
// UIView+Tooltips.m
// Crossword
//
// Created by Steven Troughton-Smith on 13/09/2019.
// Copyright © 2019 Steven Troughton-Smith. All rights reserved.
//
#import "UIView+Tooltips.h"
#import <objc/runtime.h>
@implementation UITooltipView
+ (instancetype)sharedInstance
{
static UITooltipView *sharedInstance = nil;
static dispatch_once_t onceToken; // onceToken = 0
dispatch_once(&onceToken, ^{
sharedInstance = [[UITooltipView alloc] init];
});
return sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (self) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterial];
UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
blurView.bounds = self.bounds;
blurView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[self addSubview:blurView];
self.tooltipLabel = [[UILabel alloc] init];
self.tooltipLabel.text = @"";
self.tooltipLabel.font = [UIFont systemFontOfSize:14];
self.tooltipLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.tooltipLabel.lineBreakMode = NSLineBreakByClipping;
[[blurView contentView] addSubview:self.tooltipLabel];
self.layer.borderWidth = 1.0/[UIScreen mainScreen].scale;
self.layer.borderColor = [UIColor secondarySystemFillColor].CGColor;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 6;
self.layer.shadowOffset = CGSizeMake(0, 0);
self.layer.shadowOpacity = 0.3;
}
return self;
}
-(void)_sizeToFit
{
[self.tooltipLabel sizeToFit];
self.frame = CGRectInset(self.tooltipLabel.bounds, -5, -3);
self.tooltipLabel.frame = CGRectInset(self.bounds, 5, 3);
}
-(void)showFromRect:(CGRect)convertedRect
{
[CATransaction setDisableActions:YES];
[self _sizeToFit];
CGRect labelRect = self.bounds;
self.alpha = 0;
self.frame = CGRectMake(convertedRect.origin.x+convertedRect.size.width/2-labelRect.size.width/2, convertedRect.origin.y+convertedRect.size.height, self.frame.size.width, self.frame.size.height);
[CATransaction setDisableActions:NO];
self.visible = YES;
[UIView animateWithDuration:0.1 animations:^{
self.alpha = 1;
} completion:nil];
}
-(void)hide
{
[UIView animateWithDuration:0.1 animations:^{
self.alpha = 0;
} completion:^(BOOL finished) {
self.visible = NO;
}];
}
@end
@implementation UIView (Tooltips)
#pragma mark - Properties
-(void)set_tooltipMouseOver:(BOOL)tooltipMouseOver
{
objc_setAssociatedObject(self, @selector(_tooltipMouseOver), @(tooltipMouseOver), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)_tooltipMouseOver
{
return [objc_getAssociatedObject(self, @selector(_tooltipMouseOver)) boolValue];
}
-(void)set_tooltipRecognizer:(UIHoverGestureRecognizer *)obj
{
objc_setAssociatedObject(self, @selector(_tooltipRecognizer), obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIHoverGestureRecognizer *)_tooltipRecognizer
{
return objc_getAssociatedObject(self, @selector(_tooltipRecognizer));
}
-(void)setToolTip:(NSString *)obj
{
objc_setAssociatedObject(self, @selector(toolTip), obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (!self._tooltipRecognizer && obj && [obj length])
[self _installTooltipRecognizer];
}
- (NSString *)toolTip
{
return objc_getAssociatedObject(self, @selector(toolTip));
}
#pragma mark - Recognizer
-(void)_installTooltipRecognizer
{
self._tooltipRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(tooltipHover:)];
[self addGestureRecognizer:self._tooltipRecognizer];
}
-(void)_beginTooltipTimer
{
CGFloat time = 2.0;
if ([UITooltipView sharedInstance].visible)
time = 0.0;
[NSTimer scheduledTimerWithTimeInterval:time repeats:NO block:^(NSTimer * _Nonnull timer) {
if (self._tooltipMouseOver)
{
CGRect convertedRect = [self convertRect:self.bounds toView:self.window];
[UITooltipView sharedInstance].tooltipLabel.text = self.toolTip;
[self.window addSubview:[UITooltipView sharedInstance]];
[[UITooltipView sharedInstance] showFromRect:convertedRect];
}
}];
}
-(void)tooltipHover:(UIHoverGestureRecognizer *)recognizer
{
switch (recognizer.state)
{
case UIGestureRecognizerStateBegan:
{
self._tooltipMouseOver = YES;
[self _beginTooltipTimer];
break;
}
case UIGestureRecognizerStateChanged:
{
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
{
self._tooltipMouseOver = NO;
[[UITooltipView sharedInstance] hide];
break;
}
default:
break;
}
}
@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.