-
Notifications
You must be signed in to change notification settings - Fork 0
/
CQFormItem.h
300 lines (250 loc) · 8.35 KB
/
CQFormItem.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/**
Copyright (C) 2012 Quentin Mathe
Author: Quentin Mathe <[email protected]>
Date: June 2012
License: MIT
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString * const CQFormItemUndefinedValue;
@protocol CQValueEditor
@property (nonatomic, readonly, nullable) UILabel *valueLabel;
/**
* An editor which responds to one of these accessors -value, -isOn or -text.
*
* One of these getters should be implemented. To support propagating model
* changes to the editor, a matching setter needs to be implemented.
*/
@property (nonatomic, readonly, nullable) UIView *valueEditor;
@end
@class CQFormItem;
typedef void (^ CQFormItemActionBlock)(CQFormItem *item);
/**
* Each item represents a UI component to edit a represented object property.
*
* This UI component can be presented as a form row with CQFormView.
*/
@interface CQFormItem : NSObject
/** @taskunit Initialization */
- (instancetype)initWithView: (UIView *)aView;
- (instancetype)init;
/** @taskunit Reacting to View and Represented Object Changes */
@property (nonatomic, copy) NSSet<NSString *> *observedKeyPaths;
/**
* Can be overriden to extend the Key-Value observation.
*/
- (void)updateObservedKeyPaths;
/** @taskunit Form Configuration */
/**
* Returns an empty string.
*
* Can be overriden, but must not return nil.
*
* CQFormItem uses it as the initial section name.
*
* See -setSectionName:
*/
+ (NSString *)defaultSectionName;
/**
* The section name or an empty string if the item must not be presented in a
* UITableView section.
*
* By default, returns +defaultSectionName.
*
* For a nil name, raises an NSInvalidArgumentException.
*/
@property (nonatomic, copy) NSString *sectionName;
/**
* The view used to view or edit the value.
*
* Will call -updateControlEvents.
*
* Attempt to set a nil view, raises a NSInvalidArgumentException.
*/
@property (nonatomic) UIView *view;
/**
* The model object that exposes the viewed or edited property.
*
* If you need to change both -keyPath and -representedObject, and you don't
* want to create a new item, you must follow these steps:
*
* - reset the represented object to nil
* - set the new key path
* - set the new represented object.
*/
@property (nonatomic, nullable) id representedObject;
/**
* The viewed or edited property whose value is updated on -changeEvents.
*
* It is evaluated against the represented object to access the value.
*
* See -value and -setValue:.
*/
@property (nonatomic, copy, nullable) NSString *keyPath;
/**
* The value bound to the represented object key path.
*
* For a nil or non-existent key path, returns CQFormItemUndefinedValue.
* For a nil representedObject, returns CQFormItemUndefinedValue.
*
* See -keyPath:.
*/
@property (nonatomic, nullable) id value;
/**
* An optional view controller to present the value content.
*
* This controller is pushed on the current navigation stack when the receiver
* is tapped. To get this behavior, a CQFormViewController must manage the form
* view presenting the receiver.
*
* The presented content is either the value elements when the value is a
* collection, or the option items set on this view controller when this
* controller is a CQFormViewController.
*
* By default, returns nil and does nothing in reaction to tap events.
*/
@property (nonatomic, nullable) UIViewController *contentViewController;
/**
* The label that describes the value role.
*
* See -value and -setValue:.
*/
@property (nonatomic, nullable) NSString *label;
/** @taskunit Converting Value between View and Model */
/**
* The formatter that converts between the value representation between the view
* and the represented object.
*
* The formatter must be set before -representedObject and -keyPath, otherwise
* updating these properties will trigger -refreshViewFromRepresentedObject
* too early (for a UITextField view, a non-string object could be set on
* UITextField.text). If you use CQFormBuilder, you can -prepareItem
*
* See -value and -setValue:.
*/
@property (nonatomic, nullable) NSFormatter *formatter;
- (nullable NSString *)stringFromObjectValue: (nullable id)aValue;
- (nullable id)objectValueFromString: (nullable NSString *)aValue;
/** @taskunit Refreshing View */
/**
* Invokes the refresh block if available.
*
* This method is automatically called on -[CQCollectionView setContent:] and
* KVO notifications posted for the observed key paths.
*
* Will also be called automatically by -setView:, -setRepresentedObject:,
* -setObservedKeyPaths: and -setRefreshBlock:.
*
* Can be overriden to implement a refresh in reaction to represented object
* changes.
*/
- (void)refreshViewFromRepresentedObject;
- (void)enableRefresh;
- (void)disableRefresh;
@property (nonatomic, readonly) BOOL canRefresh;
/**
* -representedObject can be nil when this block is evaluated, unlike -view and
* -keyPath.
*
* For subclasses such as CQFormItem, using -[CQFormItem value] or
* -[CQFormOptionItem affectedValue] to access the current state is the proper
* thing to do.
*/
@property (nonatomic, copy, nullable) CQFormItemActionBlock refreshBlock;
/** @taskunit Updating Model */
- (void)updateRepresentedObjectFromView;
@property (nonatomic, copy, nullable) CQFormItemActionBlock updateBlock;
/** @taskunit Editing */
@property (nonatomic, assign) UIControlEvents beginEditingEvents;
/**
* The control events that triggers the propagation of the change to the
* represented object.
*
* By default, returns UIControlEventValueChanged | UIControlEventEditingChanged.
*/
@property (nonatomic, assign) UIControlEvents changeEvents;
/**
* The control events that triggers -editorDidEndEditing:.
*
* By default, returns these control events based on the view type:
*
* <list>
* <item>UIControlEventEditingDidEnd or views that conform to UITextInput</item>
* <item>UIControlEventTouchUpOutside | UIControlEventTouchUpInside for any
* other views e.g. UISlider</item>
* </list>
*/
@property (nonatomic, assign) UIControlEvents endEditingEvents;
/**
* Tells the receiver the user is editing the item with the given editor.
*
* The argument is usually either a UIControl, UITextView or some custom view
* that implements -value or -text to support being refreshed.
*/
- (void)editorDidBeginEditing: (UIView *)aValueEditor;
/**
* Tells the receiver the user changed the editor value.
*
* By default, propagates the change to the represented object with -setValue:.
*
* Can be overriden, the superclass implementation must be called (don't attempt
* to update the represented object directly).
*
* The argument is usually either a UIControl, UITextView or some custom view
* that implements -value or -text to support being refreshed.
*/
- (void)editorDidChangeValue: (UIView *)aValueEditor;
/**
* Tells the receiver the user is done editing the item with the given editor.
*
* By default, reformats the editor value when a formatter is set.
*
* Can be overriden to save or discard the user changes, the superclass
* implementation must be called.
*
* The argument is usually either a UIControl, UITextView or some custom view
* that implements -value or -text to support being refreshed.
*/
- (void)editorDidEndEditing: (UIView *)aValueEditor;
- (void)endEditing;
/**
* Updates -beginEditingEvents, -changeEvents, -endEditingEvents for the current
* editor.
*
* Based on the editor type, default control events will be set.
*
* You usually call this method, right after replacing the editor directly
* without using -setView:.
*/
- (void)updateControlEvents;
/** @taskunit Selection and Highlighting */
@property (nonatomic, readonly) BOOL isHighlightable;
@property (nonatomic, readonly) BOOL isChecked;
/**
* Tells the receiver that it got selected.
*
* By default, invokes -selectBlock.
*
* Can be overriden. For example, -[CQFormOptionItem didSelect] updates the
* -[CQFormOptionItem affectedValue] when the selection changes.
*/
- (void)didSelect;
/**
* Tells the receiver that it got deselected.
*
* By default, does nothing.
*
* Can be overriden.
*/
- (void)didDeselect;
/**
* Will be called by -didSelect.
*
* The select block should usually call -[CQFormView deselectRowAtIndexPath:]
* before returning to ensure the row is deselected once the selection is
* handled.
*/
@property (nonatomic, copy, nullable) CQFormItemActionBlock selectBlock;
@end
NS_ASSUME_NONNULL_END