FLEXFieldEditorViewController.m 5.1 KB

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