change subviews according to its superview frame - ios

I have a custom UIView class
CustomView.m
- (instancetype)initWithString:(NSString *)aString {
self = [super init];
if (!self)
return nil;
[self setAutoresizesSubviews:YES];
UILabel *label = [[UILabel alloc] init];
label.frame = ({
CGRect frame = self.frame;
frame.origin.x = CGRectGetMinX(frame) + 50;
frame.origin.y = CGRectGetMinY(frame) + 20;
frame.size.width = CGRectGetWidth(frame) - 100;
frame.size.height = CGRectGetHeight(frame) - 40;
frame;
});
[label setText:aString];
[self addSubview:label];
return self;
}
ViewController.m
-(void)addCustomView {
CustomView *custom = [CustomView alloc] initWithString:#"abc"];
custom.frame = ({
CGRect frame = self.view.frame;
frame.origin.y = CGRectGetHeight(frame) - 100;
frame.size.height = 100;
frame;
});
[self.view addSubview: custom];
}
Here's the problem, I set frame of my CustomView after alloc init it.
Which means its frame is CGRectZero before I change it.
Therefore, the frame of UILabel is zero, too.
How can I change frame of my subviews after their superview's frame changed ? Like above.
Thanks.

You simply need to implement -layoutSubviews in your CustomView, and do your subview layout/frame calculation stuff in there. -layoutSubviews will get called when your CustomView's frame changes.
Edit for further clarification:
You could add a property for the label, or use a tag, so that you can access it it -layoutSubviews, something like this;
- (void)layoutSubviews
{
self.label.frame = ({
CGRect frame = self.frame;
frame.origin.x = CGRectGetMinX(frame) + 50;
frame.origin.y = CGRectGetMinY(frame) + 20;
frame.size.width = CGRectGetWidth(frame) - 100;
frame.size.height = CGRectGetHeight(frame) - 40;
frame;
});
}
Edit 2:
Of course, what you probably want to do as well is change your initialiser to include the frame, like so:
- (instancetype)initWithFrame:(CGRect)frame string:(NSString *)string
{
self = [super initWithFrame:frame];
if (self) {
// Do some stuff
}
return self;
}
That way, your view's frame is set when you first create the label. Implementing -layoutSubviews is still a good idea though, for any future frame changes/orientation changes etc.

Related

iOS Layer animation with auto layout

