Files
main/Tweaks/FLEX/ViewHierarchy/SnapshotExplorer/FHSRangeSlider.m
2023-06-27 09:54:41 +02:00

202 lines
5.6 KiB
Objective-C

//
// FHSRangeSlider.m
// FLEX
//
// Created by Tanner Bennett on 1/7/20.
// Copyright © 2020 FLEX Team. All rights reserved.
//
#import "FHSRangeSlider.h"
#import "FLEXResources.h"
#import "FLEXUtility.h"
@interface FHSRangeSlider ()
@property (nonatomic, readonly) UIImageView *track;
@property (nonatomic, readonly) UIImageView *fill;
@property (nonatomic, readonly) UIImageView *leftHandle;
@property (nonatomic, readonly) UIImageView *rightHandle;
@property (nonatomic, getter=isTrackingLeftHandle) BOOL trackingLeftHandle;
@property (nonatomic, getter=isTrackingRightHandle) BOOL trackingRightHandle;
@end
@implementation FHSRangeSlider
#pragma mark - Initialization
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_allowedMaxValue = 1.f;
_maxValue = 1.f;
[self initSubviews];
}
return self;
}
- (void)initSubviews {
self.userInteractionEnabled = YES;
UIImageView * (^newSubviewImageView)(UIImage *) = ^UIImageView *(UIImage *image) {
UIImageView *iv = [UIImageView new];
iv.image = image;
// iv.userInteractionEnabled = YES;
[self addSubview:iv];
return iv;
};
_track = newSubviewImageView(FLEXResources.rangeSliderTrack);
_fill = newSubviewImageView(FLEXResources.rangeSliderFill);
_leftHandle = newSubviewImageView(FLEXResources.rangeSliderLeftHandle);
_rightHandle = newSubviewImageView(FLEXResources.rangeSliderRightHandle);
}
#pragma mark - Setters / Private
- (CGFloat)valueAt:(CGFloat)x {
CGFloat minX = self.leftHandle.image.size.width;
CGFloat maxX = self.bounds.size.width - self.rightHandle.image.size.width;
CGFloat cappedX = MIN(MAX(x, minX), maxX);
CGFloat delta = maxX - minX;
CGFloat maxDelta = self.allowedMaxValue - self.allowedMinValue;
return ((delta > 0) ? (cappedX - minX) / delta : 0) * maxDelta + self.allowedMinValue;
}
- (void)setAllowedMinValue:(CGFloat)allowedMinValue {
_allowedMinValue = allowedMinValue;
if (self.minValue < self.allowedMaxValue) {
self.minValue = self.allowedMaxValue;
} else {
[self setNeedsLayout];
}
}
- (void)setAllowedMaxValue:(CGFloat)allowedMaxValue {
_allowedMaxValue = allowedMaxValue;
if (self.maxValue > self.allowedMaxValue) {
self.maxValue = self.allowedMaxValue;
} else {
[self valuesChanged:NO];
}
}
- (void)setMinValue:(CGFloat)minValue {
_minValue = minValue;
[self valuesChanged:YES];
}
- (void)setMaxValue:(CGFloat)maxValue {
_maxValue = maxValue;
[self valuesChanged:YES];
}
- (void)valuesChanged:(BOOL)sendActions {
if (NSThread.isMainThread) {
if (sendActions) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
[self setNeedsLayout];
}
}
#pragma mark - Overrides
- (CGSize)intrinsicContentSize {
return CGSizeMake(UIViewNoIntrinsicMetric, self.leftHandle.image.size.height);
}
- (void)layoutSubviews {
[super layoutSubviews];
CGSize lhs = self.leftHandle.image.size;
CGSize rhs = self.rightHandle.image.size;
CGSize trackSize = self.track.image.size;
CGFloat delta = self.allowedMaxValue - self.allowedMinValue;
CGFloat minPercent, maxPercent;
if (delta <= 0) {
minPercent = maxPercent = 0;
} else {
minPercent = MAX(0, (self.minValue - self.allowedMinValue) / delta);
maxPercent = MAX(minPercent, (self.maxValue - self.allowedMinValue) / delta);
}
CGFloat rangeSliderWidth = self.bounds.size.width - lhs.width - rhs.width;
self.leftHandle.frame = FLEXRectMake(
rangeSliderWidth * minPercent,
CGRectGetMidY(self.bounds) - (lhs.height / 2.f) + 3.f,
lhs.width,
lhs.height
);
self.rightHandle.frame = FLEXRectMake(
lhs.width + (rangeSliderWidth * maxPercent),
CGRectGetMidY(self.bounds) - (rhs.height / 2.f) + 3.f,
rhs.width,
rhs.height
);
self.track.frame = FLEXRectMake(
lhs.width / 2.f,
CGRectGetMidY(self.bounds) - trackSize.height / 2.f,
self.bounds.size.width - (lhs.width / 2.f) - (rhs.width / 2.f),
trackSize.height
);
self.fill.frame = FLEXRectMake(
CGRectGetMidX(self.leftHandle.frame),
CGRectGetMinY(self.track.frame),
CGRectGetMidX(self.rightHandle.frame) - CGRectGetMidX(self.leftHandle.frame),
self.track.frame.size.height
);
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint loc = [touch locationInView:self];
if (CGRectContainsPoint(self.leftHandle.frame, loc)) {
self.trackingLeftHandle = YES;
self.trackingRightHandle = NO;
} else if (CGRectContainsPoint(self.rightHandle.frame, loc)) {
self.trackingLeftHandle = NO;
self.trackingRightHandle = YES;
} else {
return NO;
}
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint loc = [touch locationInView:self];
if (self.isTrackingLeftHandle) {
self.minValue = MIN(MAX(self.allowedMinValue, [self valueAt:loc.x]), self.maxValue);
} else if (self.isTrackingRightHandle) {
self.maxValue = MAX(MIN(self.allowedMaxValue, [self valueAt:loc.x]), self.minValue);
} else {
return NO;
}
[self setNeedsLayout];
[self layoutIfNeeded];
return YES;
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
self.trackingLeftHandle = NO;
self.trackingRightHandle = NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return NO;
}
@end