Autolayout with dynamic height - ios

I am trying to create a subclass of UIView that will show a list of fixed sized UIImages similar to how a UILabel displays letters.If all the images won't fit on one line, the images are arranged on multiple lines.
How can I achieve this using autolayouts so that if I put this view in a UIStackView the images will be listed correctly?
Here is a sample if I did it using fixed position :
- (void) layoutSubviews{
[super layoutSubviews];
CGRect bounds = self.bounds;
CGRect imageRect = CGRectMake(0, 0, 30, 30);
for (UIImageView* imageView in self.imageViews){
imageView.frame = imageRect;
imageRect.origin.x = CGRectGetMaxX(imageRect);
if (CGRectGetMaxX(imageRect) > CGRectGetMaxX(bounds)){
imageRect.origin.x = 0.0;
imageRect.origin.y = CGRectGetMaxY(imageRect);
}
}
}
Update:
Here is a sample project to show the issue.
https://github.com/datinc/DATDemoImageListView
Here the link for ImageListView
https://github.com/datinc/DATDemoImageListView/blob/master/DATDemoImageListView/DATDemoImageListView.m

You should overwrite intrinsicContentSize returning the preferred content size of your view.
The problem is that in intrinsicContentSize the view has not it's final bounds. You can use an internal height constraint, and overwrite updateConstraints:
- (void)updateConstraints {
CGFloat theWidth = CGRectGetWidth(self.bounds);
NSUInteger theCount = [self.subviews count];
CGFloat theRows = ceilf(theCount / floorf(theWidth / 30.0));
self.heightConstraint.constant = 30.0 * theRows;
[super updateConstraints];
}
In viewDidAppear: and on layout changes (e. g. rotation) you have to call setNeedsUpdateConstraints to get a proper initial layout of the image views.

Related

Implementing iPad General Setting Page

So far I have customized the tableview and implemented the iPad General Setting Page. Below is the code for tableview which will change frame accordingly. But the issue is when I insert/delete rows or section in the tableview. My tableviewcell backgroundview (not cell) width get shrinks. Any idea what wrong am I doing here?
- (void)setFrame:(CGRect)frame
{
if (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad)
{
CGFloat inset =10;
frame.origin.x += inset;
frame.size.width -= 2 * inset;//The issue is in this line
}
[super setFrame:frame];
}
I found the simple solution to achieve this. No need of customizing UITableView subclass. Just take the outlet of tableview and set the frame and change the color of backgroundview. like that below:-
CGFloat tableBorderLeft = 10;
CGFloat tableBorderRight = 10;
CGRect tableRect = self.view.frame;
tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
yourTableView.frame = tableRect;
self.view.backgroundColor=[UIColor colorWithRed:(239/255.0f) green:(239/255.0f) blue:(244/255.0f) alpha:1.0];

Change the width of a popover which has table view in it

I'm trying to achieve two things:
To change the height of the popover to the number of cells in my static table view (which works).
But for the popovers width to be configurable using the storyboard builder.
- (void)viewDidLoad
{
CGRect popoverBounds = self.view.frame;
[self.tableView sizeToFit];
CGRect newBounds = self.tableView.bounds;
newBounds.size.width = popoverBounds.size.width;
self.tableView.bounds = newBounds;
self.preferredContentSize = self.tableView.contentSize;
}
currently this sets the popover to the width of the table view.
Problem is inside this line:
self.tableView.bounds = newBounds;
You change internal bounds, instead of frame, you need to use:
self.tableView.frame = newBounds;

How can I resize a UICollectionViewCell along one axis without distorting its contents?

