patternMinor
Creating view rects in loop
Viewed 0 times
creatinglooprectsview
Problem
I don't use constraints or IB to create views so it's easier to create rects this way. Does it wrong totaly?
```
@interface Client ()
@property (nonatomic, strong) UITextField *name;
@property (nonatomic, strong) UITextField *phoneNumber;
@property (nonatomic, strong) UITextField *address;
@property (nonatomic, strong) UIButton *submit;
@end
@implementation ClientAddVC
[super viewDidLoad];
[self createSubviews];
[self createViewsRects:@[_name, _phoneNumber, _address, _submit]];
}
_name = [Utility textFieldWithPadding:@"Name" frame:[Utility rectForFormFields]];
_name.delegate = self;
_phoneNumber = [Utility textFieldWithPadding:@"Phone Number" frame:[Utility rectForFormFields]];
_phoneNumber.keyboardType = UIKeyboardTypePhonePad;
_phoneNumber.delegate = self;
_address = [Utility textFieldWithPadding:@"Address" frame:[Utility rectForFormFields]];
_address.delegate = self;
_submit = [UIButton buttonWithType:UIButtonTypeCustom];
[_submit setTitle:@"Submit" forState:UIControlStateNormal];
[_submit addTarget:self action:@selector(saveClient) forControlEvents:UIControlEventTouchUpInside];
_submit.backgroundColor = [[Cache sharedCache]blackColor];
_submit.frame = [Utility rectForFormFields];
[self.view addSubview:_name];
[self.view addSubview:_phoneNumber];
[self.view addSubview:_address];
[self.view addSubview:_specialRequest];
[self.view addSubview:_averageOrderSize];
[self.view addSubview:_submit];
}
}
_currentField = textField;
}
CGFloat padding = 16;
CGFloat rowPadding = 8;
CGFloat totalHeight = padding;
for (UIView *view in views) {
CGRect viewRect = view.frame;
viewRect.origin.x = padding;
viewRect.origin.y = totalHeight;
view.frame = viewRect;
to
```
@interface Client ()
@property (nonatomic, strong) UITextField *name;
@property (nonatomic, strong) UITextField *phoneNumber;
@property (nonatomic, strong) UITextField *address;
@property (nonatomic, strong) UIButton *submit;
@end
@implementation ClientAddVC
- (void)viewDidLoad {
[super viewDidLoad];
[self createSubviews];
[self createViewsRects:@[_name, _phoneNumber, _address, _submit]];
}
- (void)createSubviews{
_name = [Utility textFieldWithPadding:@"Name" frame:[Utility rectForFormFields]];
_name.delegate = self;
_phoneNumber = [Utility textFieldWithPadding:@"Phone Number" frame:[Utility rectForFormFields]];
_phoneNumber.keyboardType = UIKeyboardTypePhonePad;
_phoneNumber.delegate = self;
_address = [Utility textFieldWithPadding:@"Address" frame:[Utility rectForFormFields]];
_address.delegate = self;
_submit = [UIButton buttonWithType:UIButtonTypeCustom];
[_submit setTitle:@"Submit" forState:UIControlStateNormal];
[_submit addTarget:self action:@selector(saveClient) forControlEvents:UIControlEventTouchUpInside];
_submit.backgroundColor = [[Cache sharedCache]blackColor];
_submit.frame = [Utility rectForFormFields];
[self.view addSubview:_name];
[self.view addSubview:_phoneNumber];
[self.view addSubview:_address];
[self.view addSubview:_specialRequest];
[self.view addSubview:_averageOrderSize];
[self.view addSubview:_submit];
}
- (void)saveClient{
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
_currentField = textField;
}
- (void)createViewsRects:(NSArray *)views{
CGFloat padding = 16;
CGFloat rowPadding = 8;
CGFloat totalHeight = padding;
for (UIView *view in views) {
CGRect viewRect = view.frame;
viewRect.origin.x = padding;
viewRect.origin.y = totalHeight;
view.frame = viewRect;
to
Solution
We should be using auto-layout. Not being as comfortable with auto-layout is not an excuse to not use it. Manually setting rects is not very good at all. Manually set rects don't play well with rotation. Manually set rects mean we probably need a special set of code for iPad versus iPhone. Manually set rects mean we probably need a special set of code for iPhone 4 vs iPhone 5 vs iPhone 6 vs iPhone 6+.
I'm not really going to touch the
The method sets up the position for four views. The spacing follows two rules:
For this, I'm going to assume that
Let's define a couple dictionaries to use with VFL.
For starters, so we can put them in a dictionary, let's use
We don't need a total height. Auto-layout will resolve this for us. Anyway, creating a dictionary is as simple as this:
Now, using a mutable string, a mutable dictionary, let's set up these constraints:
Ultimately, this is more lines of code, but this is much more flexible.
Consider what happens if we were to change the size of one of our subviews. With your code, we must be sure to call
I'm not really going to touch the
createSubviews method, which has some issues that should be address, but I do want to point out the problems with createViewRects: and how we can more easily write this code with auto-layout and using the Visual Format Language quite simply.The method sets up the position for four views. The spacing follows two rules:
- Each view has an X of 16 pixels from the left edge.
- Each view has a Y that leaves a gap of 8 pixels between the other views (plus 8 pixels between the top of the super view and the top of the top view and 8 pixels between the bottom of the super view and the bottom of the bottom view).
For this, I'm going to assume that
bgView is a scroll view. Scroll view's content doesn't really work particularly well with multiple top-level subviews. Instead, we give the bgView a single subview and add all of the subviews to that subview. This will allow auto-layout to work well.Let's define a couple dictionaries to use with VFL.
For starters, so we can put them in a dictionary, let's use
NSNumber objects rather than CGFloat for our metrics:NSNumber *padding = @16.0;
NSNumber *rowPadding = @8.0;We don't need a total height. Auto-layout will resolve this for us. Anyway, creating a dictionary is as simple as this:
NSDictionary *metrics = NSDictionaryOfVariableBindings(padding, rowPadding);Now, using a mutable string, a mutable dictionary, let's set up these constraints:
int viewIndex = 0;
NSMutableString *vertConstraintsStr = [@"V:|-[rowPadding]-" mutableCopy];
NSMutableDictionary *views = [NSMutableDictionary dictionary];
UIView *backgroundView = [[UIView alloc] init];
for (UIView *view in views) {
NSString *viewKey = [NSString stringWithFormat:@"view%i", viewIndex];
++viewIndex;
[backgroundView addSubview:view];
views[viewKey] = view;
view.translatesAutoresizingMaskIntoConstraints = NO;
[vertConstraintsStr addFormat:@"[%@]-[rowPadding]-", viewKey];
NSString *horizConstraintStr =
[NSString stringWithFormat:@"H:|-[padding]-[%@]", viewKey];
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:horizConstraintStr
options:0
metrics:metrics
views:views];
[backgroundView addConstraints:horizontalConstraints];
}
[vertConstraintsStr addString:@"[rowPadding]-|"];
NSArray *verticalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:vertConstraintStr
options:0
metrics:metrics
views:views];
[backgroundView addConstraints:verticalConstraints];
[self.bgView addSubview:backgroundView];Ultimately, this is more lines of code, but this is much more flexible.
Consider what happens if we were to change the size of one of our subviews. With your code, we must be sure to call
createViewsRect: each time any view changes sizes in order to ensure everything still looks right. With auto layout, the view will automatically adjust to the new size of one of the subviews. Plus, now you've got some experience working with auto layout so you'll be less intimidated and more comfortable with it in the future, particularly when you want to start doing more complicated layouts.Code Snippets
NSNumber *padding = @16.0;
NSNumber *rowPadding = @8.0;NSDictionary *metrics = NSDictionaryOfVariableBindings(padding, rowPadding);int viewIndex = 0;
NSMutableString *vertConstraintsStr = [@"V:|-[rowPadding]-" mutableCopy];
NSMutableDictionary *views = [NSMutableDictionary dictionary];
UIView *backgroundView = [[UIView alloc] init];
for (UIView *view in views) {
NSString *viewKey = [NSString stringWithFormat:@"view%i", viewIndex];
++viewIndex;
[backgroundView addSubview:view];
views[viewKey] = view;
view.translatesAutoresizingMaskIntoConstraints = NO;
[vertConstraintsStr addFormat:@"[%@]-[rowPadding]-", viewKey];
NSString *horizConstraintStr =
[NSString stringWithFormat:@"H:|-[padding]-[%@]", viewKey];
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:horizConstraintStr
options:0
metrics:metrics
views:views];
[backgroundView addConstraints:horizontalConstraints];
}
[vertConstraintsStr addString:@"[rowPadding]-|"];
NSArray *verticalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:vertConstraintStr
options:0
metrics:metrics
views:views];
[backgroundView addConstraints:verticalConstraints];
[self.bgView addSubview:backgroundView];Context
StackExchange Code Review Q#69020, answer score: 7
Revisions (0)
No revisions yet.