I have a simple container view(green) and two sub views(red and blue) as below.
The container view is not applying auto layout and I config its size & location by frame.
While the sub views are applying auto layout(see code below)
#implementation XXView {
UIView *_leftView;
UIView *_rightView;
}
- (instancetype)init {
self = [super initWithFrame:CGRectZero];
if (self) {
[self setupViewHierarchy];
[self setupConstraints];
}
return self;
}
- (void)setupViewHierarchy {
_leftView = [[UIView alloc] init];
_leftView.backgroundColor = UIColor.redColor;
_leftView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_leftView];
_rightView = [[UIView alloc] init];
_rightView.backgroundColor = UIColor.blueColor;
_rightView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_rightView];
self.backgroundColor = UIColor.greenColor;
}
- (void)setupConstraints {
[NSLayoutConstraint activateConstraints:#[
[_leftView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10],
[_leftView.topAnchor constraintEqualToAnchor:self.topAnchor],
[_leftView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[_leftView.widthAnchor constraintEqualToConstant:50],
[_rightView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-10],
[_rightView.topAnchor constraintEqualToAnchor:_leftView.topAnchor],
[_rightView.bottomAnchor constraintEqualToAnchor:_leftView.bottomAnchor],
[_rightView.widthAnchor constraintEqualToAnchor:_leftView.widthAnchor],
]];
}
...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
XXView *tv = [[XXView alloc] init];
CGFloat width = self.view.bounds.size.width;
tv.frame = CGRectMake(50, 400, width-100, 100);
[self.view addSubview:tv];
self.tv = tv;
}
Then I would like to animate the container's width change by using the CABasicAnimation as below:
- (void)startAnimation {
[CATransaction begin];
CGFloat width = self.bounds.size.width;
CABasicAnimation *widthAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size.width"];
widthAnimation.fromValue = #(width/2);
widthAnimation.toValue = #(width);
widthAnimation.duration = 1;
[self.layer addAnimation:widthAnimation forKey:#"123"];
[CATransaction commit];
}
However, the animation is not as I would expect. I would expect the left view moves as the container's leading side and the right view does as the trailing side.
What I see is, the green view expands as expected and the left view moves as green view's leading side. However, the right view is always keeping the same distance to left view. Below is the screenshot taken at the beginning of the animation.
Why the animation is not working as expected?
The problem is that your CABasicAnimation is modifying the bounds of the layer ... but as far as auto-layout is concerned that does not change the width of the view.
It's not really clear what your goal is here, but if you change your animation method to this it might get you on your way:
- (void)startAnimation {
CGRect f = self.frame;
CGFloat fw = f.size.width;
f.size.width = fw / 2;
self.frame = f;
[self layoutIfNeeded];
f.size.width = fw;
[UIView animateWithDuration:1.0 animations:^{
self.frame = f;
[self layoutIfNeeded];
}];
}

URL is not loading completely on my UIWebView in iOS?

I am working on an iPhone application, where i am opening an URL to my UIWebView. The problem is URL is not loading perfectly. As per my requirement, i need to add UIWebView as footer view of my table. but the problem is after getting the web view content height size from webViewDidFinishLoad method, i am setting frame of my footer view. here is my code :
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
aWebView.scrollView.scrollEnabled = NO;
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;
[self setFooterView:fittingSize.height + aWebView.frame.origin.y];
}
-(void)setFooterView:(float)fittingHeight
{
bottomView.frame = CGRectMake(0, 0, 320, fittingHeight);
webViewSocial.frame = bottomView.frame;
[tableView reloadData];
}
But when i am scrolling my table view, then my UIWebView (footer view) is not scrolling completely. Can you please tell me how can i achieve my output. Thanks!
try to set like this
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGFloat height = [[self.webView stringByEvaluatingJavaScriptFromString:#"document.height"] floatValue];
CGFloat width = [[self.webView stringByEvaluatingJavaScriptFromString:#"document.width"] floatValue];
CGRect frame = self.webView.frame;
frame.size.height = height;
frame.size.width = width;
self.webView.frame = frame;
self.tableView.tableFooterView = webView;
}

UIImageView is cut off on right side within PageControl after zooming

my image is cut off on the right side after zooming in. if i don't set the offset with cgrect make, then it is not cut off, but i want my image to be centered on the screen. how can i have my image centered and not cut off the right portion after zooming?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor grayColor];
UIScrollView *mainScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
mainScrollView.pagingEnabled = YES;
mainScrollView.showsHorizontalScrollIndicator = NO;
mainScrollView.showsVerticalScrollIndicator = NO;
CGRect innerScrollFrame = mainScrollView.bounds;
for (NSInteger i = 0; i < 2; i++) {
UIImageView *imageForZooming = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"page%d", i + 1]]];
imageForZooming.tag = VIEW_FOR_ZOOM_TAG;
imageForZooming.frame = CGRectMake(50, 0, imageForZooming.bounds.size.width, imageForZooming.bounds.size.height);
UIScrollView *pageScrollView = [[UIScrollView alloc] initWithFrame:innerScrollFrame];
pageScrollView.minimumZoomScale = 0.5f;
pageScrollView.maximumZoomScale = 1.0f;
pageScrollView.contentSize = imageForZooming.bounds.size;
pageScrollView.delegate = self;
[pageScrollView addSubview:imageForZooming];
[mainScrollView addSubview:pageScrollView];
if (i < 1) {
innerScrollFrame.origin.x += innerScrollFrame.size.width;
}
pageScrollView.zoomScale = 0.5f;
}
mainScrollView.contentSize = CGSizeMake(innerScrollFrame.origin.x + innerScrollFrame.size.width, mainScrollView.bounds.size.height);
[self.view addSubview:mainScrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [scrollView viewWithTag:VIEW_FOR_ZOOM_TAG];
}
EDIT: here is my view hierarchy
View
mainScrollView
innerScrollFrame
pageScrollView
imageForZooming
innerScrollFrame
pageScrollView
imageForZooming
For anyone else having the same problem in the future, what I did was add the following UIScrollViewDelegate protocol:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
UIView *subView = [scrollView.subviews objectAtIndex:0];
CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width) ? (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;
CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height) ? (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;
subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
scrollView.contentSize.height * 0.5 + offsetY);
}
i found the answer here: Center content of UIScrollView when smaller
so that the image would be centered after zooming instead of applying the offset that was initially applied. worked like a charm. now when I zoom, none of my image is cut off on either page control, and I can center the image at the beginning of the view.