I am trying to resize a UICollectionViewCell along only one axis without distorting its contents.
I have read answers for similar problems involving UICollectionViewLayoutAttributes layoutAttributesForItemAtIndexPath and layoutAttributesForElementsInRect. I have also tried using initWithFrame and awakeFromNib in a UICollectionViewCell subclass. I have attempted to set UIViewContentMode to UIViewContentModeScaleAspectFit or UIViewContentModeScaleAspectFill in those methods, but that has had no effect.
So far, I still get distorted text when I stretch along only one axis.
So...
I have a UICollectionView with its cells defined in a xib. Currently the cell only contains a UILabel. The collection view uses UICollectionViewFlowLayout to do some minor customizations:
self.flowLayout = [[UICollectionViewFlowLayout alloc] init];
[self.flowLayout setMinimumLineSpacing:1.0f];
[self.flowLayout setItemSize:CGSizeMake(self.cellSize.width, self.cellSize.height)];
[self.flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:self.flowLayout];
The UICollectionView is embedded inside a UIScrollView, and fits exactly inside the scroll view's dimensions. The scroll view size is set to be much larger than the screen in both directions. Hard-coded numbers are just for testing; I will turn them into appropriate constants later.
self.cellSize = CGSizeMake(150.0f, 150.0f);
CGSize scrollViewSize = self.scrollView.contentSize;
scrollViewSize.width = 20 * self.cellSize.width + 20.0f;
scrollViewSize.height = 20 * self.cellSize.height + 20.0f;
self.scrollView.contentSize = scrollViewSize;
self.collectionViewHeightConstraint.constant = self.scrollView.contentSize.height;
self.collectionViewWidthConstraint.constant = self.scrollView.contentSize.width;
The result is a grid which is scrollable in any direction. That part is working.
The grid needs to be stretchable, either proportionally or along only one axis at a time. I am using the following pinch gesture recognizer to make one-axis zooming happen (again, the hard-coded 1.0 value for y is just for testing):
- (IBAction)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1.0);
recognizer.scale = 1;
}
When I resize the collection view with a pinch gesture, the label in each cell stretches and deforms. Here's a screen grab:
(source: afterburnerimages.com)
I will supply any other pertinent information gladly, and all assistance is much appreciated!
Oky u are using scrollview which contains collection view then u can use scrollview delegate for zoom, in your case it is stretches, better u can use like below
for example,
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
_scrollView.delegate = self;
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setMinimumLineSpacing:1.0f];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionVIew setCollectionViewLayout:flowLayout];
[self.collectionVIew registerClass:[CustomCollectionVIewCell class] forCellWithReuseIdentifier:#"CELL_ID"];
//setting up content size for scroll view
CGSize size = self.scrollView.contentSize;
size.width = 200 * 10;
size.height = 185 * 10;
self.scrollView.contentSize = size;
CGRect rect = CGRectZero;
rect.origin = self.scrollView.bounds.origin;
rect.size = size;
self.collectionVIew.frame = rect;
self.scrollView.backgroundColor = [UIColor greenColor];
[self.scrollView addSubview:self.collectionVIew];
[self.view addSubview:self.scrollView];
// CGRect scrollViewFrame = self.scrollView.frame;
// CGFloat scaleWidth = _collectionVIew.frame.size.width / self.scrollView.contentSize.width;
// CGFloat scaleHeight = _collectionVIew.frame.size.height / self.scrollView.contentSize.height;
// CGFloat minScale = MIN(scaleWidth, scaleHeight);
//set min scale must be less than max zoom scale so that it can be zoomable
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.maximumZoomScale = 1.0f;
// self.scrollView.zoomScale = minScale; //default is 1.0f;
}
and use scroll view delegates to perform zoom
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.collectionVIew; //return collection view which is embedded inside scroll view
}
and result is something like gif below stretching proportionally without effecting the contents of collection view , hope this helps u .. :)

how to resize UIScrollView to fit content of children UITextViews

