I have a scroll view in my app, initialized through storyboard, and I am attempting to change the frame. Everything seems to work, but if the frame and content size are accessed just a short time after they are set, but after my method returns, they are different! Here is the code:
CGFloat inputHeight = inputView.frame.size.height;
originalContentHeight = self.scrollableContentView.frame.size.height;
NSLog(#"%f", self.scrollableContentView.frame.size.height-inputHeight);
[self.scrollableContentView setFrame:CGRectMake(self.scrollableContentView.frame.origin.x, self.scrollableContentView.frame.origin.y, self.scrollableContentView.frame.size.width, self.scrollableContentView.frame.size.height-inputHeight)];
NSLog(#"%f", self.scrollableContentView.frame.size.height);
NSLog(#"%#", self.scrollableContentView);
The output of these logs are all as expected, that is:
<UIScrollView: 0x808de00; frame = (0 0; 320 81); ... >
However, if I set a breakpoint in another method, by the time that is called, logging the scrollView shows this:
<UIScrollView: 0x808de00; frame = (0 0; 320 416); ... >
Additionally, the content size changes as such:
(width=320, height=416)
to
(width=320, height=504)
Both of the values seem to be reverting automatically to the values they have when the view is first laid out. I have set breakpoints in the getter and setter of my scroll view, and cannot find any unexpected accesses, so I have concluded that the change must come internally. Is this a justifiable conclusion? What could be going wrong?
If you're using autolayout you should be changing the constraints, not setting the scrollview's frame.
See my answer to a similar question here. It has some links to examples of how to set constraints programmatically.
Also, in which method are you setting the frame and contentSize? Since you are using a storyboard, if it is in viewDidLoad or viewWillAppear: it would be better to move this code to viewDidLayoutSubviews instead.
Related
I have 3 different collectionViewCells all which should be different sizes, I've set them all to have a custom size but the cell size reverts back to the default size set by the collectionView during runtime. I've tried this within my cellForIndexPath
cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, 200)
collectionView(_:cellForItemAtIndexPath:) is a member of UICollectionViewDataSource which means it deals with data population of the cell, not visual presentation. The Apple documentation on this method states: "You do not need to set the location of the cell inside the collection view’s bounds. The collection view sets the location of each cell automatically using the layout attributes provided by its layout object." Most likely you're setting the frame and the layout object is overriding that value.
What you're looking for is UICollectionViewDelegateFlowLayouts collectionView(_:layout:sizeForItemAtIndexPath:) method. Here you can specify the frame size of individual cells. If you want to get really crazy, you can even write your own layout.
I have an iPhone app in which I'm using a UITableView to format a set of text responses, one per row. I've set up the cell in the storyboard to have a label inside it, and set up a constraint saying that the label should be 10 points from the edge of the cell. I then set up a custom subclass of UITableViewCell, set the cell in the storyboard to be of that class, and connected the outlet.
However, when I load the table, I see the text in the cell moving slightly to the right under some circumstances: when I select the cell, or when I load additional cells into the table. In fact, in the latter case, sometimes everything gets shifted to the right, even cells which were already there!
What the heck is going on here? The only changes I'm making in tableView:cellForRowAtIndexPath: are to the text in the label, and I'm always setting it. And I've unset "Indent While Editing" on the cell in the storyboard.
Answering some of the questions: I'm setting the view up using the storyboard. Xcode isn't reporting any ambiguity with the constraints. Also, here are the screenshots, before and after:
My guess is that the constraints for the label are ambiguous. Ambiguity can make UI components jump around for inexplicable reasons. You probably need to set more constraints for the label to define its position on both axes.
Or, maybe all you need to do is set the label to the "size that fits content" (intrinsic content size) under the Editor menu in IB.
Did you add a new label to the UITableViewCell, or are you working with the textLabel that already exists in it? If you added a new one, consider removing it and using the cell's existing textLabel property instead. If that's not an option for some reason, double-check that the label you've added is in the contentView of the cell, and that all the constraints are relative to the parent view, not to the cell itself.
Also, for debugging, you could set the cell's contentView background color to red (cell.contentView.backgroundColor = [UIColor redColor];) - this might give you a better sense of what's moving, the label or the whole view.
This is just a guess without seeing your code. I had a similar problem once, when the app reused an existing cell, the dimensions of the label were not correct. So, I had to remove the old label in my cellForRowAtIndexPath method, before adding a new label. Here's how I removed the old one:
UIView *oldLabel = [cell viewWithTag:3];
if (oldLabel != nil)
{
[oldLabel removeFromSuperview];
}
Then I added a new label like this:
[cell.contentView addSubview:newLabelOrWhatever];
It might be worth checking to see that your string content doesn't have a space prefixed on it. Also, you could verify the actual position of the label by setting the background color.
I had a similar issue when the label would move when the cell was selected. It was a custom cell that I was loading from a custom Nib.
In the Nib I had not set the backgroundView of the UITableViewCell (superclass) to any view. Once I set it (I set it to the ContentView) the issue stopped.
My auto-layout constrains seems fine and had no issues, so I'm assuming it was the above that fixed it.
If you have a custom subclass of UITableViewCell, try implementing layoutSubviews. Let me try from the top of my head:
static CGFloat const kLeftMargin = 10.0f;
- (void) layoutSubviews
{
[super layoutSubviews];
/* Prepare for processing */
CGSize cellSize = self.bounds.size;
/* Change the textLabel frame */
{
/* Compute the new label frame */
CGSize labelSize = self.textLabel.bounds.size;
CGFloat xCoor = kLeftMargin;
CGFloat yCoor = roundf((cellSize.height - labelSize.height) / 2.0f);
CGRect labelFrame = CGRectMake(xCoor, yCoor,
labelSize.width, labelSize.height);
/* Set the new label frame */
self.textLabel.frame = labelFrame;
}
}
Now, this isn't what is usually recommended (since a lot of people use Storyboards and NIBs), but from experience, if you want some correct layouting done, implement layoutSubviews.
I ran into an issue where clipping of subviews is not working consistently (or potentially, the UIView resize isn't working). Here's the scenario:
I have a UIView (section above the line). The actual size is larger to accommodate search bar. I basically resize it on viewDidLoad.
When clicking on the text field, UIView expands and show a search bar
After search, I collapse the UIView, but found that the UIView is not collapsed in certain case (see first image). If I were to not hide the search bar, it will remain also.
Is there any reason why this issue occurs? I'm still trying to debug and see if there is anything that could have caused this issue (as something may reset to original size). It definitely does seem like everything is resized correctly since even the result table is moved correctly also. Something else must have triggered it after the expected resize. Any pointer is appreciated. I've only done iOS development for 5 days so I'm still not aware of a lot of things.
- (void)showAddressField
{
CGRect headerFrame = self.searchHeaderView.frame;
headerFrame.size.height = headerHeight + adjustmentSize;
self.searchHeaderView.frame = headerFrame;
CGRect tableFrame = resultTableView.frame;
tableFrame.size.height = tableHeight - adjustmentSize;
tableFrame.origin.y = headerFrame.size.height + statusBarHeight;
resultTableView.frame = tableFrame;
[self renderHeaderBorder:headerFrame.size.height - 1];
}
- (void)hideAddressField
{
CGRect headerFrame = self.searchHeaderView.frame;
headerFrame.size.height = headerHeight;
self.searchHeaderView.frame = headerFrame;
CGRect tableFrame = resultTableView.frame;
tableFrame.size.height = tableHeight;
tableFrame.origin.y = headerHeight + statusBarHeight;
resultTableView.frame = tableFrame;
[self renderHeaderBorder:headerHeight - 1];
}
EDITED
SearchHeaderView is just a UIView that is a subview of the main view. It's not a table header. I found that if I put a search bar in the table header, it behaves very unpredictably so I have a UIView containing the search portion and have a UITableView right below it.
Sorry, since I only have just over a week to get a rather massive app out from scratch, I didn't have time to wait. I already changed my approach a little bit, but I will still award the points even after the bounty has expired. I'm trying to understand everything to do with layout since that's pretty much the only thing that I can't quite figure out with iOS app development.
If the search bar is in the table view header, try reassigning the header view to the table view:
[tableView setTableHeaderView:self.searchHeaderView];
If it is a section header, be sure to update the value for the delegate's
– tableView:heightForHeaderInSection:
– tableView:viewForHeaderInSection:
Otherwise, please post more code on how the searchHeaderView is initialized and added to the table.
Hope this helps!
Firstly, frames are already relative to the enclosing window. You should not be taking into account the height of the status bar (assuming that refers to the 20px area at the top of the screen).
I have found in the past that you may need to implement "layoutSubviews" in your view controller and compute the frames of the views there. It depends on your resizing mask / if you auto-layout enabled.
As the others have stated, more code would be helpful... or at least the relevant portions of the nib/xib.
I have a segment of code that gets executed everytime a backend database change occurs. Essentially I have a label inside of a parent view. The label gets updated by one of many status messages each one on a different line ended by a new line (\n). Each status message needs to be on one line only and not go over.
The problem I am having is that everything works fine when the View first reloads. However when I am on the screen and a change occurs in background, once in a while a status message displays on multiple lines. While sizeToFit does a great job of making the label as high as the parent containing view, it does so poorly in terms of width. That is it will break up text on multiple lines as opposed to looking at the line breaks only.
What can I do?
self.messageLabel.text = message; //lets get the status message.
[self.messageLabel sizeToFit];
[self.messageLabel setNeedsDisplay];
CGRect frame = [self.messageView frame];
frame.size.height = self.messageLabel.bounds.size.height;
self.messageView.frame = frame;
self.messageLabel.center = CGPointMake(CGRectGetMidX(self.messageView.bounds), CGRectGetMidY(self.messageView.bounds)-(self.messageView.cornerRadius/4));
self.messageLabel.backgroundColor = [UIColor orangeColor];
[self.messageView setNeedsDisplay];
In order to determine the size that your label will need to be, you need to look into the NSString size methods. There's a number of them that can tell you the size a string will be given a font, and various constraints.
For example [#"string" sizeWithFont: forWidth: lineBreakMode:]; will return a CGSize that you can then use to size your label appropriately.
As an aside, I find the UILabel does not handle strings with multiple lines well. You may be better off writing your own subview of UIView that can handle multiple lines of text, each with its own label.
It seems to happen each time the frame is resized after the initial operation. Presumably the frame's current size is used when deciding the new size in -sizeToFit.
The workaround I'm using is to set the view's frame to a pre-defined size before each call to sizeToFit, e.g:
myLabel.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.width.height);
[myLabel sizeToFit];
I am having a problem! When I use addSubview: and add a view to a UIScrollView, it adds a completely new view.
I want to either:
toggle between 3 predefined views, or
remove the last view from the UIScrollView before adding another one.
(All 3 views have some images so I'd imagine that the second option would be more efficient, memory-wise. However, perhaps the first option would be better on battery life? This is a 'subquestion'.)
How does one accomplish this?
You could accomplish your plan B by using removeFromSuperview function of your UIView.
Use it as below.
[mySecondImageView removeFromSuperview];
[myScrollView addSubview:myThirdImageView];
EDITED:
When you create UIImageView assign a tag value to each and also use and integer iVar to hold the tag value of your current view lets say it's currentViewTag ;
myFirstImageView.tag = 1;
mySecondImageView.tag = 2;
myThirdImageView.tag = 3;
currentViewTag = 1;
[myScrollView addSubview:myFirstImageView];
Now use as below
[[myScrollView viewWithTag:currentViewTag] removeFromSuperview];
[myScrollView addSubview:myThirdImageView];
currentViewTag = 3;
Sounds to me that you are trying to implement an infinite scrolling uiscrollview like the photos app.
If so, you don't need to remove any subviews and add them, you just need to create a UIScrollView that is as wide as three of your images and then when the scroll view is scrolled, change the image in either position 1 or 3 depending on which way the scroll is performed and then reset the position of the scroll view. If this is what you are trying to do, I can give more information if you need it.