added files via upload

This commit is contained in:
Balackburn
2023-06-27 09:54:41 +02:00
commit 2ff6aac218
1420 changed files with 88898 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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