I have a UIScrollView with a number of children UITextViews arranged vertically. I want the UIScrollView to resize to fit content. So my TextViews I do
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat fixedWidth = textView.frame.size.width;
CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
CGRect newFrame = textView.frame;
newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
textView.frame = newFrame;
[textView setNeedsLayout];
}
And for the scrollview I do
-(void)resizeScrollViewToFitContent
{
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
}
I call [self resizeScrollViewToFitContent] inside viewDidAppear. Any ideas why this setup is not working?
I am not sure how is the arrangement of your textViews inside the scrollView. I assume that they are arranged vertically right next to each other. I have created a sample project with a scrollView and 3 textViews and it works well with the following code:-
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
CGFloat height = self.textView1.frame.size.height +self.textView2.frame.size.height +self.textView3.frame.size.height ;
CGSize size = CGSizeMake(self.scrollView.frame.size.width, height);
self.scrollView.contentSize = size;
}
From my own project, the content Height of the scrollview is the combination of the height for all 3 textviews. It might be different in your project.
You shouldn't be doing the union of the contentFrame and view.frame. Try the following:
-(void)resizeScrollViewToFitContent
{
UIScrollView* scrollView;
CGSize contentSize = CGSizeZero;
for (UIView* subview in scrollView.subviews)
{
CGFloat subviewRight = CGRectGetMaxX(subview.frame);
if (subviewRight > contentSize.width)
{
contentSize.width = subviewRight;
}
CGFloat subviewBottom = CGRectGetMaxY(subview.frame);
if (subviewBottom > contentSize.height)
{
contentSize.height = subviewBottom;
}
}
scrollView.contentSize = contentSize;
}
One small thing worth noting: a UIScrollView's scroll indicators are subviews of any UIScrollView. This has the potential to throw off the above code's accuracy, but I've found that it always works except in some very particular cases.
If you know how many subviews are in your scrollview per row then you'll want to do a little math to find the correct height.
For example, you might have a uiview with a uiimageview and uilabel, so you're subview count will be 3 times what it should be.
try
// csv = contentscrollview
NSLog(#"%d", [[csv subviews] count] / 3);

Resize UItextview according to it's content in iOS7

I'm trying to resize a text view according to content & also it's sibling and parent container.
Below code is working fine in iOS 6
if (/* less than ios 7 */) {
CGRect frame = _textView.frame;
CGSize conSize = _textView.contentSize;
CGFloat difference = conSize.height - frame.size.height;
frame.size.height += difference;
_textView.frame = frame;
UIScrollView *parentView = (UIScrollView *)_textView.superview;
// adjust views residing below this text view.
// sibling view
UIView *belowView = // access it somehow
CGRect frame1 = belowView.frame;
frame1.origin.y += difference;
belowView.frame = frame1;
// adjust parent scroll view, increase height.
CGSize frame3 = parentView.contentSize;
frame3.height += difference;
parentView.contentSize = frame3;
} else {
// tried
[_textView sizeToFit];
[_textView layoutIfNeeded];
[parentView sizeToFit];
[parentView layoutIfNeeded];
}
Tried to follow iOS 7 solution from:
How do I size a UITextView to its content on iOS 7?
but not working.
Any pointers?
Working code solution from #NSBouzouki
if (/* ios 7 */) {
[_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer];
[_textView layoutIfNeeded];
}
CGRect frame = _textView.frame;
CGSize conSize = _textView.contentSize;
CGFloat difference = conSize.height - frame.size.height;
frame.size.height += difference;
_textView.frame = frame;
UIScrollView *parentView = (UIScrollView *)_textView.superview;
// adjust views residing below this text view.
// sibling view
UIView *belowView = // access it somehow
CGRect frame1 = belowView.frame;
frame1.origin.y += difference;
belowView.frame = frame1;
// adjust parent scroll view, increase height.
CGSize frame3 = parentView.contentSize;
frame3.height += difference;
parentView.contentSize = frame3;
It seems UITextView's contentSize property is not correctly set in iOS 7 till viewDidAppear:. This is probably because NSLayoutManager lays out the text lazily and the entire text must be laid out for contentSize to be correct. The ensureLayoutForTextContainer: method forces layout of the provided text container after which usedRectForTextContainer: can be used for getting the bounds. In order to get total width and height correctly, textContainerInset property must be taken into account. The following method worked for me.
- (CGRect)contentSizeRectForTextView:(UITextView *)textView
{
[textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
CGRect textBounds = [textView.layoutManager usedRectForTextContainer:textView.textContainer];
CGFloat width = (CGFloat)ceil(textBounds.size.width + textView.textContainerInset.left + textView.textContainerInset.right);
CGFloat height = (CGFloat)ceil(textBounds.size.height + textView.textContainerInset.top + textView.textContainerInset.bottom);
return CGRectMake(0, 0, width, height);
}
Additionally, it seems UITextView's setContentSize: method is called from layoutSubviews. So, calling layoutIfNeeded on a textView (which itself calls layoutSubviews) after calling ensureLayoutForTextContainer: on its layoutManager, should make the textView's contentSize correct.
[someTextView.layoutManager ensureLayoutForTextContainer:someTextView.textContainer];
[someTextView layoutIfNeeded];
// someTextView.contentSize should now have correct value
GrowingTextViewHandler is an NSObject subclass which resizes text view as user types text.
Here is how you can use it.
#interface ViewController ()<UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UITextView *textView;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *heightConstraint;
#property (strong, nonatomic) GrowingTextViewHandler *handler;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.handler = [[GrowingTextViewHandler alloc]initWithTextView:self.textView withHeightConstraint:self.heightConstraint];
[self.handler updateMinimumNumberOfLines:3 andMaximumNumberOfLine:8];
}
- (void)textViewDidChange:(UITextView *)textView {
[self.handler resizeTextViewWithAnimation:YES];
}
#end

Resources