FLEXFieldEditorViewController.m 5.4 KB

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