I have a collection view wrap by UIView, the collection view has same bounds with wrapper view. And collection view data source will be changed, so the wrapper view bounds also be changed. I have implement the wrapper view intrinsicContentSize method with collectionView.collectionViewLayout.collectionViewContentSize, and the collection view layout is subclass of UICollectionViewFlowLayout, the cell of the UICollectionView has also implement method intrinsicContentSize, but the wrapper view did not update frame with collection view content size. collectionViewContentSize is always CGRectZero.
I have tried
[self.layout invalidateLayout];
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
but did not work,
What should I do to update the wrapper view frame with collectionView.collectionViewLayout.collectionViewContentSize.
There is some code below:
ZXCollectionViewWrapperView *wrapperView = [[ZXCollectionViewWrapperView alloc] init];
[self.view addSubview:wrapperView];
[wrapperView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
}];
self.wrapperView = wrapperView;
/// wrapper view size
- (CGSize)intrinsicContentSize {
return self.collectionView.collectionViewLayout.collectionViewContentSize;
}
ZXCollectionViewAlignedLayout *layout = [[ZXCollectionViewAlignedLayout alloc] init];
self.layout = layout;
ZXCollectionView *collectionView = [[ZXCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.dataSource = self;
collectionView.delegate = self;
collectionView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:collectionView];
[collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
self.collectionView = collectionView;
[self.collectionView zx_registerCellClass:[ZXCommendCell class]];
self.dataSource = dataSource;
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
As you are using autolayout, you should create a height constraint for wrapper view. Then you can change the constant of that constraint to equal collection view content size, like this:
heightConstraint = make.height.equalTo(200) // initial height
then you can use KVO to observe collection view content size to change and set
heightConstraint.constant = collectionView.contentSize.height
you can refer to this for observing contentSize: observing contentSize (CGSize) with KVO in swift
Related
I created a customView (UIView) which contains a UIScrollView as its subview, and
objects I put on the scrollView did not show. To test it, I added a UIView to the scrollView and it didn't show neither(see code example below). Since the content I want to put on the scrollView is dynamic, I want to approach the problem by just using AutoLayout. I tried different ways and followed instructions online, none of it worked, all the similar questions were UIScrollView created inside of a UIViewController.
Here is my code, I'm using Masonry:
#interface CustomView : UIView
#end
#implementation CustomView
- (instancetype)init {
if (self = [super init]) {
self.scrollView = [[UIScrollView alloc]init];
self.scrollView.backgroundColor = [UIColor redColor];
[self addSubview:self.scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
//Here I add someView to the scrollView, and when I create an instance of the scrollView, the someView does not show.
self.someView = [[UIView alloc]init];
self.someView.backgroundColor = [UIColor blackColor];
[self.scrollView addSubview:self.someView];
[self.someView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.size.equalTo(CGSizeMake(40, 40));
}];
}
return self;
}
Here is how I create an instance of the CustomView
CustomView *customView = [[CustomView alloc]init];
[self.view addSubview: customView];
[customView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(64.0);
make.height.equalTo(40.0);
make.leading.trailing.equalTo(0);
}];
Is there anything I missed or did wrong that caused someView to not show?
Thank your for your help.
I solved the problem by: 1).Using scrollView directly instead of as a subView in a UIView (my customView). 2) make sure to add the right and bottom constraints of the scrollView.
I change the height of a UICollectionViewCell if a user touches it. To achieve that I use performBatchUpdates: to change the underlying data and use the standard cell animation. This works perfectly and the change gets animated with a standard grow and shrink animation.
Additionally I use reloadSection to update all subviews of the cells in that section. Here is my code:
[collectionView performBatchUpdates:^{
dataHelper.isCellExpanded = !dataHelper.isCellExpanded;
} completion:^(BOOL finished) {
[collectionView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]];
}];
I need to achieve a flexible reordering of the subviews for the expanded cell state. So I don't use Auto Layout. I only change the subviews frame and would like to animate the frame change along with the UICollectionViewCell animation.
The Problem is that the subviews inside the contentView of the cells get updated without animation and after the height change animation of the cell is finished. But I would like to animate the cell subviews along with the animation of the height change. How would one implement that?
UPDATE
A custom animation method in my UICollectionViewCell looks like this:
- (void)animate {
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^
{
CGRect labelFrame = self.testLabel.frame;
labelFrame.origin.y += 70;
self.testLabel.frame = labelFrame;
}
completion:nil];
}
I create the testLabel in the initWithFrame: of my UICollectionViewCell subclass like so:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
CGRect cellFrame = CGRectMake(0, 0, frame.size.width, frame.size.height);
// content
_testLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, frame.size.height - 20, frame.size.width - 20, 20)];
_testLabel.backgroundColor = [UIColor redColor];
_testLabel.textColor = [UIColor blackColor];
_testLabel.text = #"test";
[self.contentView addSubview:_testLabel];
....
You have to implement an animation method inside the collection view cell and you need to that call from the performBatchUpdates method.
[collectionView performBatchUpdates:^{
dataHelper.isCellExpanded = !dataHelper.isCellExpanded;
// Access the cells you want to animate
yourCustomCell *cell = (yourCustomCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell animate];
} completion:^(BOOL finished) {
[collectionView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]];
}];
Inside the cell animate method you can implement all the animations you might want.
I was able to solve this by just adding springs and struts to the contentView and to the testLabel in my UICollectionViewCell subclass in the form of autoresizingMask
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
CGRect cellFrame = CGRectMake(0, 0, frame.size.width, frame.size.height);
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
// content
_testLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, frame.size.height - 20, frame.size.width - 20, 20)];
_testLabel.backgroundColor = [UIColor redColor];
_testLabel.textColor = [UIColor blackColor];
_testLabel.text = #"You rock";
_testLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
[self.contentView addSubview:_testLabel];
Side note:
I was playing around with AutoLayout to fix this issue but was never able to get it to work.
I've got a XIB named 'ImageViewController.xib" which contains a collection view. I've set up my desired collection view layout on this collection view inside the xib.
When trying to run my app I get the error message:
'UICollectionView must be initialized with a non-nil layout parameter'
I'm using this inside ImageViewController:
-(id) initWithImages:(NSArray *)imagesUrls
{
if (self = [super initWithCollectionViewLayout:self.collectionViewLayout]) {
self.imageUrls = [[NSArray alloc] initWithArray:imagesUrls];
}
[self.collectionView registerNib:[UINib nibWithNibName:#"ImageCell" bundle:nil] forCellWithReuseIdentifier:#"Cell"];
return self;
}
I just want to use the layout I have set up in the XIB and not make one programatically.
You can add the collectionViewLayout to the collectionView in the viewDidLoad.
Create your collectionViewLayout eg:
UICollectionViewFlowLayout * flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.minimumLineSpacing = 5;
flowLayout.minimumInteritemSpacing = 5;
flowLayout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
Then we set it as the flow layout of the collectionView
_collectionView.collectionViewLayout = flowLayout;
I found this answer on a good tutorial which can be found here
Hopefully this helps
Try adding this in your view did Load method act its what I did to get rid of it
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.itemSize = CGSizeMake(100, 100);
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"MyCell"];
this worked for me…you see what the error is telling you that the layout should not be nil, the Layout's frame is set relative to its superview, the layout is passed to the parent class during initialization, check if your CollectionViewLayout is "Null"
All of my delegate are hooked up and called for a vertical layout object. In horizontal mode, for some reason the only delegate called is numberofcellsincollectionview (which returns correct number) - cellforitematindexpath isn't called unless I increase the size of the collectionview frame. when it is large enough cellforitematindexpath is called. This strange behavior only happens when the collectionview layout is in horizontal mode. In vertical mode not getting this behavior. (also this strange behavior only started in iOS7 - don't know if there is a connection)
horizontal layout code
-(UICollectionViewFlowLayout *)getFilmStripLayout
{
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setSectionInset:UIEdgeInsetsMake(0, 0, 0, 0)];
CGSize s = CGSizeMake(self.frame.size.height,self.frame.size.height);
[flowLayout setItemSize:s];
return flowLayout;
}
View controller containing view
-(UIThumbnailGalleryView *)gallery
{
if (_gallery == nil)
{
CGRect r = CGRectInset(self.filmStripRect, 0, -20); // started with inset 0,0 , increased the size and then the delegates are called.
_gallery = [[UIThumbnailGalleryView alloc] init2WithFrame:r]; // custom initializer testing layouts
[_gallery setBackgroundColor:[UIColor clearColor]];
[_gallery setBackgroundColor:[UIColor purpleColor]]; // setting colors in order to see the placement
_gallery.layer.borderColor = [UIColor orangeColor].CGColor;
_gallery.layer.borderWidth = 3;
_gallery.dataSource = self;
_gallery.delegate = self;
[_gallery setInToggleMode:YES];
}
return _gallery;
}
I have a couple of side-by-side UITableViews in a UIView, and I would like to get the whole thing to autoresize. I have a UIView In my init() method am doing:
// I don't know how big frontBack should be, so I'd like it to autosize
UIView *frontBack = [[UIView alloc] initWithFrame:CGRectZero];
frontBack.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UITableView *table = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, r.size.width / 2, height) style:UITableViewStyleGrouped];
table.autoresizingMask = UIViewAutoresizingFlexibleHeight;
table.dataSource = model1;
[table reloadData];
[frontBack addSubview:table];
... (add second table similarly, except with model2)
...
controller.view = frontBack;
This does not work. The tables are 'height' pixels tall (which is smaller than what they need; the text is cut off).
I've tried several ways of getting the UITableViews to resize, with no luck
// contentSize is correct, but frame size does not change
[table reloadData];
table.frame.size.height = table.contentSize.height;
// contentSize is correct, but bounds does not change
[table reloadData];
table.bounds.size.height = table.contentSize.height;
// Nothing appears to change
[table setNeedsLayout];
[table layoutIfNeeded];
// Again, no change
[table sizeToFit];
I assume I am missing something basic here, but I'd be grateful if someone could point out what it is.
table.bounds.size.height = table.contentSize.height;
This is a read-only property, you need to set the frame again.
Also, Are you sure your containing UIView isn't cutting off the table content? You should resize it as well.