FLEXFieldEditorViewController.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. //
  2. // FLEXFieldEditorViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 5/16/14.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXFieldEditorViewController.h"
  9. #import "FLEXFieldEditorView.h"
  10. #import "FLEXRuntimeUtility.h"
  11. #import "FLEXUtility.h"
  12. #import "FLEXArgumentInputView.h"
  13. @interface FLEXFieldEditorViewController () <UIScrollViewDelegate>
  14. @property (nonatomic, strong) UIScrollView *scrollView;
  15. @property (nonatomic, strong, readwrite) id target;
  16. @property (nonatomic, strong, readwrite) FLEXFieldEditorView *fieldEditorView;
  17. @property (nonatomic, strong, readwrite) UIBarButtonItem *setterButton;
  18. @end
  19. @implementation FLEXFieldEditorViewController
  20. - (id)initWithTarget:(id)target
  21. {
  22. self = [super initWithNibName:nil bundle:nil];
  23. if (self) {
  24. self.target = target;
  25. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
  26. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
  27. }
  28. return self;
  29. }
  30. - (void)dealloc
  31. {
  32. [[NSNotificationCenter defaultCenter] removeObserver:self];
  33. }
  34. - (void)keyboardDidShow:(NSNotification *)notification
  35. {
  36. CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
  37. UIEdgeInsets scrollInsets = self.scrollView.contentInset;
  38. scrollInsets.bottom = keyboardSize.height;
  39. self.scrollView.contentInset = scrollInsets;
  40. self.scrollView.scrollIndicatorInsets = scrollInsets;
  41. // Find the active input view and scroll to make sure it's visible.
  42. for (FLEXArgumentInputView *argumentInputView in self.fieldEditorView.argumentInputViews) {
  43. if (argumentInputView.inputViewIsFirstResponder) {
  44. CGRect scrollToVisibleRect = [self.scrollView convertRect:argumentInputView.bounds fromView:argumentInputView];
  45. [self.scrollView scrollRectToVisible:scrollToVisibleRect animated:YES];
  46. break;
  47. }
  48. }
  49. }
  50. - (void)keyboardWillHide:(NSNotification *)notification
  51. {
  52. UIEdgeInsets scrollInsets = self.scrollView.contentInset;
  53. scrollInsets.bottom = 0.0;
  54. self.scrollView.contentInset = scrollInsets;
  55. self.scrollView.scrollIndicatorInsets = scrollInsets;
  56. }
  57. - (BOOL)prefersStatusBarHidden
  58. {
  59. return YES;
  60. }
  61. - (void)viewDidLoad
  62. {
  63. [super viewDidLoad];
  64. self.view.backgroundColor = [FLEXUtility scrollViewGrayColor];
  65. self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
  66. self.scrollView.backgroundColor = self.view.backgroundColor;
  67. self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  68. self.scrollView.delegate = self;
  69. [self.view addSubview:self.scrollView];
  70. self.fieldEditorView = [[FLEXFieldEditorView alloc] init];
  71. self.fieldEditorView.backgroundColor = self.view.backgroundColor;
  72. self.fieldEditorView.targetDescription = [NSString stringWithFormat:@"%@ %p", [self.target class], self.target];
  73. [self.scrollView addSubview:self.fieldEditorView];
  74. // One argument input view by default. Subclasses can configure the field editor view with more/different argument input views if needed.
  75. FLEXArgumentInputView *inputView = [[FLEXArgumentInputView alloc] init];
  76. inputView.backgroundColor = self.view.backgroundColor;
  77. inputView.numberOfInputLines = 8;
  78. self.fieldEditorView.argumentInputViews = @[inputView];
  79. self.setterButton = [[UIBarButtonItem alloc] initWithTitle:[self titleForActionButton] style:UIBarButtonItemStyleDone target:self action:@selector(actionButtonPressed:)];
  80. self.navigationItem.rightBarButtonItem = self.setterButton;
  81. }
  82. - (void)viewWillLayoutSubviews
  83. {
  84. CGSize constrainSize = CGSizeMake(self.scrollView.bounds.size.width, CGFLOAT_MAX);
  85. CGSize fieldEditorSize = [self.fieldEditorView sizeThatFits:constrainSize];
  86. self.fieldEditorView.frame = CGRectMake(0, 0, fieldEditorSize.width, fieldEditorSize.height);
  87. self.scrollView.contentSize = fieldEditorSize;
  88. }
  89. - (FLEXArgumentInputView *)firstInputView
  90. {
  91. return [[self.fieldEditorView argumentInputViews] firstObject];
  92. }
  93. - (void)actionButtonPressed:(id)sender
  94. {
  95. // Subclasses can override
  96. [self.fieldEditorView endEditing:YES];
  97. }
  98. - (NSString *)titleForActionButton
  99. {
  100. // Subclasses can override.
  101. return @"Set";
  102. }
  103. + (BOOL)canEditType:(NSString *)typeEncoding currentObjectValue:(id)value
  104. {
  105. // Many primitive types can always be edited (numbers and supported structs).
  106. static NSArray *primitiveTypes = nil;
  107. static dispatch_once_t onceToken;
  108. dispatch_once(&onceToken, ^{
  109. primitiveTypes = @[@(@encode(char)),
  110. @(@encode(int)),
  111. @(@encode(short)),
  112. @(@encode(long)),
  113. @(@encode(long long)),
  114. @(@encode(unsigned char)),
  115. @(@encode(unsigned int)),
  116. @(@encode(unsigned short)),
  117. @(@encode(unsigned long)),
  118. @(@encode(unsigned long long)),
  119. @(@encode(float)),
  120. @(@encode(double)),
  121. @(@encode(CGRect)),
  122. @(@encode(CGSize)),
  123. @(@encode(CGPoint)),
  124. @(@encode(CGAffineTransform)),
  125. @(@encode(NSRange)),
  126. @(@encode(UIEdgeInsets)),
  127. @(@encode(UIOffset))];
  128. });
  129. BOOL canEdit = [primitiveTypes containsObject:typeEncoding];
  130. // Object types may be
  131. if (!canEdit) {
  132. if (value) {
  133. // If the current value is non-nil and we can represent it with an editable string, then we can suppor editing.
  134. canEdit = canEdit || [FLEXRuntimeUtility editiableDescriptionForObject:value] != nil;
  135. } else {
  136. // Also always edit types that are explicitly typed NSString, NSNumber, NSArray, or NSDictionary and are nil.
  137. // This kind of type encoding is only kept by ivars and properties. Method agruments and return types drop the class from the type encoding.
  138. // The editor supports populating these types through NSJSONSerilization parsing of the input string.
  139. canEdit = canEdit || [typeEncoding isEqual:[self stringTypeEncoding]];
  140. canEdit = canEdit || [typeEncoding isEqual:[self numberTypeEncoding]];
  141. canEdit = canEdit || [typeEncoding isEqual:[self arrayTypeEncoding]];
  142. canEdit = canEdit || [typeEncoding isEqual:[self dictionaryTypeEncoding]];
  143. }
  144. }
  145. return canEdit;
  146. }
  147. + (NSString *)stringTypeEncoding
  148. {
  149. return @"@\"NSString\"";
  150. }
  151. + (NSString *)numberTypeEncoding
  152. {
  153. return @"@\"NSNumber\"";
  154. }
  155. + (NSString *)arrayTypeEncoding
  156. {
  157. return @"@\"NSArray\"";
  158. }
  159. + (NSString *)dictionaryTypeEncoding
  160. {
  161. return @"@\"NSDictionary\"";
  162. }
  163. @end