Currently I have multiple UIButtons that have an image on the left side (added through the IB). I'd like to add a UIImageView subview to the buttons programatically to the right side. Currently I am doing it in this manner:
UIImageView *rightArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"menu-arrow.png"]];
rightArrow.frame = CGRectMake(button.frame.size.width - 32, button.frame.size.height/2 - 9, 17, 17);
rightArrow.tag = 22;
[button addSubview:rightArrow];
This works great in adding the button and placing it on the right side. However, doing this in the ViewDidLoad method creates issues. In the IB the width of the button is the width of the view. So in the ViewDidLoad method the button's width hasn't been changed by autolayout. So it doesn't show up on the far right side.
So I moved it to the ViewDidLayoutSubviews. But on iOS 8, this is called multiple times! And on iOS 7 it's called once! Also, in iOS 8 the LAST call by ViewDidLayoutSubviews is the most accurate in terms of placing the image!
How can I solve this particular issue? Also I'd like to do this programatically rather than try and mess with the IB if possible.
Edit: Turns out the best answer (IMO) was from Mr. T (in the comments) which stats putting the code in ViewDidAppear and then doing a check if you've already added the ImageViewto the button.
But on iOS 8, this is called multiple times! And on iOS 7 it's called once! Also, in iOS 8 the LAST call by ViewDidLayoutSubviews is the most accurate in terms of placing the image!
So respond each time viewDidLayoutSubviews is called. If the image view is already added, don't add it again — but do reposition it if necessary.
An even better approach, however, would be be to position the image view itself using autolayout! Instead of assigning it a frame, give it constraints. Now it will be repositioned automatically in response to view layout, for the rest of time.
Related
I have created my own implementation of a UITabBar (just a UIView instance).
This UIView contains 3 TabBarItem instances. A TabBarItem is a UIView subclass, and each contains the following UI controls:
UIImageView
UILabel
TabBarBadge (custom UIView subclass)
I am laying out the view hierarchy in a storyboard using auto layout.
The badge for a tab bar item should be positioned so that the y-center of the badge is aligned with the top edge of the image view, and it should be aligned to the right side of the image view, with a 4 point overlap (so, left edge of badge aligned with right edge of image view, with a constant of -4).
This works fine when the app loads, whether in portrait or landscape mode. In either case, though, rotating the device ends up with the frame of the badge in the wrong place after rotation finishes.
Here's a printout from the console of the NSLayoutConstraint in question:
<NSLayoutConstraint H:[UIImageView]-(-4)-[TabBarBadge] (active)>
That's exactly what I would expect it to be.
Here's a printout from the console of the TabBarBadge in question (portrait mode; correct presentation):
<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>
Here's a printout from the console of the TabBarBadge in question (landscape mode; incorrect presentation):
<TabBarBadge frame = (80.6667 0; 36 20); text = '67'; autoresize = RM+BM>
So, you can see that the frame isn't changing/updating when the rotation occurs.
Here are a couple of screen shots:
Portrait, Correct Display
Landscape, Incorrect Display
Constraints (Printout of constraints above is from this view)
So, what has me confused is that the rest of the TabBarItem is updating correctly. The UIImageView and UILabel are automatically updating correctly. Why is this one frame not updating correctly?
So, first I researched whether a custom drawRect: method might be the cause of my problem. That doesn't appear to be a factor.
Then, I started noticing some odd discrepancies between the view's frame at different times, and how only this particular one (there are actually 3 in the app) was having the problem. The other 2 currently have no values displayed.
Then, I remembered that I use a UIDynamicAnimator when the value is changed to "bounce" the view, in order to bring attention to the new value.
So, the problem is actually that I wasn't "releasing" (in the control sense, not the memory sense) the view from the UIDynamicAnimator when I was done with the animation. (I'm still not sure whether that's a bug or not. It seems to me that if the animation is paused and the constraints should update the view's frame, then perhaps that should occur.)
So, here's the actual code that was "buggy":
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
// {Other code}
}
And here's the code that fixed my issue:
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator {
// {Other code}
// Code that fixed the issue:
[self.animator removeAllBehaviors];
}
If I need to animate the view again, I just re-create the animator anyway, so this seems to be the best fix.
This appears to be in agreement with Matt's answer here. (I didn't know this was an issue with UIDynamicAnimator when I asked the question.)
I created a UIView using storyboard, set up constraints. When I ran the application, the interface looked fine. The view covered the entire screen as it was supposed to (I made the background color black so it's easier to see what's going on).
http://contentlibrary.sinaapp.com/View%20from%20Storyboard.png (I'm sorry I have to link images in like this, but the system says I need at least 10 rep to post images.)
However, when I tried to get the size of that view using myView.bounds.size. I got (600, 580), which is exactly the size that appeared in the storyboard's size inspector.
I created another UIView in code
var myView = UIView(frame: CGRect(x: 0, y: 20, width: 600, height: 580))
and added it as a subview to the top-level view. It appeared but it didn't cover the entire screen like the view I created in storyboard. Both the view I created in storyboard and in code have the same value in their .bounds.size, but they appeared to have very different sizes on screen. (This picture is the view created in code. See there is white space at the bottom of the screen.)
http://contentlibrary.sinaapp.com/View%20from%20Code.png
Another interesting thing is, if I replace the above UIView with a UIScrollView, after the scroll view zoomed, it will update to the correct size.
I added a UIScrollView the same way I added the UIView using storyboard. I tried to print its size inside the delegate method scrollViewDidEndZooming:withView:atScale:. It says (375.0, 647.0), which is the correct on-screen size for a view that covers the entire screen on an iPhone 6.
Judging from this, it seems that views that come out of a storyboard don't automatically update to the correct on-screen sizes.
So how do I get a UIView object created in a storyboard to update its size?
Sounds like you're checking the size too early.
Try to put your code in viewDidLayoutSubviews method and see if it's going to change.
I'm trying to update the position and size of several UIButtons in my app by using:
-(void)viewDidAppear:(BOOL)animated {
_UIButton1.frame = CGRectMake(-7, 35, 212, 77);
_UIButton2.frame = CGRectMake(139, 149, 290, 77);
// And so on for several more buttons
}
However, when I use this code, these buttons just appear at wherever the location is set on the Storyboard, not where I'm setting it programmatically. How would I make these buttons appear at a location this is different than the one I have set in my Storyboard?
Open your storyboard file and make sure the right pane is showing the Utilities menu, then uncheck Use Auto Layout.
When Auto Layout is enabled, you have to set constraints on your views and create outlets to those constraints to move things around with code. If you want to work directly with frames, Auto Layout needs to be turned off. It has to be one or the other.
Auto Layout can be great, but it's hard to make a recommendation without knowing more about what you are trying to achieve.
Please update frame in -(void) viewWillAppear method as in viewDidAppear your view is practically set and is ready to displayed.
after updating your button's frame please use following line of code
[self.view laoutIfNeeded];
Thanks
I have 2 UITableView in my UIViewController. one of them is in the right side of the screen till center of screen. The other is from center till left bound.
User can hide the right UITableview by pressing one a button on the navigation bar.
I use this code to make left UITableview full screen:
self.rightTableView.hidden= YES;
self.leftTable.frame= CGRectMake(0,
self.dataTableView.frame.origin.y,
self.view.frame.size.width,
self.dataTableView.frame.size.height);
But it is not working.
I even put this code on the viewdidload and viewDidAppear methods but the frame not changing.
Try self.leftTable.translatesAutoresizingMaskIntoConstraints = YES, then call to frame resize at runtime is automatically translated into new constraints.
But, translatesAutoresizingMaskIntoConstraints will also cause new constraints to be added based on the view's autoresizingMask. So make sure other constraints are working properly.
When using autolayout, modifying the frame will have no effect.
Instead, you should modify the constraints.
You can do this by creating outlets for your constraints and connecting then in the interface builder.
I'm pretty new to ios. I'm working on a project which is going to frequently use a certain UIView class throughout the application. This class is simply an image that does some transparency stuff with the background color. This works fine so far. This class sometimes exists as a lone UIView, and sometimes as a button subview.
When I add this UIView to a UIButton as a subview, the full contents of the UIView are displayed at full size, but the clickable area of the button remains the size defined in the xib unless Use Autolayout is turned off (even if I manually try to set the button's frame.)
I would like to put a UIButton on the xib as a placeholder, and then later define its size/clickable area based on the size of the overlay image which the UIView that was initialized with.
Should this be possible, or am I misinterpreting the usage of xib files/autolayout?
under ViewDidLoad I have...
- (void)viewDidLoad{
[theButton addSubview:_theView];
CGRect buttonFrame = theButton.frame;
buttonFrame.size = CGSizeMake(_theView.getSize.width,_theView.getSize.height);
[theButton setFrame:buttonFrame];
}
However, the frame stays the same size when I print the button info before and after I try calling setFrame.
(note that '_theView.getSize' was added by me)
Under Autolayout, views don't have frames at viewDidLoad. Try your code in viewWillAppear: or viewDidLayoutSubviews.
Under Autolayout, you don't set frames. You edit constraints instead. Setting a frame will work until the next layout pass, when your layout will revert to that described by your constraints.
To size a button to fit a subview, you can try something like this (in visual format language): |-[theView]-| but it would depend what constraints are in place from your xib.