The buttons / labels added to the UIScrollView are appeared only on the first page

I have a UIScrollView paging option enabled with the next code:
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i = 0; i < 6; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
[scrollView scrollRectToVisible:frame animated:YES];
UIView *subview = [[UIView alloc] initWithFrame:frame];
itemName.text = #"Samara";
[self.scrollView addSubview:subview];
[subview release];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 6, 1.0);
}
I have a number of elements (buttons, labels), added in storyboard to this ScrollView element, but in UI they're appeared only on the first page - the next pages are blank. How can I solve this?
Just reading through your code example - here is how to make your scrollview page, what you add inside those scrollview is up to you:
//Set your scrollView frame
[self.scrollView setFrame:CGRectMake(0,0,320,460];
//Will use this value to set the content width after the loop
CGFloat scrollWidth = 0;
CGFloat pageWidth = self.scrollView.frame.size.width;
CGFloat pageHeight = self.scrollView.frame.size.height;
for (int i = 0; i < 6; ++i)
{
//Create the page and make the origin.x i * pageWidth
UIView *page = [[UIView alloc] initWithFrame:CGRectMake((i * pageWidth),0,pageWidth,pageHeight)];
scrollWidth += page.width;
//Add the page
[self.scrollView addSubview:page];
}
//set the content size to the right edge of the last page (scrollWidth)
[self.scrollView setContentSize:CGSizeMake(scrollWidth,pageHeight)];
//Scroll to the last page - assuming you wanted this by looking at the code in your loop
[self.scrollView scrollRectToVisible:CGRectMake((scrollWidth - pageWidth),0,pageWidth,pageHeight)];
//Enable paging and scrolling (scrollEnabled should be YES by default)
self.scrollView.pagingEnabled = YES;
self.scrollView.scrollEnabled = YES;
Best I can do based on your code - hope it helps

IOS Add subview on viewDidLoad

i have a problem when i'm trying to add subviews to a UIScrollView on viewDidLoad.
I'm using this code to programmatically add the UIImageViews to the scrollView:
- (void)viewDidLoad {
[super viewDidLoad];
NSInteger const_width = 100;
NSInteger numberOfViews = 4;
CGRect theFrame = [self.scrollView frame];
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * const_width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin,theFrame.origin.y,const_width,110)];
UIImage *image = [UIImage imageNamed:#"cocoloco.jpg"];
[imageView setImage:image];
//[self.scrollView addSubview:imageView];
imageView.tag = i;
CGRect rect = imageView.frame;
rect.size.height = 110;
rect.size.width = 110;
imageView.frame = rect;
[self.scrollView addSubview:imageView];
}
self.scrollView.contentSize = CGSizeMake(const_width * numberOfViews, 110);}
But i get the current view:
It seems that the scroll view frame takes its position regardless the 3 yellow tabs (that are a special TabBarController) so i get a wrong frame origin from the UIScrollView and therefore the UIImageViews are wrong positioned.
Any idea?
I don't know exactly how to do it, but add the height of the frame to the "Y" position of your rectangle. Something like this:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin,theFrame.origin.y + (theFrame.height),const_width,110)];
To get the scrollview below the navigation bar at the top, try
self.scrollview.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
Then position the table below the scrollview by setting it's origin base on the scrollview origin and height:
CGRect frame = tableView.frame;
frame.origin.y = self.scrollview.frame.origin.y + self.scrollview.frame.size.height;
tableView.frame = frame;
You should move any resizing or layout of your UI to the -(void)layoutSubviews method and this should sort your problem correctly.

Resources