mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2025-08-25 03:48:51 -04:00
added files via upload
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputColorView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/30/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputColorView : FLEXArgumentInputView
|
||||
|
||||
@end
|
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// FLEXArgumentInputColorView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/30/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputColorView.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
@protocol FLEXColorComponentInputViewDelegate;
|
||||
|
||||
@interface FLEXColorComponentInputView : UIView
|
||||
|
||||
@property (nonatomic) UISlider *slider;
|
||||
@property (nonatomic) UILabel *valueLabel;
|
||||
|
||||
@property (nonatomic, weak) id <FLEXColorComponentInputViewDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FLEXColorComponentInputViewDelegate <NSObject>
|
||||
|
||||
- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation FLEXColorComponentInputView
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.slider = [UISlider new];
|
||||
[self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[self addSubview:self.slider];
|
||||
|
||||
self.valueLabel = [UILabel new];
|
||||
self.valueLabel.backgroundColor = self.backgroundColor;
|
||||
self.valueLabel.font = [UIFont systemFontOfSize:14.0];
|
||||
self.valueLabel.textAlignment = NSTextAlignmentRight;
|
||||
[self addSubview:self.valueLabel];
|
||||
|
||||
[self updateValueLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
self.slider.backgroundColor = backgroundColor;
|
||||
self.valueLabel.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
const CGFloat kValueLabelWidth = 50.0;
|
||||
|
||||
[self.slider sizeToFit];
|
||||
CGFloat sliderWidth = self.bounds.size.width - kValueLabelWidth;
|
||||
self.slider.frame = CGRectMake(0, 0, sliderWidth, self.slider.frame.size.height);
|
||||
|
||||
[self.valueLabel sizeToFit];
|
||||
CGFloat valueLabelOriginX = CGRectGetMaxX(self.slider.frame);
|
||||
CGFloat valueLabelOriginY = FLEXFloor((self.slider.frame.size.height - self.valueLabel.frame.size.height) / 2.0);
|
||||
self.valueLabel.frame = CGRectMake(valueLabelOriginX, valueLabelOriginY, kValueLabelWidth, self.valueLabel.frame.size.height);
|
||||
}
|
||||
|
||||
- (void)sliderChanged:(id)sender {
|
||||
[self.delegate colorComponentInputViewValueDidChange:self];
|
||||
[self updateValueLabel];
|
||||
}
|
||||
|
||||
- (void)updateValueLabel {
|
||||
self.valueLabel.text = [NSString stringWithFormat:@"%.3f", self.slider.value];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGFloat height = [self.slider sizeThatFits:size].height;
|
||||
return CGSizeMake(size.width, height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FLEXColorPreviewBox : UIView
|
||||
|
||||
@property (nonatomic) UIColor *color;
|
||||
|
||||
@property (nonatomic) UIView *colorOverlayView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXColorPreviewBox
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.layer.borderWidth = 1.0;
|
||||
self.layer.borderColor = UIColor.blackColor.CGColor;
|
||||
self.backgroundColor = [UIColor colorWithPatternImage:[[self class] backgroundPatternImage]];
|
||||
|
||||
self.colorOverlayView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
self.colorOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.colorOverlayView.backgroundColor = UIColor.clearColor;
|
||||
[self addSubview:self.colorOverlayView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setColor:(UIColor *)color {
|
||||
self.colorOverlayView.backgroundColor = color;
|
||||
}
|
||||
|
||||
- (UIColor *)color {
|
||||
return self.colorOverlayView.backgroundColor;
|
||||
}
|
||||
|
||||
+ (UIImage *)backgroundPatternImage {
|
||||
const CGFloat kSquareDimension = 5.0;
|
||||
CGSize squareSize = CGSizeMake(kSquareDimension, kSquareDimension);
|
||||
CGSize imageSize = CGSizeMake(2.0 * kSquareDimension, 2.0 * kSquareDimension);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(imageSize, YES, UIScreen.mainScreen.scale);
|
||||
|
||||
[UIColor.whiteColor setFill];
|
||||
UIRectFill(CGRectMake(0, 0, imageSize.width, imageSize.height));
|
||||
|
||||
[UIColor.grayColor setFill];
|
||||
UIRectFill(CGRectMake(squareSize.width, 0, squareSize.width, squareSize.height));
|
||||
UIRectFill(CGRectMake(0, squareSize.height, squareSize.width, squareSize.height));
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FLEXArgumentInputColorView () <FLEXColorComponentInputViewDelegate>
|
||||
|
||||
@property (nonatomic) FLEXColorPreviewBox *colorPreviewBox;
|
||||
@property (nonatomic) UILabel *hexLabel;
|
||||
@property (nonatomic) FLEXColorComponentInputView *alphaInput;
|
||||
@property (nonatomic) FLEXColorComponentInputView *redInput;
|
||||
@property (nonatomic) FLEXColorComponentInputView *greenInput;
|
||||
@property (nonatomic) FLEXColorComponentInputView *blueInput;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputColorView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.colorPreviewBox = [FLEXColorPreviewBox new];
|
||||
[self addSubview:self.colorPreviewBox];
|
||||
|
||||
self.hexLabel = [UILabel new];
|
||||
self.hexLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.9];
|
||||
self.hexLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.hexLabel.font = [UIFont systemFontOfSize:12.0];
|
||||
[self addSubview:self.hexLabel];
|
||||
|
||||
self.alphaInput = [FLEXColorComponentInputView new];
|
||||
self.alphaInput.slider.minimumTrackTintColor = UIColor.blackColor;
|
||||
self.alphaInput.delegate = self;
|
||||
[self addSubview:self.alphaInput];
|
||||
|
||||
self.redInput = [FLEXColorComponentInputView new];
|
||||
self.redInput.slider.minimumTrackTintColor = UIColor.redColor;
|
||||
self.redInput.delegate = self;
|
||||
[self addSubview:self.redInput];
|
||||
|
||||
self.greenInput = [FLEXColorComponentInputView new];
|
||||
self.greenInput.slider.minimumTrackTintColor = UIColor.greenColor;
|
||||
self.greenInput.delegate = self;
|
||||
[self addSubview:self.greenInput];
|
||||
|
||||
self.blueInput = [FLEXColorComponentInputView new];
|
||||
self.blueInput.slider.minimumTrackTintColor = UIColor.blueColor;
|
||||
self.blueInput.delegate = self;
|
||||
[self addSubview:self.blueInput];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
self.alphaInput.backgroundColor = backgroundColor;
|
||||
self.redInput.backgroundColor = backgroundColor;
|
||||
self.greenInput.backgroundColor = backgroundColor;
|
||||
self.blueInput.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
CGFloat runningOriginY = 0;
|
||||
CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
|
||||
|
||||
self.colorPreviewBox.frame = CGRectMake(0, runningOriginY, self.bounds.size.width, [[self class] colorPreviewBoxHeight]);
|
||||
runningOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) + [[self class] inputViewVerticalPadding];
|
||||
|
||||
[self.hexLabel sizeToFit];
|
||||
const CGFloat kLabelVerticalOutsetAmount = 0.0;
|
||||
const CGFloat kLabelHorizontalOutsetAmount = 2.0;
|
||||
UIEdgeInsets labelOutset = UIEdgeInsetsMake(-kLabelVerticalOutsetAmount, -kLabelHorizontalOutsetAmount, -kLabelVerticalOutsetAmount, -kLabelHorizontalOutsetAmount);
|
||||
self.hexLabel.frame = UIEdgeInsetsInsetRect(self.hexLabel.frame, labelOutset);
|
||||
CGFloat hexLabelOriginX = self.colorPreviewBox.layer.borderWidth;
|
||||
CGFloat hexLabelOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) - self.colorPreviewBox.layer.borderWidth - self.hexLabel.frame.size.height;
|
||||
self.hexLabel.frame = CGRectMake(hexLabelOriginX, hexLabelOriginY, self.hexLabel.frame.size.width, self.hexLabel.frame.size.height);
|
||||
|
||||
NSArray<FLEXColorComponentInputView *> *colorComponentInputViews = @[self.alphaInput, self.redInput, self.greenInput, self.blueInput];
|
||||
for (FLEXColorComponentInputView *inputView in colorComponentInputViews) {
|
||||
CGSize fitSize = [inputView sizeThatFits:constrainSize];
|
||||
inputView.frame = CGRectMake(0, runningOriginY, fitSize.width, fitSize.height);
|
||||
runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] inputViewVerticalPadding];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue isKindOfClass:[UIColor class]]) {
|
||||
[self updateWithColor:inputValue];
|
||||
} else if ([inputValue isKindOfClass:[NSValue class]]) {
|
||||
const char *type = [inputValue objCType];
|
||||
if (strcmp(type, @encode(CGColorRef)) == 0) {
|
||||
CGColorRef colorRef;
|
||||
[inputValue getValue:&colorRef];
|
||||
UIColor *color = [[UIColor alloc] initWithCGColor:colorRef];
|
||||
[self updateWithColor:color];
|
||||
}
|
||||
} else {
|
||||
[self updateWithColor:UIColor.clearColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
return [UIColor colorWithRed:self.redInput.slider.value green:self.greenInput.slider.value blue:self.blueInput.slider.value alpha:self.alphaInput.slider.value];
|
||||
}
|
||||
|
||||
- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView {
|
||||
[self updateColorPreview];
|
||||
}
|
||||
|
||||
- (void)updateWithColor:(UIColor *)color {
|
||||
CGFloat red, green, blue, white, alpha;
|
||||
if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) {
|
||||
self.alphaInput.slider.value = alpha;
|
||||
[self.alphaInput updateValueLabel];
|
||||
self.redInput.slider.value = red;
|
||||
[self.redInput updateValueLabel];
|
||||
self.greenInput.slider.value = green;
|
||||
[self.greenInput updateValueLabel];
|
||||
self.blueInput.slider.value = blue;
|
||||
[self.blueInput updateValueLabel];
|
||||
} else if ([color getWhite:&white alpha:&alpha]) {
|
||||
self.alphaInput.slider.value = alpha;
|
||||
[self.alphaInput updateValueLabel];
|
||||
self.redInput.slider.value = white;
|
||||
[self.redInput updateValueLabel];
|
||||
self.greenInput.slider.value = white;
|
||||
[self.greenInput updateValueLabel];
|
||||
self.blueInput.slider.value = white;
|
||||
[self.blueInput updateValueLabel];
|
||||
}
|
||||
[self updateColorPreview];
|
||||
}
|
||||
|
||||
- (void)updateColorPreview {
|
||||
self.colorPreviewBox.color = self.inputValue;
|
||||
unsigned char redByte = self.redInput.slider.value * 255;
|
||||
unsigned char greenByte = self.greenInput.slider.value * 255;
|
||||
unsigned char blueByte = self.blueInput.slider.value * 255;
|
||||
self.hexLabel.text = [NSString stringWithFormat:@"#%02X%02X%02X", redByte, greenByte, blueByte];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGFloat height = 0;
|
||||
height += [[self class] colorPreviewBoxHeight];
|
||||
height += [[self class] inputViewVerticalPadding];
|
||||
height += [self.alphaInput sizeThatFits:size].height;
|
||||
height += [[self class] inputViewVerticalPadding];
|
||||
height += [self.redInput sizeThatFits:size].height;
|
||||
height += [[self class] inputViewVerticalPadding];
|
||||
height += [self.greenInput sizeThatFits:size].height;
|
||||
height += [[self class] inputViewVerticalPadding];
|
||||
height += [self.blueInput sizeThatFits:size].height;
|
||||
return CGSizeMake(size.width, height);
|
||||
}
|
||||
|
||||
+ (CGFloat)inputViewVerticalPadding {
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
+ (CGFloat)colorPreviewBoxHeight {
|
||||
return 40.0;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
|
||||
// We don't care if currentValue is a color or not; we will default to +clearColor
|
||||
return (strcmp(type, @encode(CGColorRef)) == 0) || (strcmp(type, FLEXEncodeClass(UIColor)) == 0);
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputDataView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Daniel Rodriguez Troitino on 2/14/15.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputDateView : FLEXArgumentInputView
|
||||
|
||||
@end
|
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// FLEXArgumentInputDataView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Daniel Rodriguez Troitino on 2/14/15.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputDateView.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
@interface FLEXArgumentInputDateView ()
|
||||
|
||||
@property (nonatomic) UIDatePicker *datePicker;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputDateView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.datePicker = [UIDatePicker new];
|
||||
self.datePicker.datePickerMode = UIDatePickerModeDateAndTime;
|
||||
// Using UTC, because that's what the NSDate description prints
|
||||
self.datePicker.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
|
||||
self.datePicker.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
|
||||
[self addSubview:self.datePicker];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue isKindOfClass:[NSDate class]]) {
|
||||
self.datePicker.date = inputValue;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
return self.datePicker.date;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.datePicker.frame = self.bounds;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGFloat height = [self.datePicker sizeThatFits:size].height;
|
||||
return CGSizeMake(size.width, height);
|
||||
}
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
return strcmp(type, FLEXEncodeClass(NSDate)) == 0;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputFontView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/28/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputFontView : FLEXArgumentInputView
|
||||
|
||||
@end
|
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// FLEXArgumentInputFontView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/28/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputFontView.h"
|
||||
#import "FLEXArgumentInputViewFactory.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXArgumentInputFontsPickerView.h"
|
||||
|
||||
@interface FLEXArgumentInputFontView ()
|
||||
|
||||
@property (nonatomic) FLEXArgumentInputView *fontNameInput;
|
||||
@property (nonatomic) FLEXArgumentInputView *pointSizeInput;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputFontView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.fontNameInput = [[FLEXArgumentInputFontsPickerView alloc] initWithArgumentTypeEncoding:FLEXEncodeClass(NSString)];
|
||||
self.fontNameInput.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
self.fontNameInput.title = @"Font Name:";
|
||||
[self addSubview:self.fontNameInput];
|
||||
|
||||
self.pointSizeInput = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:@encode(CGFloat)];
|
||||
self.pointSizeInput.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
self.pointSizeInput.title = @"Point Size:";
|
||||
[self addSubview:self.pointSizeInput];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
self.fontNameInput.backgroundColor = backgroundColor;
|
||||
self.pointSizeInput.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue isKindOfClass:[UIFont class]]) {
|
||||
UIFont *font = (UIFont *)inputValue;
|
||||
self.fontNameInput.inputValue = font.fontName;
|
||||
self.pointSizeInput.inputValue = @(font.pointSize);
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
CGFloat pointSize = 0;
|
||||
if ([self.pointSizeInput.inputValue isKindOfClass:[NSValue class]]) {
|
||||
NSValue *pointSizeValue = (NSValue *)self.pointSizeInput.inputValue;
|
||||
if (strcmp([pointSizeValue objCType], @encode(CGFloat)) == 0) {
|
||||
[pointSizeValue getValue:&pointSize];
|
||||
}
|
||||
}
|
||||
return [UIFont fontWithName:self.fontNameInput.inputValue size:pointSize];
|
||||
}
|
||||
|
||||
- (BOOL)inputViewIsFirstResponder {
|
||||
return [self.fontNameInput inputViewIsFirstResponder] || [self.pointSizeInput inputViewIsFirstResponder];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide;
|
||||
|
||||
CGSize fontNameFitSize = [self.fontNameInput sizeThatFits:self.bounds.size];
|
||||
self.fontNameInput.frame = CGRectMake(0, runningOriginY, fontNameFitSize.width, fontNameFitSize.height);
|
||||
runningOriginY = CGRectGetMaxY(self.fontNameInput.frame) + [[self class] verticalPaddingBetweenFields];
|
||||
|
||||
CGSize pointSizeFitSize = [self.pointSizeInput sizeThatFits:self.bounds.size];
|
||||
self.pointSizeInput.frame = CGRectMake(0, runningOriginY, pointSizeFitSize.width, pointSizeFitSize.height);
|
||||
}
|
||||
|
||||
+ (CGFloat)verticalPaddingBetweenFields {
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
|
||||
CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX);
|
||||
|
||||
CGFloat height = fitSize.height;
|
||||
height += [self.fontNameInput sizeThatFits:constrainSize].height;
|
||||
height += [[self class] verticalPaddingBetweenFields];
|
||||
height += [self.pointSizeInput sizeThatFits:constrainSize].height;
|
||||
|
||||
return CGSizeMake(fitSize.width, height);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
return strcmp(type, FLEXEncodeClass(UIFont)) == 0;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// FLEXArgumentInputFontsPickerView.h
|
||||
// FLEX
|
||||
//
|
||||
// Created by 啟倫 陳 on 2014/7/27.
|
||||
// Copyright (c) 2014年 f. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
|
||||
@interface FLEXArgumentInputFontsPickerView : FLEXArgumentInputTextView <UIPickerViewDataSource, UIPickerViewDelegate>
|
||||
@end
|
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// FLEXArgumentInputFontsPickerView.m
|
||||
// FLEX
|
||||
//
|
||||
// Created by 啟倫 陳 on 2014/7/27.
|
||||
// Copyright (c) 2014年 f. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputFontsPickerView.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
@interface FLEXArgumentInputFontsPickerView ()
|
||||
|
||||
@property (nonatomic) NSMutableArray<NSString *> *availableFonts;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation FLEXArgumentInputFontsPickerView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
[self createAvailableFonts];
|
||||
self.inputTextView.inputView = [self createFontsPicker];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
self.inputTextView.text = inputValue;
|
||||
if ([self.availableFonts indexOfObject:inputValue] == NSNotFound) {
|
||||
[self.availableFonts insertObject:inputValue atIndex:0];
|
||||
}
|
||||
[(UIPickerView *)self.inputTextView.inputView selectRow:[self.availableFonts indexOfObject:inputValue] inComponent:0 animated:NO];
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
return self.inputTextView.text.length > 0 ? [self.inputTextView.text copy] : nil;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
|
||||
- (UIPickerView*)createFontsPicker {
|
||||
UIPickerView *fontsPicker = [UIPickerView new];
|
||||
fontsPicker.dataSource = self;
|
||||
fontsPicker.delegate = self;
|
||||
fontsPicker.showsSelectionIndicator = YES;
|
||||
return fontsPicker;
|
||||
}
|
||||
|
||||
- (void)createAvailableFonts {
|
||||
NSMutableArray<NSString *> *unsortedFontsArray = [NSMutableArray new];
|
||||
for (NSString *eachFontFamily in UIFont.familyNames) {
|
||||
for (NSString *eachFontName in [UIFont fontNamesForFamilyName:eachFontFamily]) {
|
||||
[unsortedFontsArray addObject:eachFontName];
|
||||
}
|
||||
}
|
||||
self.availableFonts = [NSMutableArray arrayWithArray:[unsortedFontsArray sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]];
|
||||
}
|
||||
|
||||
#pragma mark - UIPickerViewDataSource
|
||||
|
||||
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
|
||||
return self.availableFonts.count;
|
||||
}
|
||||
|
||||
#pragma mark - UIPickerViewDelegate
|
||||
|
||||
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
|
||||
UILabel *fontLabel;
|
||||
if (!view) {
|
||||
fontLabel = [UILabel new];
|
||||
fontLabel.backgroundColor = UIColor.clearColor;
|
||||
fontLabel.textAlignment = NSTextAlignmentCenter;
|
||||
} else {
|
||||
fontLabel = (UILabel*)view;
|
||||
}
|
||||
UIFont *font = [UIFont fontWithName:self.availableFonts[row] size:15.0];
|
||||
NSDictionary<NSString *, id> *attributesDictionary = [NSDictionary<NSString *, id> dictionaryWithObject:font forKey:NSFontAttributeName];
|
||||
NSAttributedString *attributesString = [[NSAttributedString alloc] initWithString:self.availableFonts[row] attributes:attributesDictionary];
|
||||
fontLabel.attributedText = attributesString;
|
||||
[fontLabel sizeToFit];
|
||||
return fontLabel;
|
||||
}
|
||||
|
||||
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
|
||||
self.inputTextView.text = self.availableFonts[row];
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputNotSupportedView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/18/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
|
||||
@interface FLEXArgumentInputNotSupportedView : FLEXArgumentInputTextView
|
||||
|
||||
@end
|
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// FLEXArgumentInputNotSupportedView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/18/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputNotSupportedView.h"
|
||||
#import "FLEXColor.h"
|
||||
|
||||
@implementation FLEXArgumentInputNotSupportedView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.inputTextView.userInteractionEnabled = NO;
|
||||
self.inputTextView.backgroundColor = [FLEXColor secondaryGroupedBackgroundColorWithAlpha:0.5];
|
||||
self.inputPlaceholderText = @"nil (type not supported)";
|
||||
self.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputNumberView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
|
||||
@interface FLEXArgumentInputNumberView : FLEXArgumentInputTextView
|
||||
|
||||
@end
|
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// FLEXArgumentInputNumberView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputNumberView.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
@implementation FLEXArgumentInputNumberView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
|
||||
self.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue respondsToSelector:@selector(stringValue)]) {
|
||||
self.inputTextView.text = [inputValue stringValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
return [FLEXRuntimeUtility valueForNumberWithObjCType:self.typeEncoding.UTF8String fromInputString:self.inputTextView.text];
|
||||
}
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
|
||||
static NSArray<NSString *> *supportedTypes = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
supportedTypes = @[
|
||||
@FLEXEncodeClass(NSNumber),
|
||||
@FLEXEncodeClass(NSDecimalNumber),
|
||||
@(@encode(char)),
|
||||
@(@encode(int)),
|
||||
@(@encode(short)),
|
||||
@(@encode(long)),
|
||||
@(@encode(long long)),
|
||||
@(@encode(unsigned char)),
|
||||
@(@encode(unsigned int)),
|
||||
@(@encode(unsigned short)),
|
||||
@(@encode(unsigned long)),
|
||||
@(@encode(unsigned long long)),
|
||||
@(@encode(float)),
|
||||
@(@encode(double)),
|
||||
@(@encode(long double))
|
||||
];
|
||||
});
|
||||
|
||||
return type && [supportedTypes containsObject:@(type)];
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputObjectView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
|
||||
@interface FLEXArgumentInputObjectView : FLEXArgumentInputTextView
|
||||
|
||||
@end
|
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// FLEXArgumentInputJSONObjectView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputObjectView.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
static const CGFloat kSegmentInputMargin = 10;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FLEXArgInputObjectType) {
|
||||
FLEXArgInputObjectTypeJSON,
|
||||
FLEXArgInputObjectTypeAddress
|
||||
};
|
||||
|
||||
@interface FLEXArgumentInputObjectView ()
|
||||
|
||||
@property (nonatomic) UISegmentedControl *objectTypeSegmentControl;
|
||||
@property (nonatomic) FLEXArgInputObjectType inputType;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputObjectView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
// Start with the numbers and punctuation keyboard since quotes, curly braces, or
|
||||
// square brackets are likely to be the first characters type for the JSON.
|
||||
self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
|
||||
self.targetSize = FLEXArgumentInputViewSizeLarge;
|
||||
|
||||
self.objectTypeSegmentControl = [[UISegmentedControl alloc] initWithItems:@[@"Value", @"Address"]];
|
||||
[self.objectTypeSegmentControl addTarget:self action:@selector(didChangeType) forControlEvents:UIControlEventValueChanged];
|
||||
self.objectTypeSegmentControl.selectedSegmentIndex = 0;
|
||||
[self addSubview:self.objectTypeSegmentControl];
|
||||
|
||||
self.inputType = [[self class] preferredDefaultTypeForObjCType:typeEncoding withCurrentValue:nil];
|
||||
self.objectTypeSegmentControl.selectedSegmentIndex = self.inputType;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didChangeType {
|
||||
self.inputType = self.objectTypeSegmentControl.selectedSegmentIndex;
|
||||
|
||||
if (super.inputValue) {
|
||||
// Trigger an update to the text field to show
|
||||
// the address of the stored object we were given,
|
||||
// or to show a JSON representation of the object
|
||||
[self populateTextAreaFromValue:super.inputValue];
|
||||
} else {
|
||||
// Clear the text field
|
||||
[self populateTextAreaFromValue:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setInputType:(FLEXArgInputObjectType)inputType {
|
||||
if (_inputType == inputType) return;
|
||||
|
||||
_inputType = inputType;
|
||||
|
||||
// Resize input view
|
||||
switch (inputType) {
|
||||
case FLEXArgInputObjectTypeJSON:
|
||||
self.targetSize = FLEXArgumentInputViewSizeLarge;
|
||||
break;
|
||||
case FLEXArgInputObjectTypeAddress:
|
||||
self.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
break;
|
||||
}
|
||||
|
||||
// Change placeholder
|
||||
switch (inputType) {
|
||||
case FLEXArgInputObjectTypeJSON:
|
||||
self.inputPlaceholderText =
|
||||
@"You can put any valid JSON here, such as a string, number, array, or dictionary:"
|
||||
"\n\"This is a string\""
|
||||
"\n1234"
|
||||
"\n{ \"name\": \"Bob\", \"age\": 47 }"
|
||||
"\n["
|
||||
"\n 1, 2, 3"
|
||||
"\n]";
|
||||
break;
|
||||
case FLEXArgInputObjectTypeAddress:
|
||||
self.inputPlaceholderText = @"0x0000deadb33f";
|
||||
break;
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self.superview setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
super.inputValue = inputValue;
|
||||
[self populateTextAreaFromValue:inputValue];
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
switch (self.inputType) {
|
||||
case FLEXArgInputObjectTypeJSON:
|
||||
return [FLEXRuntimeUtility objectValueFromEditableJSONString:self.inputTextView.text];
|
||||
case FLEXArgInputObjectTypeAddress: {
|
||||
NSScanner *scanner = [NSScanner scannerWithString:self.inputTextView.text];
|
||||
|
||||
unsigned long long objectPointerValue;
|
||||
if ([scanner scanHexLongLong:&objectPointerValue]) {
|
||||
return (__bridge id)(void *)objectPointerValue;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)populateTextAreaFromValue:(id)value {
|
||||
if (!value) {
|
||||
self.inputTextView.text = nil;
|
||||
} else {
|
||||
if (self.inputType == FLEXArgInputObjectTypeJSON) {
|
||||
self.inputTextView.text = [FLEXRuntimeUtility editableJSONStringForObject:value];
|
||||
} else if (self.inputType == FLEXArgInputObjectTypeAddress) {
|
||||
self.inputTextView.text = [NSString stringWithFormat:@"%p", value];
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate methods are not called for programmatic changes
|
||||
[self textViewDidChange:self.inputTextView];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
fitSize.height += [self.objectTypeSegmentControl sizeThatFits:size].height + kSegmentInputMargin;
|
||||
|
||||
return fitSize;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height;
|
||||
self.objectTypeSegmentControl.frame = CGRectMake(
|
||||
0.0,
|
||||
// Our segmented control is taking the position
|
||||
// of the text view, as far as super is concerned,
|
||||
// and we override this property to be different
|
||||
super.topInputFieldVerticalLayoutGuide,
|
||||
self.frame.size.width,
|
||||
segmentHeight
|
||||
);
|
||||
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (CGFloat)topInputFieldVerticalLayoutGuide {
|
||||
// Our text view is offset from the segmented control
|
||||
CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height;
|
||||
return segmentHeight + super.topInputFieldVerticalLayoutGuide + kSegmentInputMargin;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
// Must be object type
|
||||
return type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass;
|
||||
}
|
||||
|
||||
+ (FLEXArgInputObjectType)preferredDefaultTypeForObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass);
|
||||
|
||||
if (value) {
|
||||
// If there's a current value, it must be serializable to JSON
|
||||
// to display the JSON editor. Otherwise display the address field.
|
||||
if ([FLEXRuntimeUtility editableJSONStringForObject:value]) {
|
||||
return FLEXArgInputObjectTypeJSON;
|
||||
} else {
|
||||
return FLEXArgInputObjectTypeAddress;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, see if we have more type information than just 'id'.
|
||||
// If we do, make sure the encoding is something serializable to JSON.
|
||||
// Properties and ivars keep more detailed type encoding information than method arguments.
|
||||
if (strcmp(type, @encode(id)) != 0) {
|
||||
BOOL isJSONSerializableType = NO;
|
||||
|
||||
// Parse class name out of the string,
|
||||
// which is in the form `@"ClassName"`
|
||||
Class cls = NSClassFromString(({
|
||||
NSString *className = nil;
|
||||
NSScanner *scan = [NSScanner scannerWithString:@(type)];
|
||||
NSCharacterSet *allowed = [NSCharacterSet
|
||||
characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$"
|
||||
];
|
||||
|
||||
// Skip over the @" then scan the name
|
||||
if ([scan scanString:@"@\"" intoString:nil]) {
|
||||
[scan scanCharactersFromSet:allowed intoString:&className];
|
||||
}
|
||||
|
||||
className;
|
||||
}));
|
||||
|
||||
// Note: we can't use @encode(NSString) here because that drops
|
||||
// the class information and just goes to @encode(id).
|
||||
NSArray<Class> *jsonTypes = @[
|
||||
[NSString class],
|
||||
[NSNumber class],
|
||||
[NSArray class],
|
||||
[NSDictionary class],
|
||||
];
|
||||
|
||||
// Look for matching types
|
||||
for (Class jsonClass in jsonTypes) {
|
||||
if ([cls isSubclassOfClass:jsonClass]) {
|
||||
isJSONSerializableType = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isJSONSerializableType) {
|
||||
return FLEXArgInputObjectTypeJSON;
|
||||
} else {
|
||||
return FLEXArgInputObjectTypeAddress;
|
||||
}
|
||||
} else {
|
||||
return FLEXArgInputObjectTypeAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputStringView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/28/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
|
||||
@interface FLEXArgumentInputStringView : FLEXArgumentInputTextView
|
||||
|
||||
@end
|
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// FLEXArgumentInputStringView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/28/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputStringView.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
|
||||
@implementation FLEXArgumentInputStringView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
FLEXTypeEncoding type = typeEncoding[0];
|
||||
if (type == FLEXTypeEncodingConst) {
|
||||
// A crash here would mean an invalid type encoding string
|
||||
type = typeEncoding[1];
|
||||
}
|
||||
|
||||
// Selectors don't need a multi-line text box
|
||||
if (type == FLEXTypeEncodingSelector) {
|
||||
self.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
} else {
|
||||
self.targetSize = FLEXArgumentInputViewSizeLarge;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue isKindOfClass:[NSString class]]) {
|
||||
self.inputTextView.text = inputValue;
|
||||
} else if ([inputValue isKindOfClass:[NSValue class]]) {
|
||||
NSValue *value = (id)inputValue;
|
||||
NSParameterAssert(strlen(value.objCType) == 1);
|
||||
|
||||
// C-String or SEL from NSValue
|
||||
FLEXTypeEncoding type = value.objCType[0];
|
||||
if (type == FLEXTypeEncodingConst) {
|
||||
// A crash here would mean an invalid type encoding string
|
||||
type = value.objCType[1];
|
||||
}
|
||||
|
||||
if (type == FLEXTypeEncodingCString) {
|
||||
self.inputTextView.text = @((const char *)value.pointerValue);
|
||||
} else if (type == FLEXTypeEncodingSelector) {
|
||||
self.inputTextView.text = NSStringFromSelector((SEL)value.pointerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
NSString *text = self.inputTextView.text;
|
||||
// Interpret empty string as nil. We loose the ability to set empty string as a string value,
|
||||
// but we accept that tradeoff in exchange for not having to type quotes for every string.
|
||||
if (!text.length) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Case: C-strings and SELs
|
||||
if (self.typeEncoding.length <= 2) {
|
||||
FLEXTypeEncoding type = [self.typeEncoding characterAtIndex:0];
|
||||
if (type == FLEXTypeEncodingConst) {
|
||||
// A crash here would mean an invalid type encoding string
|
||||
type = [self.typeEncoding characterAtIndex:1];
|
||||
}
|
||||
|
||||
if (type == FLEXTypeEncodingCString || type == FLEXTypeEncodingSelector) {
|
||||
const char *encoding = self.typeEncoding.UTF8String;
|
||||
SEL selector = NSSelectorFromString(text);
|
||||
return [NSValue valueWithBytes:&selector objCType:encoding];
|
||||
}
|
||||
}
|
||||
|
||||
// Case: NSStrings
|
||||
return self.inputTextView.text.copy;
|
||||
}
|
||||
|
||||
// TODO: Support using object address for strings, as in the object arg view.
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
unsigned long len = strlen(type);
|
||||
|
||||
BOOL isConst = type[0] == FLEXTypeEncodingConst;
|
||||
NSInteger i = isConst ? 1 : 0;
|
||||
|
||||
BOOL typeIsString = strcmp(type, FLEXEncodeClass(NSString)) == 0;
|
||||
BOOL typeIsCString = len <= 2 && type[i] == FLEXTypeEncodingCString;
|
||||
BOOL typeIsSEL = len <= 2 && type[i] == FLEXTypeEncodingSelector;
|
||||
BOOL valueIsString = [value isKindOfClass:[NSString class]];
|
||||
|
||||
BOOL typeIsPrimitiveString = typeIsSEL || typeIsCString;
|
||||
BOOL typeIsSupported = typeIsString || typeIsCString || typeIsSEL;
|
||||
|
||||
BOOL valueIsNSValueWithCorrectType = NO;
|
||||
if ([value isKindOfClass:[NSValue class]]) {
|
||||
NSValue *v = (id)value;
|
||||
len = strlen(v.objCType);
|
||||
if (len == 1) {
|
||||
FLEXTypeEncoding type = v.objCType[i];
|
||||
if (type == FLEXTypeEncodingCString && typeIsCString) {
|
||||
valueIsNSValueWithCorrectType = YES;
|
||||
} else if (type == FLEXTypeEncodingSelector && typeIsSEL) {
|
||||
valueIsNSValueWithCorrectType = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!value && typeIsSupported) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (typeIsString && valueIsString) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Primitive strings can be input as NSStrings or NSValues
|
||||
if (typeIsPrimitiveString && (valueIsString || valueIsNSValueWithCorrectType)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputStructView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/16/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputStructView : FLEXArgumentInputView
|
||||
|
||||
@end
|
@@ -0,0 +1,220 @@
|
||||
//
|
||||
// FLEXArgumentInputStructView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/16/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputStructView.h"
|
||||
#import "FLEXArgumentInputViewFactory.h"
|
||||
#import "FLEXRuntimeUtility.h"
|
||||
#import "FLEXTypeEncodingParser.h"
|
||||
|
||||
@interface FLEXArgumentInputStructView ()
|
||||
|
||||
@property (nonatomic) NSArray<FLEXArgumentInputView *> *argumentInputViews;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputStructView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
NSMutableArray<FLEXArgumentInputView *> *inputViews = [NSMutableArray new];
|
||||
NSArray<NSString *> *customTitles = [[self class] customFieldTitlesForTypeEncoding:typeEncoding];
|
||||
[FLEXRuntimeUtility enumerateTypesInStructEncoding:typeEncoding usingBlock:^(NSString *structName,
|
||||
const char *fieldTypeEncoding,
|
||||
NSString *prettyTypeEncoding,
|
||||
NSUInteger fieldIndex,
|
||||
NSUInteger fieldOffset) {
|
||||
|
||||
FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:fieldTypeEncoding];
|
||||
inputView.targetSize = FLEXArgumentInputViewSizeSmall;
|
||||
|
||||
if (fieldIndex < customTitles.count) {
|
||||
inputView.title = customTitles[fieldIndex];
|
||||
} else {
|
||||
inputView.title = [NSString stringWithFormat:@"%@ field %lu (%@)",
|
||||
structName, (unsigned long)fieldIndex, prettyTypeEncoding
|
||||
];
|
||||
}
|
||||
|
||||
[inputViews addObject:inputView];
|
||||
[self addSubview:inputView];
|
||||
}];
|
||||
self.argumentInputViews = inputViews;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Superclass Overrides
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
|
||||
inputView.backgroundColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
if ([inputValue isKindOfClass:[NSValue class]]) {
|
||||
const char *structTypeEncoding = [inputValue objCType];
|
||||
if (strcmp(self.typeEncoding.UTF8String, structTypeEncoding) == 0) {
|
||||
NSUInteger valueSize = 0;
|
||||
|
||||
if (FLEXGetSizeAndAlignment(structTypeEncoding, &valueSize, NULL)) {
|
||||
void *unboxedValue = malloc(valueSize);
|
||||
[inputValue getValue:unboxedValue];
|
||||
[FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName,
|
||||
const char *fieldTypeEncoding,
|
||||
NSString *prettyTypeEncoding,
|
||||
NSUInteger fieldIndex,
|
||||
NSUInteger fieldOffset) {
|
||||
|
||||
void *fieldPointer = unboxedValue + fieldOffset;
|
||||
FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex];
|
||||
|
||||
if (fieldTypeEncoding[0] == FLEXTypeEncodingObjcObject || fieldTypeEncoding[0] == FLEXTypeEncodingObjcClass) {
|
||||
inputView.inputValue = (__bridge id)fieldPointer;
|
||||
} else {
|
||||
NSValue *boxedField = [FLEXRuntimeUtility valueForPrimitivePointer:fieldPointer objCType:fieldTypeEncoding];
|
||||
inputView.inputValue = boxedField;
|
||||
}
|
||||
}];
|
||||
free(unboxedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
NSValue *boxedStruct = nil;
|
||||
const char *structTypeEncoding = self.typeEncoding.UTF8String;
|
||||
NSUInteger structSize = 0;
|
||||
|
||||
if (FLEXGetSizeAndAlignment(structTypeEncoding, &structSize, NULL)) {
|
||||
void *unboxedStruct = malloc(structSize);
|
||||
[FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName,
|
||||
const char *fieldTypeEncoding,
|
||||
NSString *prettyTypeEncoding,
|
||||
NSUInteger fieldIndex,
|
||||
NSUInteger fieldOffset) {
|
||||
|
||||
void *fieldPointer = unboxedStruct + fieldOffset;
|
||||
FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex];
|
||||
|
||||
if (fieldTypeEncoding[0] == FLEXTypeEncodingObjcObject || fieldTypeEncoding[0] == FLEXTypeEncodingObjcClass) {
|
||||
// Object fields
|
||||
memcpy(fieldPointer, (__bridge void *)inputView.inputValue, sizeof(id));
|
||||
} else {
|
||||
// Boxed primitive/struct fields
|
||||
id inputValue = inputView.inputValue;
|
||||
if ([inputValue isKindOfClass:[NSValue class]] && strcmp([inputValue objCType], fieldTypeEncoding) == 0) {
|
||||
[inputValue getValue:fieldPointer];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
boxedStruct = [NSValue value:unboxedStruct withObjCType:structTypeEncoding];
|
||||
free(unboxedStruct);
|
||||
}
|
||||
|
||||
return boxedStruct;
|
||||
}
|
||||
|
||||
- (BOOL)inputViewIsFirstResponder {
|
||||
BOOL isFirstResponder = NO;
|
||||
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
|
||||
if ([inputView inputViewIsFirstResponder]) {
|
||||
isFirstResponder = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isFirstResponder;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide;
|
||||
|
||||
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
|
||||
CGSize inputFitSize = [inputView sizeThatFits:self.bounds.size];
|
||||
inputView.frame = CGRectMake(0, runningOriginY, inputFitSize.width, inputFitSize.height);
|
||||
runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] verticalPaddingBetweenFields];
|
||||
}
|
||||
}
|
||||
|
||||
+ (CGFloat)verticalPaddingBetweenFields {
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
|
||||
CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX);
|
||||
CGFloat height = fitSize.height;
|
||||
|
||||
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
|
||||
height += [inputView sizeThatFits:constrainSize].height;
|
||||
height += [[self class] verticalPaddingBetweenFields];
|
||||
}
|
||||
|
||||
return CGSizeMake(fitSize.width, height);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Class Helpers
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
if (type[0] == FLEXTypeEncodingStructBegin) {
|
||||
return FLEXGetSizeAndAlignment(type, nil, nil);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)customFieldTitlesForTypeEncoding:(const char *)typeEncoding {
|
||||
NSArray<NSString *> *customTitles = nil;
|
||||
if (strcmp(typeEncoding, @encode(CGRect)) == 0) {
|
||||
customTitles = @[@"CGPoint origin", @"CGSize size"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGPoint)) == 0) {
|
||||
customTitles = @[@"CGFloat x", @"CGFloat y"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGSize)) == 0) {
|
||||
customTitles = @[@"CGFloat width", @"CGFloat height"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGVector)) == 0) {
|
||||
customTitles = @[@"CGFloat dx", @"CGFloat dy"];
|
||||
} else if (strcmp(typeEncoding, @encode(UIEdgeInsets)) == 0) {
|
||||
customTitles = @[@"CGFloat top", @"CGFloat left", @"CGFloat bottom", @"CGFloat right"];
|
||||
} else if (strcmp(typeEncoding, @encode(UIOffset)) == 0) {
|
||||
customTitles = @[@"CGFloat horizontal", @"CGFloat vertical"];
|
||||
} else if (strcmp(typeEncoding, @encode(NSRange)) == 0) {
|
||||
customTitles = @[@"NSUInteger location", @"NSUInteger length"];
|
||||
} else if (strcmp(typeEncoding, @encode(CATransform3D)) == 0) {
|
||||
customTitles = @[@"CGFloat m11", @"CGFloat m12", @"CGFloat m13", @"CGFloat m14",
|
||||
@"CGFloat m21", @"CGFloat m22", @"CGFloat m23", @"CGFloat m24",
|
||||
@"CGFloat m31", @"CGFloat m32", @"CGFloat m33", @"CGFloat m34",
|
||||
@"CGFloat m41", @"CGFloat m42", @"CGFloat m43", @"CGFloat m44"];
|
||||
} else if (strcmp(typeEncoding, @encode(CGAffineTransform)) == 0) {
|
||||
customTitles = @[@"CGFloat a", @"CGFloat b",
|
||||
@"CGFloat c", @"CGFloat d",
|
||||
@"CGFloat tx", @"CGFloat ty"];
|
||||
} else {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (strcmp(typeEncoding, @encode(NSDirectionalEdgeInsets)) == 0) {
|
||||
customTitles = @[@"CGFloat top", @"CGFloat leading",
|
||||
@"CGFloat bottom", @"CGFloat trailing"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return customTitles;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// FLEXArgumentInputSwitchView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/16/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputSwitchView : FLEXArgumentInputView
|
||||
|
||||
@end
|
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// FLEXArgumentInputSwitchView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 6/16/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputSwitchView.h"
|
||||
|
||||
@interface FLEXArgumentInputSwitchView ()
|
||||
|
||||
@property (nonatomic) UISwitch *inputSwitch;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputSwitchView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.inputSwitch = [UISwitch new];
|
||||
[self.inputSwitch addTarget:self action:@selector(switchValueDidChange:) forControlEvents:UIControlEventValueChanged];
|
||||
[self.inputSwitch sizeToFit];
|
||||
[self addSubview:self.inputSwitch];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Input/Output
|
||||
|
||||
- (void)setInputValue:(id)inputValue {
|
||||
BOOL on = NO;
|
||||
if ([inputValue isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *number = (NSNumber *)inputValue;
|
||||
on = [number boolValue];
|
||||
} else if ([inputValue isKindOfClass:[NSValue class]]) {
|
||||
NSValue *value = (NSValue *)inputValue;
|
||||
if (strcmp([value objCType], @encode(BOOL)) == 0) {
|
||||
[value getValue:&on];
|
||||
}
|
||||
}
|
||||
self.inputSwitch.on = on;
|
||||
}
|
||||
|
||||
- (id)inputValue {
|
||||
BOOL isOn = [self.inputSwitch isOn];
|
||||
NSValue *boxedBool = [NSValue value:&isOn withObjCType:@encode(BOOL)];
|
||||
return boxedBool;
|
||||
}
|
||||
|
||||
- (void)switchValueDidChange:(id)sender {
|
||||
[self.delegate argumentInputViewValueDidChange:self];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
self.inputSwitch.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.inputSwitch.frame.size.width, self.inputSwitch.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
fitSize.height += self.inputSwitch.frame.size.height;
|
||||
return fitSize;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Class Helpers
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
NSParameterAssert(type);
|
||||
// Only BOOLs. Current value is irrelevant.
|
||||
return strcmp(type, @encode(BOOL)) == 0;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// FLEXArgumentInputTextView.h
|
||||
// FLEXInjected
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
|
||||
@interface FLEXArgumentInputTextView : FLEXArgumentInputView <UITextViewDelegate>
|
||||
|
||||
// For subclass eyes only
|
||||
|
||||
@property (nonatomic, readonly) UITextView *inputTextView;
|
||||
@property (nonatomic) NSString *inputPlaceholderText;
|
||||
|
||||
@end
|
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// FLEXArgumentInputTextView.m
|
||||
// FLEXInjected
|
||||
//
|
||||
// Created by Ryan Olson on 6/15/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "FLEXColor.h"
|
||||
#import "FLEXArgumentInputTextView.h"
|
||||
#import "FLEXUtility.h"
|
||||
|
||||
@interface FLEXArgumentInputTextView ()
|
||||
|
||||
@property (nonatomic) UITextView *inputTextView;
|
||||
@property (nonatomic) UILabel *placeholderLabel;
|
||||
@property (nonatomic, readonly) NSUInteger numberOfInputLines;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputTextView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithArgumentTypeEncoding:typeEncoding];
|
||||
if (self) {
|
||||
self.inputTextView = [UITextView new];
|
||||
self.inputTextView.font = [[self class] inputFont];
|
||||
self.inputTextView.backgroundColor = FLEXColor.secondaryGroupedBackgroundColor;
|
||||
self.inputTextView.layer.cornerRadius = 10.f;
|
||||
self.inputTextView.contentInset = UIEdgeInsetsMake(0, 5, 0, 0);
|
||||
self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
self.inputTextView.delegate = self;
|
||||
self.inputTextView.inputAccessoryView = [self createToolBar];
|
||||
if (@available(iOS 11, *)) {
|
||||
self.inputTextView.smartQuotesType = UITextSmartQuotesTypeNo;
|
||||
[self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"];
|
||||
} else {
|
||||
self.inputTextView.layer.borderWidth = 1.f;
|
||||
self.inputTextView.layer.borderColor = FLEXColor.borderColor.CGColor;
|
||||
}
|
||||
|
||||
self.placeholderLabel = [UILabel new];
|
||||
self.placeholderLabel.font = self.inputTextView.font;
|
||||
self.placeholderLabel.textColor = FLEXColor.deemphasizedTextColor;
|
||||
self.placeholderLabel.numberOfLines = 0;
|
||||
|
||||
[self addSubview:self.inputTextView];
|
||||
[self.inputTextView addSubview:self.placeholderLabel];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (UIToolbar *)createToolBar {
|
||||
UIToolbar *toolBar = [UIToolbar new];
|
||||
[toolBar sizeToFit];
|
||||
UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil action:nil
|
||||
];
|
||||
UIBarButtonItem *pasteItem = [[UIBarButtonItem alloc]
|
||||
initWithTitle:@"Paste" style:UIBarButtonItemStyleDone
|
||||
target:self.inputTextView action:@selector(paste:)
|
||||
];
|
||||
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
||||
target:self.inputTextView action:@selector(resignFirstResponder)
|
||||
];
|
||||
toolBar.items = @[spaceItem, pasteItem, doneItem];
|
||||
return toolBar;
|
||||
}
|
||||
|
||||
- (void)setInputPlaceholderText:(NSString *)placeholder {
|
||||
self.placeholderLabel.text = placeholder;
|
||||
if (placeholder.length) {
|
||||
if (!self.inputTextView.text.length) {
|
||||
self.placeholderLabel.hidden = NO;
|
||||
} else {
|
||||
self.placeholderLabel.hidden = YES;
|
||||
}
|
||||
} else {
|
||||
self.placeholderLabel.hidden = YES;
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (NSString *)inputPlaceholderText {
|
||||
return self.placeholderLabel.text;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Superclass Overrides
|
||||
|
||||
- (BOOL)inputViewIsFirstResponder {
|
||||
return self.inputTextView.isFirstResponder;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Layout and Sizing
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
self.inputTextView.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.bounds.size.width, [self inputTextViewHeight]);
|
||||
// Placeholder label is positioned by insetting then origin
|
||||
// by the content inset then the text container inset
|
||||
CGSize s = self.inputTextView.frame.size;
|
||||
self.placeholderLabel.frame = CGRectMake(0, 0, s.width, s.height);
|
||||
self.placeholderLabel.frame = UIEdgeInsetsInsetRect(
|
||||
UIEdgeInsetsInsetRect(self.placeholderLabel.frame, self.inputTextView.contentInset),
|
||||
self.inputTextView.textContainerInset
|
||||
);
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfInputLines {
|
||||
switch (self.targetSize) {
|
||||
case FLEXArgumentInputViewSizeDefault:
|
||||
return 2;
|
||||
case FLEXArgumentInputViewSizeSmall:
|
||||
return 1;
|
||||
case FLEXArgumentInputViewSizeLarge:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)inputTextViewHeight {
|
||||
return ceil([[self class] inputFont].lineHeight * self.numberOfInputLines) + 16.0;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGSize fitSize = [super sizeThatFits:size];
|
||||
fitSize.height += [self inputTextViewHeight];
|
||||
return fitSize;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Class Helpers
|
||||
|
||||
+ (UIFont *)inputFont {
|
||||
return [UIFont systemFontOfSize:14.0];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UITextViewDelegate
|
||||
|
||||
- (void)textViewDidChange:(UITextView *)textView {
|
||||
[self.delegate argumentInputViewValueDidChange:self];
|
||||
self.placeholderLabel.hidden = !(self.inputPlaceholderText.length && !textView.text.length);
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// FLEXArgumentInputView.h
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 5/30/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) {
|
||||
/// 2 lines, medium-sized
|
||||
FLEXArgumentInputViewSizeDefault = 0,
|
||||
/// One line
|
||||
FLEXArgumentInputViewSizeSmall,
|
||||
/// Several lines
|
||||
FLEXArgumentInputViewSizeLarge
|
||||
};
|
||||
|
||||
@protocol FLEXArgumentInputViewDelegate;
|
||||
|
||||
@interface FLEXArgumentInputView : UIView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding;
|
||||
|
||||
/// The name of the field. Optional (can be nil).
|
||||
@property (nonatomic, copy) NSString *title;
|
||||
|
||||
/// To populate the filed with an initial value, set this property.
|
||||
/// To reteive the value input by the user, access the property.
|
||||
/// Primitive types and structs should/will be boxed in NSValue containers.
|
||||
/// Concrete subclasses should override both the setter and getter for this property.
|
||||
/// Subclasses can call super.inputValue to access a backing store for the value.
|
||||
@property (nonatomic) id inputValue;
|
||||
|
||||
/// Setting this value to large will make some argument input views increase the size of their input field(s).
|
||||
/// Useful to increase the use of space if there is only one input view on screen (i.e. for property and ivar editing).
|
||||
@property (nonatomic) FLEXArgumentInputViewSize targetSize;
|
||||
|
||||
/// Users of the input view can get delegate callbacks for incremental changes in user input.
|
||||
@property (nonatomic, weak) id <FLEXArgumentInputViewDelegate> delegate;
|
||||
|
||||
// Subclasses can override
|
||||
|
||||
/// If the input view has one or more text views, returns YES when one of them is focused.
|
||||
@property (nonatomic, readonly) BOOL inputViewIsFirstResponder;
|
||||
|
||||
/// For subclasses to indicate that they can handle editing a field the give type and value.
|
||||
/// Used by FLEXArgumentInputViewFactory to create appropriate input views.
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value;
|
||||
|
||||
// For subclass eyes only
|
||||
|
||||
@property (nonatomic, readonly) UILabel *titleLabel;
|
||||
@property (nonatomic, readonly) NSString *typeEncoding;
|
||||
@property (nonatomic, readonly) CGFloat topInputFieldVerticalLayoutGuide;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FLEXArgumentInputViewDelegate <NSObject>
|
||||
|
||||
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView;
|
||||
|
||||
@end
|
114
Tweaks/FLEX/Editing/ArgumentInputViews/FLEXArgumentInputView.m
Normal file
114
Tweaks/FLEX/Editing/ArgumentInputViews/FLEXArgumentInputView.m
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// FLEXArgumentInputView.m
|
||||
// Flipboard
|
||||
//
|
||||
// Created by Ryan Olson on 5/30/14.
|
||||
// Copyright (c) 2020 FLEX Team. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FLEXArgumentInputView.h"
|
||||
#import "FLEXUtility.h"
|
||||
#import "FLEXColor.h"
|
||||
|
||||
@interface FLEXArgumentInputView ()
|
||||
|
||||
@property (nonatomic) UILabel *titleLabel;
|
||||
@property (nonatomic) NSString *typeEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FLEXArgumentInputView
|
||||
|
||||
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
self.typeEncoding = typeEncoding != NULL ? @(typeEncoding) : nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
if (self.showsTitle) {
|
||||
CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
|
||||
CGSize labelSize = [self.titleLabel sizeThatFits:constrainSize];
|
||||
self.titleLabel.frame = CGRectMake(0, 0, labelSize.width, labelSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
self.titleLabel.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString *)title {
|
||||
if (![_title isEqual:title]) {
|
||||
_title = title;
|
||||
self.titleLabel.text = title;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (UILabel *)titleLabel {
|
||||
if (!_titleLabel) {
|
||||
_titleLabel = [UILabel new];
|
||||
_titleLabel.font = [[self class] titleFont];
|
||||
_titleLabel.textColor = FLEXColor.primaryTextColor;
|
||||
_titleLabel.numberOfLines = 0;
|
||||
[self addSubview:_titleLabel];
|
||||
}
|
||||
return _titleLabel;
|
||||
}
|
||||
|
||||
- (BOOL)showsTitle {
|
||||
return self.title.length > 0;
|
||||
}
|
||||
|
||||
- (CGFloat)topInputFieldVerticalLayoutGuide {
|
||||
CGFloat verticalLayoutGuide = 0;
|
||||
if (self.showsTitle) {
|
||||
CGFloat titleHeight = [self.titleLabel sizeThatFits:self.bounds.size].height;
|
||||
verticalLayoutGuide = titleHeight + [[self class] titleBottomPadding];
|
||||
}
|
||||
return verticalLayoutGuide;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Subclasses Can Override
|
||||
|
||||
- (BOOL)inputViewIsFirstResponder {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Class Helpers
|
||||
|
||||
+ (UIFont *)titleFont {
|
||||
return [UIFont systemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
+ (CGFloat)titleBottomPadding {
|
||||
return 4.0;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Sizing
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
CGFloat height = 0;
|
||||
|
||||
if (self.title.length > 0) {
|
||||
CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX);
|
||||
height += ceil([self.titleLabel sizeThatFits:constrainSize].height);
|
||||
height += [[self class] titleBottomPadding];
|
||||
}
|
||||
|
||||
return CGSizeMake(size.width, height);
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user