I am creating a UI component that should display a UILabel and a UISearchBar below.
However, I am not able to align them, the UISearchBar always has extra space on both left and right side (highlighted RED).
I was trying to set the dimensions of searchBarTextField by layout anchors directly, but it didn't work.
I would prefer to do it using layout anchors when possible.
SearchBar.m:
-(id) init {
self.titleLabel = [UILabel new];
self.searchBar = self.searchController.searchBar;
UIView *searchBarWrapper = [UIView new];
[self.view addSubview:self.titleLabel];
[self.view addSubview:searchBarWrapper];
[searchBarWrapper addSubview:self.searchBar];
[self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[searchBarWrapper setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.searchBar setTranslatesAutoresizingMaskIntoConstraints:NO];
//[self.titleLabel.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:1.0].active = YES;
[self.titleLabel.heightAnchor constraintEqualToConstant:14.0].active = YES;
[self.titleLabel.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[self.titleLabel.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[self.titleLabel.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
//[self.titleLabel.bottomAnchor constraintEqualToAnchor:searchBarTextField.topAnchor].active = YES;
[searchBarWrapper.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:1.0].active = YES;
//[self.searchBar.heightAnchor constraintEqualToConstant:36.0].active = YES;
[searchBarWrapper.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor].active = YES;
[searchBarWrapper.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
[searchBarWrapper.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[searchBarWrapper.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
[self.searchBar.topAnchor constraintEqualToAnchor:searchBarWrapper.topAnchor].active = YES;
[self.searchBar.bottomAnchor constraintEqualToAnchor:searchBarWrapper.bottomAnchor].active = YES;
[self.searchBar.leftAnchor constraintEqualToAnchor:searchBarWrapper.leftAnchor].active = YES;
[self.searchBar.rightAnchor constraintEqualToAnchor:searchBarWrapper.rightAnchor constant:16.0].active = YES;
return self;
}
The UISearchBarTextField is embedded in a UIView to which I don't have access to.
The constraints:
The approach by #BenOng - works fine, but breaks after tapping for the first time:
UISearchBar have this property searchFieldBackgroundPositionAdjustment:UIOffset which you can set to UIOffsetMake(-8,0) to make the left side of the text field align with the left edge of the search bar.
Create a UIView right where the full search bar should be and make the search bar it's subview. Set the right edge of the search bar beyond it's super view till the right edge aligns with the superview, it should be about 16 points extra. Make sure the superview's property clipsToBoundsis set to true, the extra 16 points of search bar background will be clipped.
Related
I've got a view with two date pickers set to cover the entire view's height and half a width each, so that they are filling the entire view.
I've then added an overlay to each picker to make the selection more visible, like this:
-(void)drawOverlays {
if (_overlay1 != nil) {
[_overlay1 removeFromSuperview];
}
if (_overlay2 != nil) {
[_overlay2 removeFromSuperview];
}
_overlay1 = [[UIView alloc] initWithFrame:CGRectMake(_startPicker.bounds.origin.x, (_startPicker.frame.size.height/2)-19, _startPicker.bounds.size.width, 38)];
_overlay1.backgroundColor = [UIColor redColor];
_overlay1.alpha = 0.5f;
[_startPicker addSubview:_overlay1];
_overlay2 = [[UIView alloc] initWithFrame:CGRectMake(_endPicker.bounds.origin.x, (_endPicker.frame.size.height/2)-19, _endPicker.bounds.size.width, 38)];
_overlay2.backgroundColor = [UIColor redColor];
_overlay2.alpha = 0.5f;
[_endPicker addSubview:_overlay2];
}
I'm calling this method from the -viewDidLayoutSubviews method and from the -viewWillTransitionToSize:withTransitionCoordinator method, and the first time the view appears everything is fine.
Then I rotate my iPad and the overlays are shown inverted, meaning that when in landscape the overlays are the size I want for the portrait and vice versa.
What's wrong with my code?
You will be much better off using constraints and letting auto-layout handle the resizing:
-(void)drawOverlays {
if (_overlay1 != nil) {
[_overlay1 removeFromSuperview];
}
if (_overlay2 != nil) {
[_overlay2 removeFromSuperview];
}
//_overlay1 = [[UIView alloc] initWithFrame:CGRectMake(_startPicker.bounds.origin.x, (_startPicker.frame.size.height/2)-19, _startPicker.bounds.size.width, 38)];
// instantiate overlay1
_overlay1 = [UIView new];
_overlay1.backgroundColor = [UIColor redColor];
_overlay1.alpha = 0.5f;
// add as subview of startPicker
[_startPicker addSubview:_overlay1];
//_overlay2 = [[UIView alloc] initWithFrame:CGRectMake(_endPicker.bounds.origin.x, (_endPicker.frame.size.height/2)-19, _endPicker.bounds.size.width, 38)];
// instantiate overlay2
_overlay2 = [UIView new];
_overlay2.backgroundColor = [UIColor redColor];
_overlay2.alpha = 0.5f;
// add as subview of endPicker
[_endPicker addSubview:_overlay2];
// we want to use auto-layout / constraints
_overlay1.translatesAutoresizingMaskIntoConstraints = NO;
_overlay2.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:#[
// constrain overlay1 to startPicker
// centerY
// leading / trailing = 0
// height = 38
[_overlay1.centerYAnchor constraintEqualToAnchor:_startPicker.centerYAnchor],
[_overlay1.leadingAnchor constraintEqualToAnchor:_startPicker.leadingAnchor constant:0.0],
[_overlay1.trailingAnchor constraintEqualToAnchor:_startPicker.trailingAnchor constant:0.0],
[_overlay1.heightAnchor constraintEqualToConstant:38.0],
// constrain overlay2 to startPicker
// centerY
// leading / trailing = 0
// height = 38
[_overlay2.centerYAnchor constraintEqualToAnchor:_endPicker.centerYAnchor],
[_overlay2.leadingAnchor constraintEqualToAnchor:_endPicker.leadingAnchor constant:0.0],
[_overlay2.trailingAnchor constraintEqualToAnchor:_endPicker.trailingAnchor constant:0.0],
[_overlay2.heightAnchor constraintEqualToConstant:38.0],
]
];
}
And, this only needs to be called from viewDidLoad (or wherever else you may find it appropriate). There is no need for it to be -- and in fact, it should not be -- called from viewDidLayoutSubviews or viewWillTransitionToSize.
As a side note -- if you are using remove and re-add to show and hide them, you'll also get a little better optimization if you add them once, and then set the .hidden property to YES or NO.
I have a view controller animating in from the left edge of screen, and this view controller has a UITableView subview.
On every device and orientation except for one (iPhone X in landscape), this table view shows properly.
On the iPhone X in landscape, the table view is in the correct place but its cells do not populate until you scroll the tableView. Once you scroll, the table view is as if it had been there the whole time but with invisible cells.
This constraint causes the cells to not load:
[tableView.leftAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leftAnchor].active = YES;
And when replacing this constraint with the following, the problem is solved as well:
[tableView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
I've made the background of the UITableView red to show that it is correctly placed, but no rows are showing even though they load in the portrait orientation.
I've also tried leaving it as:
[tableView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
but doing:
[tableView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
which solves the problem for header cells:
but not content cells:
This code causes the same issue:
[tableView setInsetsContentViewsToSafeArea:YES];
I am setting up my regular cells like this :
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if([reuseIdentifier isEqual: #"LayerCell"]) {
_titleLabel = [[UILabel alloc] init];
_imgView = [[UIImageView alloc] init];
_toggleView = [[UIImageView alloc] init];
[_imgView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:_imgView];
[_imgView.heightAnchor constraintEqualToAnchor:self.heightAnchor multiplier:0.8].active = YES;
[_imgView.widthAnchor constraintEqualToAnchor:self.heightAnchor multiplier:0.8].active = YES;
[_imgView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;
[_imgView.leftAnchor constraintEqualToAnchor:self.leftAnchor constant:8].active = YES;
[_imgView setContentMode:UIViewContentModeScaleAspectFit];
[self addSubview:_toggleView];
[_toggleView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_toggleView.heightAnchor constraintEqualToAnchor:_imgView.heightAnchor].active = YES;
[_toggleView.widthAnchor constraintEqualToAnchor:_imgView.widthAnchor].active = YES;
[_toggleView.centerYAnchor constraintEqualToAnchor:_imgView.centerYAnchor].active = YES;
[_toggleView.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:-10].active = YES;
[_toggleView setContentMode:UIViewContentModeScaleAspectFit];
[self addSubview:_titleLabel];
[_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_titleLabel.heightAnchor constraintEqualToConstant:20].active = YES;
[_titleLabel.centerYAnchor constraintEqualToAnchor:_imgView.centerYAnchor].active = YES;
[_titleLabel.rightAnchor constraintEqualToAnchor:_toggleView.leftAnchor constant:-10].active = YES;
[_titleLabel.leftAnchor constraintEqualToAnchor:_imgView.rightAnchor constant:10].active = YES;
[self layoutIfNeeded];
}
return self;
}
In my app I'm trying to add search functionality. I have table view controller and custom top bar view where I show UISearchBar. The problem is that overlay view is always a bit under the top bar and it adds gap between them:
In my table view controller .m file's viewDidLoad:
[super viewDidLoad];
LSDropdownViewController *menuCtrl = (LSDropdownViewController *)[self parentViewController];
menuCtrl.topSearchBar.delegate = self;
[menuCtrl.topSearchBar setBackgroundColor:[UIColor clearColor]];
[menuCtrl.topSearchBar setBackgroundImage:[UIImage imageWithGradientColors]];
[menuCtrl.topSearchBar setTintColor:[UIColor whiteColor]];
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:menuCtrl.topSearchBar contentsController:self];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
any ideas?
It's due to iOS7+. So, to get rid of it, you have to set search bar frame manually by offsetting y-value 20 pixels i.e. searchBar.frame = CGRectMake(0,20,width,height).
I am adding a UISearchBar to a UINavigation Bar as so (this is a storyboard based app) :
//Setup the search bar.
searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0.0f, 0.0f, 50.0f, 44.0f)];
self.tabBarController.navigationItem.titleView = searchBar;
searchBar.delegate = self;
controller = [[UISearchDisplayController alloc]initWithSearchBar:self.searchBar contentsController:self];
controller.searchResultsDataSource = self;
controller.searchResultsDelegate = self;
No matter what I set the width and height and position of the search bar to be, it will not change the height / width of the search bar. It is always the same width and height.
Can anyone suggest how to alter ?
I've experienced the same problem one time, and what I did was adding the searchBar into a container View (i've had problems showing the cancel button) and add the container view as a left bar item
- (UIBarButtonItem *)createSearchBarHolder {
UIView *searchBarContainer = [[UIView alloc] initWithFrame:navigationSearchBar.frame];
searchBarContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[searchBarContainer addSubview:navigationSearchBar];
navigationSearchBar.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
UIBarButtonItem *searchBarHolder = [[UIBarButtonItem alloc] initWithCustomView:searchBarContainer];
return searchBarHolder;
}
And in viewDidLoad:
NSArray *leftBarButtons = [[NSArray alloc] initWithObjects:[self createSearchBarHolder], nil];
self.navigationItem.leftBarButtonItems = leftBarButtons;
It's not clear what size you want your search bar to be. If you make it the navigation item's titleView, it will adjust for any buttons on the navigation bar, and adjust on rotation. Is that what you want?
- (void)viewDidLoad {
[super viewDidLoad];
UISearchBar *sb = [[UISearchBar alloc] init];
self.navigationItem.titleView = sb;
}
I've been searching around on how to nest UIScrollViews.
It seems like it should be as easy as adding the inner scroll views by using addSubview: of the container scroll view. I have everything showing up visually correct but the functionality of the inner scroll views is non existent despite supplying them with proper frames and content sizes.
My code below shows what I have so far. Only the outer scroll view scrolls. I want it so that the outer scroll view controls left to right scrolling and each inner scroll view controls vertical scrolling of their related page content.
self.containerScroll = [[UIScrollView alloc]init];
self.containerScroll.frame = CGRectMake(0,(self.headerView.frame.size.height + self.pageControl.frame.size.height),screenWidth, (screenHeight - (self.headerView.frame.size.height + self.pageControl.frame.size.height)));
self.containerScroll.backgroundColor = [UIColor clearColor];
self.containerScroll.alpha = 1;
self.containerScroll.pagingEnabled = YES;
self.containerScroll.contentSize = CGSizeMake(self.containerScroll.bounds.size.width*3,1);
self.containerScroll.bounces = NO;
self.containerScroll.delegate = self;
[self.view addSubview:self.containerScroll];
self.page1Scroll = [[UIScrollView alloc]init];
self.page1Scroll.frame = CGRectMake(0,0,self.containerScroll.bounds.size.width,self.containerScroll.bounds.size.height);
self.page1Scroll.backgroundColor = [UIColor redColor];
self.page1Scroll.alpha = 1;
[self.page1Scroll addSubview:self.feedPageVC.view];
self.page1Scroll.contentSize = CGSizeMake(320,500);
self.page1Scroll.delegate = self;
[self.containerScroll addSubview:self.page1Scroll];
self.page2Scroll = [[UIScrollView alloc]init];
self.page2Scroll.frame = CGRectMake(self.containerScroll.bounds.size.width,0,self.containerScroll.bounds.size.width,self.containerScroll.bounds.size.height);
self.page2Scroll.backgroundColor = [UIColor greenColor];
self.page2Scroll.delegate = self;
[self.containerScroll addSubview:self.page2Scroll];
self.page3Scroll = [[UIScrollView alloc]init];
self.page3Scroll.frame = CGRectMake(self.containerScroll.bounds.size.width*2,0,self.containerScroll.bounds.size.width,self.containerScroll.bounds.size.height);
self.page3Scroll.backgroundColor = [UIColor blueColor];
[self.page3Scroll addSubview:self.detailsPageVC.view];
self.page3Scroll.contentSize = CGSizeMake(320,500);
self.page3Scroll.delegate = self;
[self.containerScroll addSubview:self.page3Scroll];
self.containerScroll.contentSize = CGSizeMake(self.containerScroll.bounds.size.width*3,1);
Looks like the height of your content is set to 1. If a child is larger than its parent, it won't function correctly (works the same for buttons).
Also, make sure you know which scrollview you're handling in the delegate methods. All the scrollviews will be handled in the same method and it could cause you problems.