I'm trying to implement a custom table cell using Auto Layout programmatically like below, but for some reason I didn't get the expected result.
Expected:
Actual:
My observation is:
The cell height doesn't grow as the content grows, and content overflows;
The bar element should be a vertical blue bar, but it's not properly showing up;
Setting background colors on the UIView elements doesn't work at all for some reason.
Please share some pointers on what I did wrong. Thanks in advance
UITableViewCell Code is below:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.infoContainer = [[UIView alloc] init];
self.title = [[UILabel alloc] init];
self.time = [[UILabel alloc] init];
self.bar = [[UIView alloc] init];
[self.infoContainer addSubview:self.bar];
[self.infoContainer addSubview:self.title];
[self.infoContainer addSubview:self.time];
[self.contentView addSubview:self.infoContainer];
self.infoContainer.translatesAutoresizingMaskIntoConstraints = NO;
self.title.translatesAutoresizingMaskIntoConstraints = NO;
self.time.translatesAutoresizingMaskIntoConstraints = NO;
self.bar.translatesAutoresizingMaskIntoConstraints = NO;
[self.infoContainer.leftAnchor constraintEqualToAnchor:self.contentView.leftAnchor constant:18].active = YES;
[self.infoContainer.rightAnchor constraintEqualToAnchor:self.contentView.rightAnchor constant:-18].active = YES;
[self.infoContainer.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10].active = YES;
self.infoContainer.backgroundColor = [UIColor yellowColor];
[self.bar.leftAnchor constraintEqualToAnchor:self.infoContainer.leftAnchor constant:0].active = YES;
[self.bar.topAnchor constraintEqualToAnchor:self.infoContainer.topAnchor constant:0].active = YES;
[self.bar.bottomAnchor constraintEqualToAnchor:self.infoContainer.bottomAnchor constant:0].active = YES;
[self.bar.heightAnchor constraintEqualToAnchor:self.infoContainer.heightAnchor].active = YES;
[self.bar.widthAnchor constraintEqualToConstant:10];
self.bar.backgroundColor = [UIColor blueColor];
[self.title.leftAnchor constraintEqualToAnchor:self.bar.rightAnchor constant:15].active = YES;
[self.title.rightAnchor constraintEqualToAnchor:self.infoContainer.rightAnchor constant:0].active = YES;
[self.title.topAnchor constraintEqualToAnchor:self.infoContainer.topAnchor constant:0].active = YES;
[self.time.leftAnchor constraintEqualToAnchor:self.title.leftAnchor constant:0].active = YES;
[self.time.rightAnchor constraintEqualToAnchor:self.title.rightAnchor constant:0].active = YES;
[self.time.topAnchor constraintEqualToAnchor:self.title.bottomAnchor constant:10].active = YES;
}
return self;
}
and in the table view, I have:
self.recentView.rowHeight = UITableViewAutomaticDimension;
self.recentView.estimatedRowHeight = 64.0f;
Thanks!
Automatic height completely depends on hooking constraints properly from top to bottom , so you miss 2 constraints
1-
[self.infoContainer.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:10].active = YES;
2-
[self.time.bottomAnchor constraintEqualToAnchor:self.infoContainer.bottomAnchor constant:10].active = YES;
Tip : If you set top & bottom constraints here
[self.bar.topAnchor constraintEqualToAnchor:self.infoContainer.topAnchor constant:0].active = YES;
[self.bar.bottomAnchor constraintEqualToAnchor:self.infoContainer.bottomAnchor constant:0].active = YES;
Then no need for
[self.bar.heightAnchor constraintEqualToAnchor:self.infoContainer.heightAnchor].active = YES;
Related
I'm using a UICollectionViewCompositionalLayout to create a grid. Each cell contains a multi-line label. My goal is that, when 1 item in a group/row grows, all others in that group/row grow to the same height e.g. in the attached pic, I would like the cells in column 2 and 3, row 2, to match the height of the first cell that contains ("1000\ndetail").
I have tried various things like setting the height of the items to fractional(1) but that then prevented multi line. Other attempts such as changing the dimensions of the group did not help either. How can I set up my UICollectionViewCompositionalLayout to achieve my goal please?
Collection View:
NSCollectionLayoutSize *itemSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:0.333] heightDimension:[NSCollectionLayoutDimension fractionalHeightDimension:1]];
NSCollectionLayoutItem *item = [NSCollectionLayoutItem itemWithLayoutSize:itemSize];
NSCollectionLayoutSize *groupSize = [NSCollectionLayoutSize sizeWithWidthDimension:[NSCollectionLayoutDimension fractionalWidthDimension:1] heightDimension:[NSCollectionLayoutDimension estimatedDimension:40]];
NSCollectionLayoutGroup *group = [NSCollectionLayoutGroup horizontalGroupWithLayoutSize:groupSize subitem:item count:3];
NSCollectionLayoutSection *section = [NSCollectionLayoutSection sectionWithGroup:group];
UICollectionViewCompositionalLayout *compositionalLayout = [[UICollectionViewCompositionalLayout alloc] initWithSection:section];
UICollectionView *cv = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:compositionalLayout];
cv.translatesAutoresizingMaskIntoConstraints = NO;
cv.dataSource = self;
cv.delegate = self;
[self.view addSubview:cv];
[cv registerClass:[GridCVCell class] forCellWithReuseIdentifier:#"Cell"];
[cv.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:100].active = YES;
[cv.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:0].active = YES;
[cv.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:0].active = YES;
[cv.heightAnchor constraintEqualToConstant:300].active = YES;
CollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if ([super initWithFrame:frame]) {
UILabel *titleL = [UIlabel new];
titleL.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:titleL];
[titleL.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:10].active = YES;
[titleL.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-10].active = YES;
[titleL.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor constant:-2].active = YES;
[self.contentView.heightAnchor constraintEqualToAnchor:titleL.heightAnchor constant:10].active = YES;
}
return self;
}
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;
}
question one :
when a SearchController.searchBar as a tableview.tableHeaderView, and tableview have backgroundColor
why it has a strange color: image
question two :
How to use a SearchController.searchBar as a tableview.sectionHeader
when i try do it, my tableView will be so crazy image
code snippet :
- (UISearchController *)searchController{
if (!_searchController) {
_searchController = [[UISearchController alloc] initWithSearchResultsController: self.resultVC];
_searchController.searchResultsUpdater = self;
_searchController.delegate = self;
self.definesPresentationContext = YES;
// 是否添加半透明覆盖
_searchController.dimsBackgroundDuringPresentation = YES;
// 是否隐藏导航栏
_searchController.hidesNavigationBarDuringPresentation = YES;
// 可以通过此种方式修改searchBar的背景颜色
_searchController.searchBar.barTintColor = GL_NAVBAR_COLOR;
UIImageView *barImageView = [[[_searchController.searchBar.subviews firstObject] subviews] firstObject];
barImageView.layer.borderColor = GL_NAVBAR_COLOR.CGColor;
barImageView.layer.borderWidth = 1;
// 可以通过此种方式可以拿到搜索框,修改搜索框的样式
UITextField *searchField = [[[_searchController.searchBar.subviews firstObject] subviews] lastObject];
searchField.backgroundColor = [UIColor yellowColor];
searchField.placeholder = #"请输入搜索内容";
}
return _searchController;
}
- (void)configureUI{
self.tableView = [[UITableView alloc] initWithFrame: self.view.bounds style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview: self.tableView];
self.tableView.backgroundColor = backgroundColor;
self.searchController.searchBar.frame = CGRectMake(0, 0, ScreenWidth, 44);
#if tableHeaderViewSearchBar
self.tableView.tableHeaderView = self.searchController.searchBar;
#endif
}
#if !tableHeaderViewSearchBar
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 50;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return self.searchController.searchBar;
}
#endif
You can place the UISearchBar of UISearchController in the navigation bar so that it remains fixed at the top.
So, just add below code at the end of configureUI method and remove last 2 lines of code inside the same method.
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
self.navigationItem.titleView = self.searchController.searchBar;
self.definesPresentationContext = YES;
I have a provisional idea:
- (void)viewDidLoad {
[super viewDidLoad];
[self.searchController.searchBar sizeToFit];
self.tableView.frame.origin.y = CGRectGetMaxY(self.searchController.searchBar.frame);
[self.view addSubview: self.searchController.searchBar];
}
To answer question one: you need to set tableView.backgroundView
UIView *tableBackgroundView = [[UIView alloc]initWithFrame:self.tableView.bounds];
tableBackgroundView.backgroundColor = GL_BACKGROUD_COLOR;
self.tableView.backgroundView = tableBackgroundView;
I'm having difficulty laying out my custom cell. I have an image view and two labels. A titleLabel and overview label.
My problem is the overviewLabel is extending outside of the cell and is inconsistent. I think this throws off the rest of the labels.
My goal is to fit the labels better to the cell and improve the layout.
My constraints are as follows.
Custom Cell Class
self.photoImageView.translatesAutoresizingMaskIntoConstraints = false;
[self.photoImageView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor].active = YES;
[self.photoImageView.widthAnchor constraintEqualToAnchor:self.photoImageView.heightAnchor].active = YES;
[self.photoImageView.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
[self.photoImageView.bottomAnchor constraintLessThanOrEqualToAnchor:margins.bottomAnchor constant:0].active = YES;
self.photoImageView.contentMode = UIViewContentModeScaleAspectFit;
self.photoImageView.layer.cornerRadius = 17;
self.photoImageView.layer.masksToBounds = YES;
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false;
[self.titleLabel.leadingAnchor constraintEqualToAnchor:self.photoImageView.trailingAnchor].active = YES;
[self.titleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:margins.trailingAnchor].active = YES;
[self.titleLabel.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
self.titleLabel.font = [UIFont fontWithName:#"Avenir-Book" size:13];
self.titleLabel.textAlignment = NSTextAlignmentLeft;
self.overviewLabel.translatesAutoresizingMaskIntoConstraints = false;
[self.overviewLabel.leadingAnchor constraintEqualToAnchor:self.photoImageView.trailingAnchor].active = YES;
[self.overviewLabel.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
[self.overviewLabel.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:5].active = YES;
[self.overviewLabel.bottomAnchor constraintGreaterThanOrEqualToAnchor:margins.bottomAnchor constant:0].active = YES;
[self.overviewLabel sizeToFit];
In my viewdidLoad for mytableview I have the following
self.moviesTableView.estimatedRowHeight = 100;
self.moviesTableView.rowHeight = UITableViewAutomaticDimension;
I'm writing a PDF reader using vfr-reader library. To display two pages in landscape i'm rendering each page into its own view, then add these two views to a container view, then add container view to a scroll view. Each view's autoresizingMask is set to UIViewAutoresizingNone, contentMode is UIViewContentModeRedraw, autoresizingSubviews is set to 'NO' for all views.
But still somehow container view is autoresized to fit scroll view's height, and I don't know where is this happening. I care about this because, when autoresizing container view, it's width becomes larger than screen width, and I cannot scroll to the next two pages with a single swipe (two swipes are needed), which sucks. What am I missing?
EDIT gonna add some come if it helps. In the ViewController I create a Scroll View with following options:
theScrollView = [[ReaderScrollView alloc] initWithFrame:viewRect];
theScrollView.scrollsToTop = NO;
theScrollView.pagingEnabled = YES;
theScrollView.delaysContentTouches = NO;
theScrollView.showsVerticalScrollIndicator = NO;
theScrollView.showsHorizontalScrollIndicator = NO;
theScrollView.contentMode = UIViewContentModeRedraw;
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theScrollView.backgroundColor = [UIColor clearColor];
theScrollView.userInteractionEnabled = YES;
theScrollView.autoresizesSubviews = NO;
theScrollView.delegate = self;
[self.view addSubview:theScrollView];
When I'm drawing pages I'm adding an UIView to the Scroll View, which is being initiated this way:
if ((self = [super initWithFrame:frame]))
{
self.autoresizesSubviews = YES;
self.userInteractionEnabled = YES;
self.contentMode = UIViewContentModeRedraw;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.backgroundColor = [UIColor clearColor];
theScrollView = [[ReaderScrollView alloc] initWithFrame:self.bounds]; // Not sure about this part - why is the 2nd scroll view added?
// this is the way its done in vfr reader
theScrollView.scrollsToTop = NO;
theScrollView.delaysContentTouches = NO;
theScrollView.showsVerticalScrollIndicator = NO;
theScrollView.showsHorizontalScrollIndicator = NO;
theScrollView.contentMode = UIViewContentModeRedraw;
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theScrollView.backgroundColor = [UIColor clearColor];
theScrollView.userInteractionEnabled = YES;
theScrollView.autoresizesSubviews = NO;
theScrollView.bouncesZoom = YES;
theScrollView.delegate = self;
theContentView = [[ReaderContentPage alloc] initWithURL:fileURL page:page password:phrase landscape:(BOOL)isLandscape position:FALSE];
CGRect viewRect = CGRectZero;
viewRect.size.width = theContentView.bounds.size.width;
viewRect.size.height = theContentView.bounds.size.height;
if( isLandscape){
NSLog(#"Landscape detected in content view");
if (theContentView == NULL)
{
theContentView = [[ReaderContentPage alloc] initWithURL:fileURL page:(page+1) password:phrase landscape:(BOOL)isLandscape position:FALSE];
theContentView2 = NULL;
viewRect.size.width = theContentView.bounds.size.width;
viewRect.size.height = theContentView.bounds.size.height;
} else {
if (page == 1)
theContentView2 = NULL;
else
theContentView2 = [[ReaderContentPage alloc] initWithURL:fileURL page:(page+1) password:phrase landscape:(BOOL)isLandscape position:TRUE];
if (theContentView2 != NULL)
viewRect.size.width = theContentView.bounds.size.width*2;
}
}
if (theContentView != nil) // Must have a valid and initialized content view
{
theContainerView = [[UIView alloc] initWithFrame:viewRect];
theContainerView.autoresizesSubviews = NO;
theContainerView.userInteractionEnabled = NO;
theContainerView.contentMode = UIViewContentModeRedraw;
theContainerView.autoresizingMask = UIViewAutoresizingNone;
theContainerView.backgroundColor = [UIColor whiteColor];
theScrollView.contentSize = theContentView.bounds.size; // Content size same as view size
theScrollView.contentOffset = CGPointMake((0.0f - CONTENT_INSET), (0.0f - CONTENT_INSET));
theScrollView.contentInset = UIEdgeInsetsMake(CONTENT_INSET, CONTENT_INSET, CONTENT_INSET, CONTENT_INSET);
theThumbView = [[ReaderContentThumb alloc] initWithFrame:theContentView.bounds]; // Page thumb view
[theContainerView addSubview:theThumbView]; // Add the thumb view to the container view
[theContainerView addSubview:theContentView]; // Add the content view to the container view
if(( isLandscape) && (theContentView2 != NULL)){
[theContainerView addSubview:theContentView2]; // Add the content view to the container view
}
[theScrollView addSubview:theContainerView]; // Add the container view to the scroll view
[self updateMinimumMaximumZoom]; // Update the minimum and maximum zoom scales
theScrollView.zoomScale = theScrollView.minimumZoomScale; // Zoom to fit
}
[self addSubview:theScrollView]; // Add the scroll view to the parent container view
[theScrollView addObserver:self forKeyPath:#"frame" options:NSKeyValueObservingOptionNew context:NULL];
self.tag = page; // Tag the view with the page number
}
return self;
And ReaderContentPage is created this way:
if ((self = [super initWithFrame:frame]))
{
self.autoresizesSubviews = NO;
self.userInteractionEnabled = NO;
self.clearsContextBeforeDrawing = NO;
self.contentMode = UIViewContentModeRedraw;
self.autoresizingMask = UIViewAutoresizingNone;
self.backgroundColor = [UIColor clearColor];
view = self; // Return self
}
In the function updateMinimumMaximumZoom in the class ReaderContentView:
The calculation of the zoomscale to fit the screen is done with single view, but in landscape it should be calculated form theContainerView.
Try replacing this code
CGFloat zoomScale = ZoomScaleThatFits(targetRect.size, theContentView.bounds.size);
with
CGFloat zoomScale = ZoomScaleThatFits(targetRect.size, theContainerView.bounds.size);
There is a contentMode property in UIScrollView, change it to UIViewContentModeCenter or something else to prevent resizing.