FLEXFieldEditorViewController.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. //
  2. // FLEXFieldEditorViewController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 11/22/18.
  6. // Copyright © 2018 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXFieldEditorViewController.h"
  9. #import "FLEXFieldEditorView.h"
  10. #import "FLEXArgumentInputViewFactory.h"
  11. #import "FLEXPropertyAttributes.h"
  12. #import "FLEXUtility.h"
  13. #import "FLEXColor.h"
  14. @interface FLEXFieldEditorViewController () <FLEXArgumentInputViewDelegate>
  15. @property (nonatomic) FLEXProperty *property;
  16. @property (nonatomic) FLEXIvar *ivar;
  17. @property (nonatomic, readonly) id currentValue;
  18. @property (nonatomic, readonly) const FLEXTypeEncoding *typeEncoding;
  19. @property (nonatomic, readonly) NSString *fieldDescription;
  20. @end
  21. @implementation FLEXFieldEditorViewController
  22. #pragma mark - Initialization
  23. + (instancetype)target:(id)target property:(FLEXProperty *)property {
  24. id value = [property getValue:target];
  25. if (![self canEditProperty:property onObject:target currentValue:value]) {
  26. return nil;
  27. }
  28. FLEXFieldEditorViewController *editor = [self target:target];
  29. editor.title = @"Property";
  30. editor.property = property;
  31. return editor;
  32. }
  33. + (instancetype)target:(id)target ivar:(nonnull FLEXIvar *)ivar {
  34. FLEXFieldEditorViewController *editor = [self target:target];
  35. editor.title = @"Instance Variable";
  36. editor.ivar = ivar;
  37. return editor;
  38. }
  39. #pragma mark - Overrides
  40. - (void)viewDidLoad {
  41. [super viewDidLoad];
  42. self.view.backgroundColor = [FLEXColor groupedBackgroundColor];
  43. // Create getter button
  44. _getterButton = [[UIBarButtonItem alloc]
  45. initWithTitle:@"Get"
  46. style:UIBarButtonItemStyleDone
  47. target:self
  48. action:@selector(getterButtonPressed:)
  49. ];
  50. self.navigationItem.rightBarButtonItems = @[self.setterButton, self.getterButton];
  51. // Configure input view
  52. self.fieldEditorView.fieldDescription = self.fieldDescription;
  53. FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:self.typeEncoding];
  54. inputView.inputValue = self.currentValue;
  55. inputView.delegate = self;
  56. self.fieldEditorView.argumentInputViews = @[inputView];
  57. // Don't show a "set" button for switches; we mutate when the switch is flipped
  58. if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
  59. self.navigationItem.rightBarButtonItem = nil;
  60. }
  61. }
  62. - (void)actionButtonPressed:(id)sender {
  63. [super actionButtonPressed:sender];
  64. if (self.property) {
  65. id userInputObject = self.firstInputView.inputValue;
  66. NSArray *arguments = userInputObject ? @[userInputObject] : nil;
  67. SEL setterSelector = self.property.likelySetter;
  68. NSError *error = nil;
  69. [FLEXRuntimeUtility performSelector:setterSelector onObject:self.target withArguments:arguments error:&error];
  70. if (error) {
  71. [FLEXAlert showAlert:@"Property Setter Failed" message:error.localizedDescription from:self];
  72. sender = nil; // Don't pop back
  73. }
  74. } else {
  75. // TODO: check mutability and use mutableCopy if necessary;
  76. // this currently could and would assign NSArray to NSMutableArray
  77. [self.ivar setValue:self.firstInputView.inputValue onObject:self.target];
  78. }
  79. // Go back after setting, but not for switches.
  80. if (sender) {
  81. [self.navigationController popViewControllerAnimated:YES];
  82. } else {
  83. self.firstInputView.inputValue = self.currentValue;
  84. }
  85. }
  86. - (void)getterButtonPressed:(id)sender {
  87. [self.fieldEditorView endEditing:YES];
  88. [self exploreObjectOrPopViewController:self.currentValue];
  89. }
  90. - (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView {
  91. if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
  92. [self actionButtonPressed:nil];
  93. }
  94. }
  95. #pragma mark - Private
  96. - (id)currentValue {
  97. if (self.property) {
  98. return [self.property getValue:self.target];
  99. } else {
  100. return [self.ivar getValue:self.target];
  101. }
  102. }
  103. - (const FLEXTypeEncoding *)typeEncoding {
  104. if (self.property) {
  105. return self.property.attributes.typeEncoding.UTF8String;
  106. } else {
  107. return self.ivar.typeEncoding.UTF8String;
  108. }
  109. }
  110. - (NSString *)fieldDescription {
  111. if (self.property) {
  112. return self.property.fullDescription;
  113. } else {
  114. return self.ivar.description;
  115. }
  116. }
  117. + (BOOL)canEditProperty:(FLEXProperty *)property onObject:(id)object currentValue:(id)value {
  118. const FLEXTypeEncoding *typeEncoding = property.attributes.typeEncoding.UTF8String;
  119. BOOL canEditType = [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:value];
  120. return canEditType && [object respondsToSelector:property.likelySetter];
  121. }
  122. + (BOOL)canEditIvar:(Ivar)ivar currentValue:(id)value {
  123. return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:ivar_getTypeEncoding(ivar) currentValue:value];
  124. }
  125. @end