I want to create a vertical UISlider and exactly fit it into an existing container view that is sized and placed from a .xib file using autoLayout. But I simply cannot get it to work and I have Googled my fingers bloody.
The closest I have gotten creates a slider that is too long and extends off the screen.
Am I putting the code in the wrong place?
I am putting my code in "layOutSubviews"
Some Points of interest:
1.Per Apple's docs, the view's "frame" is not valid after the transformation and should not be set.
2.The frame of the slider is set to the future parent's dimensions before the transformation (with x and y swapped).
3.The transformed view appears to maintain its bounds in the pre-transformation coordinate frame. i.e. After the 90 degree transformation, the width and height of the transformed view's bounds appear to be swapped.
This code doesn't work. The slider looks right except that it extends off the bottom of the screen. The slider bounds and even the frame (which Apple says is not valid) seem to match the bounds and frame of the container view. The slider doesn't even stay within its own bounds.
[super layoutSubviews];
CGRect trigLevelSliderFrame=self.trigLevelSliderContainer.bounds;
trigLevelSliderFrame.size.height=self.trigLevelSliderContainer.bounds.size.width;
trigLevelSliderFrame.size.width=self.trigLevelSliderContainer.bounds.size.height;
UISlider *mySlider=[[UISlider alloc] initWithFrame:trigLevelSliderFrame];
self.trigSlider=mySlider;
[mySlider release];
self.trigSlider.transform=CGAffineTransformMakeRotation(-M_PI_2);
CGPoint center=CGPointMake(self.trigLevelSliderContainer.bounds.size.width/2,self.trigLevelSliderContainer.bounds.size.height/2);
self.trigSlider.center=center;
[self.trigLevelSliderContainer addSubview:self.trigSlider];
There is no problem with this code to add and rotate the slider. The problem is that the the code must be put into the "layoutSubviews" method of the slider's parent view; instead I had it in the "layoutSubviews" of the parent view of the parent view of the slider. When my original code executed the slider's parent view had not yet been laid out and did not yet have the correct dimensions.
During "layoutSubviews" of a view, the subviews have not yet been laid out and so their bounds are not yet valid. I needed to wait until later in the layout process to get the bounds of the parent view and transform the slider to fit.
In the end, I put the code to add the slider and transform the slider in the "viewDidAppear" of the top level view controller. This was the same code as in the original question - just in a different place.
Related
I've got an UIImageView inside a root view of a controller, and I've set it to be 90% of the screen width and given it an aspect ratio as constraints to set the dimensions.
In the code I'm trying to do something with respect to the size of the UIImageView at runtime, however when I get the frame.size.height or frame.size.width of the UIImageView they are clearly wrong and way too small.
At first I was accessing the size in viewDidLoad(), after which I found quite a few posts suggesting to do it either in viewWillLayoutSubviews(), viewDidLayoutSubviews(), or viewWillAppear(). Unfortunately I've tried all of those and none of these contexts seem to provide the right value. I suspect it may have something to do with auto layout but I'm not quite sure how to get around this. Any insight as to why this may be would be appreciated
viewDidLoad is too early. At this time, the views have the frames they were given in the storyboard. Ditto for viewWillAppear.
In viewWillLayoutSubviews, the view controller's top-level view has its correct frame, but its descendants do not.
In viewDidLayoutSubviews, the view controller's immediate subviews have their correct frames, but more distant descendants (“grandchildren” and so forth) don't.
If the image view is a direct subview of the view controller's view, then its frame is up to date in viewDidLayoutSubviews.
If the image view is a more distant descendant, then there is no method you can override in the view controller that will be called after the image view's frame has been updated but before the image view is visible on screen. Here are two options in this case:
Create a custom subclass of UIView to be the superview of the image view. When the superview's layoutSubviews runs, after it calls super.layoutSubviews, the image view's frame is up to date.
Create a hidden UIView that is a direct subview of the view controller's top-level view. Use constraints to make this hidden view's frame exactly match the image view's frame. Hidden views participate in layout, so when viewDidLayoutSubviews is called, this hidden view's frame is up to date, and is the same as the image view's frame will eventually be (except that the hidden view's frame is in the top-level view's geometry, which might be different than the geometry of the image view's superview).
I have a custom view inside a view controller. I'm trying to setting the CGRect of custom view from view controller
I have few more buttons in custom view and i want to position them based on frame's coordinates but in the ovveride draw method, it takes bounds of view.
I want all the view to be drawn using frame coordinates instead of bounds
Pls advice how to acheive that
When you instantiate a view programaticly, you should do the following :
1) define the size of the rectangle that will contain your view.
2) Find the origin of this rectangle (is set to be the upper left corner) in the cartesian system (x,y) of the parent view of the view that you are creating, as function of the width and the height of the parent View .
3) Set the frame of your view as myView.frame = CGRect(origin: myOrigin, size: mySize)
You should always use the 'frame' property to define a new view.
I usually work with custom view, containing themselves many other custom Views, custom Buttons, ... The best way to do so (especially when it's getting complicated), is to take a paper & and pencil, draw the (x,y) axis of the parent View (y points downwards and x points to the right) and find all coordinates of all the origins in terms of the width and the height of the view in which you are working.
When you are working with subclasses of UIView (or any other object) you can easily use the system of coordinate defined at the origin of the view that you are creating (in the draw method)
Hope this was helpful, you can publish your code if you want more specific help !
I am trying to retrieve rect value of a class in ViewDidAppear. The button is in UITableViewCell. All values return correct except frame.origin.y. It returns -1.5. I am wondering what might cause that.
CGRect customRect = CGRectMake(self.favoriteButton.frame.origin.x, self.favoriteButton.frame.origin.y, self.favoriteButton.frame.size.height, self.favoriteButton.frame.size.width) ;
Origin.y is defined in an unintuitive.
First, moving down is positive and up is negative (aka larger numbers means the view will appear lower on the screen).
Second, the origin is relative to the top left corner of the immediate parent view (in your case the cell). If origin.y really is -1.5 then the top of your button is just one and a half points above the top of your table view cell, (which sounds likely). If your origin.y really should be 400-ish points it means for it to appear in your UITableViewCell your tableViewCell would have to be over 400 points tall.
This could be the case but I'm wondering if what you're looking for is not origin.y, but how far bellow the top of the root view (self.view in your view controller) the top of your button is. If so try:
CGPoint originInRootview = [self.view convertPoint:CGPointZero fromView:self.favoriteButton];
CGFloat theNumberIThinkYouWant = originInRootview.y
There are two things
1-) You said that it is in TableViewCell so it returns correct. Because you put button inside a view(TableViewVell container view), button gets it coordinates from container view not from superview.
2-) If TableViewCell is big enough and starts from y=0 and you are sure it is wrong and must be something like 400. Try to get the rect value at viewDidLayoutSubviews
All values return correct except frame.origin.y. It returns -1.5. I am wondering what might cause that.
Applying a transform to the view can cause it's frame property to be invalid. From the documentation for UIView's frame property:
Warning
When the value of this property is anything other than the identity
transform, the value in the frame property is undefined and should be
ignored.
So, if you're not seeing what you expect, compare your view's transform to CGAffineTransformIdentity.
I understand the difference between frames and bounds I think, bounds are to the view's local coordinate system, while frames are to the superviews.
With scrollviews however, I'm a little confused.
When I have a UIImageView in a UIScrollView and pinch to zoom it larger, it seems the frame grows larger (width and height) increases the width and height of the corresponding UIImage as well.
However, it seems like the bounds of the UIImageView don't change at all.
Why is this? How do scroll views work in regards to all of this? I've read multiple questions and checked the documentation but this explanation is eluding me.
How does zooming in a UIScrollView affect its contents? Does it just change the frame of the zooming view but somehow not the bounds?
See the explanation in my book:
The scroll view zooms by applying a scaling transform to the scalable view; therefore the frame of the scalable view is scaled as well. Moreover, the scroll view is concerned to make scrolling continue to work correctly: the limits as the user scrolls should continue to match the limits of the content, and commands like scrollRectToVisible:animated: should continue to work the same way for the same values. Therefore, the scroll view automatically scales its own contentSize to match the current zoomScale. (You can actually detect this happening by overriding setContentSize in a UIScrollView subclass: you can see the scroll view adjusting its own content size as you zoom.)
Basically (though that is not quite clear from the above quotation) we have no business concerning ourselves with the frame of the scalable view - or any view - that has a non-identity transform applied to it, which is exactly the case here. That fact is made very clear by the Apple documentation on UIView. Thus you should not be looking at the frame - only the transform. The frame value that your are reading changes purely as a side-effect of the transform change.
As for the bounds of the scalable view - obviously the bounds do not change; that is the whole point of how a transform works. It maintains a constant center and bounds, so that subviews and drawing continue to operate correctly within the frame-of-reference coordinates despite the transform. My book talks you through an understanding of this as well.
On the other hand, the scroll view's own bounds can certainly change their origin, not least because that is exactly and identically what it means for a scroll view to scroll. Scrolling is a change of bounds origin, plain and simple. And this would not be surprising during zooming, because, as I just said, the content size has changed, so the content may be repositioned in order to keep displaying it coherently as you zoom.
I want create a UIView that's offscreen when ViewDidLoad is called, but that I will animate up onto the screen once a certain function has been called. The code for animating the UIView is fine, but I can't seem to draw the UIView offscreen to begin with (I've assembled the UIView in storyboard onto my UIViewControleller). This is the code I've tried:
- (void)viewDidLoad {
...
[_tagView setFrame:CGRectMake(0, self.view.frame.size.height+40, self.view.frame.size.width, 40)];
...
}
_tagView is drawn where I've placed it in the StoryBoard editor, which is at the bottom of my UIViewController.
A couple of things: A view object's frame gives it's coordinates in it's parent view's coordinate space.
A view's bounds are it's internal coordinate system.
When you set a view's frame, it should be expressed in terms of it's parent's bounds, not it's parent's frame.
So your code should be creating your _tagView's frame in terms of self.view.bounds, not self.view.frame.
Next, are you using AutoLayout or struts and springs? In Xcode 5 everything defaults to AutoLayout. You can't change frames and bounds when AutoLayout is active. Instead you have to change the relevant constraints. If you want to set a view's frame manually, you probably want to turn off AutoLayout and use "struts and springs" style view geometry.
EDITED: I moved some of the info from comments up into my original answer for clarity:
If you want to move a view at runtime with Auto Layout active, you have to find the constraint for the setting you want to change, (width, height, leading edge, trailing edge, etc.) and control-drag from the constraint into your .h file to make an outlet. Then in your code you change the constant property associated with the constraint. – Duncan C 22 mins ago
Note that you CAN turn Auto Layout off on a storyboard-by-storyboard or NIB-by-NIB basis. If you select the file and choose the File Inspector, there is a checkbox "Use Autolayout" hidden in the "Interface Builder Document" section. I always have trouble finding it.
Here is a screen-shot showing the location of the button in the Xcode project window (far right side)