You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

210 lines
8.1 KiB

//
// FLEXArgumentInputStructView.m
// Flipboard
//
// Created by Ryan Olson on 6/16/14.
// Copyright (c) 2014 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputStructView.h"
#import "FLEXArgumentInputViewFactory.h"
#import "FLEXRuntimeUtility.h"
@interface FLEXArgumentInputStructView ()
@property (nonatomic, strong) NSArray<FLEXArgumentInputView *> *argumentInputViews;
@end
@implementation FLEXArgumentInputStructView
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
{
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
NSMutableArray<FLEXArgumentInputView *> *inputViews = [NSMutableArray array];
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.backgroundColor = self.backgroundColor;
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;
@try {
// NSGetSizeAndAlignment barfs on type encoding for bitfields.
NSGetSizeAndAlignment(structTypeEncoding, &valueSize, NULL);
} @catch (NSException *exception) { }
if (valueSize > 0) {
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] == @encode(id)[0] || fieldTypeEncoding[0] == @encode(Class)[0]) {
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;
@try {
// NSGetSizeAndAlignment barfs on type encoding for bitfields.
NSGetSizeAndAlignment(structTypeEncoding, &structSize, NULL);
} @catch (NSException *exception) { }
if (structSize > 0) {
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] == @encode(id)[0] || fieldTypeEncoding[0] == @encode(Class)[0]) {
// 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
{
return type && type[0] == '{';
}
+ (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(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"];
}
return customTitles;
}
@end