Scroll content size not working in viewDidLayoutSubviews - ios

Am using xib to design my view using auto layout.Run time I want to change the content size of my scrollview in viewDidLayoutSubviews method but its not working.On orientation change the code written inside viewDidLayoutSubviews works but on load its not working.Please advice what could be the issue?
- (void)viewDidLayoutSubviews
{
scroll.contentSize = CGSizeMake(scroll.frame.size.width, attBtn.frame.origin.y+attBtn.frame.size.height+40);//atnBtn is added programatically
}

Usually when you want to have a view redraw you should call:
[self setNeedsDisplay: YES];
Granted I have never build on iOS, on OSX this code works every time. Also, for example, if you want your delegate to call a redraw for a view named someView:
[someView setNeedsDisplay: YES];

Related

In which function of my uiviewcontroller should I set position and size of programmatically created uiviews?

I have a view controller which I created with IB, It has a CollectionView that will load some data, during loading process I want to show an activity indicator. For that I set the center property on activityIndicator to be equal to the center property of collectionView.
the problem is that I don't know on which method of viewController should I do this. I tried it first inside viewDidLoad method but inside this method the frame of collectionView is equal to what it was inside IB and activityIndicator can't be set properly.
So my questions are :
I want to know where should I do this kind of positioning of UIView objects
When does ios sdk give the views the sizes they should have in respect of the device the app is running on not the IB.
What happens inside of viewController methods like viewDidLoad and viewWillLayoutSubviews when we call those methods on super object of viewController like [super viewDidLoad]
All drawing-related operations should be performed on your application’s main thread then you could create it in the viewDidLoad or any other delegate function.
Threading Considerations
Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.
dispatch_async(dispatch_get_main_queue(),{
// Call function here.
})
For Reference [UIView] :
(https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html)
1. I want to know where should I do this kind of positioning of UIView objects
If you're using interface builder, you should be positioning them in interface builder using autolayout, otherwise pretty much anywhere on the main thread is fine. Sometimes you may need to call [self.view setNeedsLayout] / [self.view setNeedsDisplay] depending on what time of change you've made, but ultimately for normal CGRectMake calls you're fine.
2. When does ios sdk give the views the sizes they should have in respect of the device the app is running on not the IB.
UIView has a method called layoutSubviews
Otherwise for VC look at viewWillLayoutSubviews and viewDidLayoutSubviews called either side of that
3. What happens inside of viewController methods like viewDidLoad and viewWillLayoutSubviews when we call those methods on super object of viewController like [super viewDidLoad]
In general for these methods you should call super at the top of the override to get whatever is happening underneath out of the way. Then proceed to work...
-(void)viewDidLoad
{
[super viewDidLoad];
UIView *myView = [[UIView alloc]initWithFrame:CGRectMake(0.0f, 0.0f, self.view.bounds.size.width, 100.0f)];
myView.backgroundColor = [UIColor redColor];
myView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:myView];
}
The above will work completely fine. No need for viewDidAppear. Look into auto layout and auto resizing. Personally I think working in code and not interface builder is better... but that's a personal choice.
You shoud use MBProgressHud the great library to show activity indicator. Just downlod this from github. drag and drop MBProgressHud.h and MBProgressHud.m in your project. Import .h file in your class.
then call , [MBProgressHUD showHUDAddedTo:self.view animated:YES];
and call when want to dissmiss or hide,
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
//using main thread to hide immediately
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
It will automatically maintain center and other kind of stuff. That's it. Hope this will help. :)

How to rotate parent view when child view orientation changes

I'm having a problem adjusting a parent view's layout when orientation changes for it's child view. I have a collection view controller that, when one of the cells are tapped, pushes a child view on top. If an orientation change occurs while the child view is visible and it is dismissed, the parent view's collection view cells haven't adjusted for the new width.
I should note that this works fine if the parent view is visible.
The only thing that has fixed this for me is in the viewDidAppear method of the parent view controller invalidates the collection view layout, but for me it's too late as the user sees the animation of the collection view cells snap into place.
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView reloadData];
}
I would have preferred to use viewWillAppear, but that doesn't seem to do anything. It sounds like it can only adjust the cells when the parent view is visible.
Is there a way around this?
Referring to this answer, iOS does not send orientation change events to offscreen view controllers, making them an unreliable way to determine whether the view has been resized.
viewWillAppear: isn't working in your case because iOS doesn't resize the offscreen view controller's view until after it calls the method, so your invalidate and reload are being pulled off the wrong values.
I believe the iOS8+ viewWillTransitionToSize:withTransitionCoordinator: method fires even when offscreen, but I'm not positive. In my experience, the size it provides does not reflect the actual size of the view. What I personally like to hook into is viewWillLayoutSubviews, usually guarded with a width check:
#property (nonatomic) CGFloat lastWidth;
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
if (self.lastWidth != CGRectGetWidth(self.view.bounds)) {
self.lastWidth = CGRectGetWidth(self.view.bounds);
// Update your collection view here.
}
}
This way, whenever your view is going to resize (on display, inside an orientation change animation) you can update the size information.
try overriding -(void)viewWillLayoutSubviews: method in your parent view controller. In your case,it goes like this
-(void)viewWillLayoutSubviews {
[self.collectionView.collectionViewLayout invalidateLayout];[self.collectionView reloadData];
}
You could try invalidating your layout and reloading in the rotation handler methods.
For pre-iOS 8
willRotateToInterfaceOrientation:duration:
And for iOS 8+
viewWillTransitionToSize:withTransitionCoordinator:

UIView Animation Fails

So, I want to do some basic animations of labels and later views.
I have a label, I'm trying to get it to move when a view loads, so I call the following method at the end of viewDidLoad:
- (void)animateView {
NSLog(#"animateView");
[UIView animateWithDuration:20 animations:^{
// set new position of label which it will animate to
self.dcFirstRunDaysLabel.frame = CGRectMake(20,320,280,215);
}];
}
Instead of animating, the label appears in position.
I've tried every tutorial and read through the docs. I get no errors.
Any thoughts?
Cheers.
Try calling your animateView method in viewDidAppear. Because in viewDidLoad your view isn't visible yet.
viewDidLoad:
Called after the controller’s view is loaded into memory.
viewDidAppear:
Notifies the view controller that its view was added to a view hierarchy.

UIViewController viewDidLoad incorrect width/height

Everybody knows that you can't trust the frame size on a UIViewController init/viewDidLoad method; this:
- (void)viewDidLoad: {
NSLog(#"%d", self.view.frame.size.width);
}
will print wrong sizes in many occasions (in particular it's pretty much broken in landscape mode)
This will actually return always corrected results so it's good to layout the subviews:
- (void)viewWillAppear: {
NSLog(#"%d", self.view.frame.size.width);
}
The problem is that viewWillAppears gets called every time the view appears, so it's not suitable to alloc or add subviews. So you end up declaring every single view in the interface and you end up with huge header files that I don't like at all since most of the items don't need any more manipulations after the initial setup.
So question one is: Is there a better way to handle subviews positioning?
Question two is very related, let's say I have a subclass of UIView including various others subviews. I declare it inside my interface, and i alloc/init it in my init/viewDidLoad method.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
...
menu = [[SNKSlidingMenu alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
...
}
As we already know we now need to reposition it in viewWillAppear to get a more accurate reading
- (void)viewWillAppear:(BOOL)animated{
....
menu.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
....
}
The problem is that of course all the subviews needs to be repositioned as well. This is done by the layoutSubviews function that get called automatically, but we got the same problem: All the subviews need to be declared inside the interface of the SNKSlidingMenu class.. Is there a way around this?
Thanks.
If you are targetting iOS 5.0 or better you can use viewWillLayoutSubviews and viewDidLayoutSubviews to make changes.
As for your second question, if you need access to an instance variable in other method than init, you need to keep it around, I don't see a problem with it.
You can, however, try to use Auto Layouts and set up rules between the subviews so it's automatically laid out for you without the need to keep a reference.
viewDidLoad only gets called when your view is created, but lots of things can affect the frame's size, and it doesn't get called again when frame changes.
Instead:
create subviews in viewDidLoad
set their sizes in
viewWillLayoutSubviews.
See some additional discussion here for handling rotation: https://stackoverflow.com/a/16421170/1445366
viewWillLayoutSubviews and viewDidLayoutSubviews can resolve this problem.
But the two methed would be performed more times.
this is my code to get correct self.view.frame.
- (void)viewDidLoad {
[super viewDidLoad];
...
dispatch_async(dispatch_get_main_queue(), ^{
// init you view and set it`s frame. this can get correct frame.
...
}
...
}
This saved my life more than once (Swift 4):
override func viewDidLoad() {
super.viewDidLoad()
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
Basically this forces the viewcontroller to correctly layout it's view and from there you can get the correct frames for all your subviews. This particularly helps when doing transition animations and your view controllers are using autolayout and interface builder.
From what I've noticed it looks like the initial frames are set to whatever your interface builder's default size class is set to. I normally edit using the iPhone XS size class so in viewDidLoad it seems that the view's width is always 375 regardless whether you are using an iPhone XR or not. This corrects itself before viewWillAppear though.
The above code will correct this issue and allow you to get the correct frames for your view / subviews before the view controller is rendered to the screen.

UISearchDisplayController change dimmed view frame

In my iPad app I have an UITableView. Table's frame size is less than screen size, so to make search functionality look nice I have to adjust searchResultTableView's frame to fit my TableView. I'm doing it in my UISearchDisplayDelegate's -searchDisplayController:willShowSearchResultsTableView: method.
Everything works fine except dimming view. When I'm starting search dimming view's width is equal to screen width:
When I start entering search string or clear textfield my searchResultsTableView resizes properly and everything works as it should:
I tried to change searchResultsTableView frame inside -searchDisplayControllerWillBeginSearch: method using this line
controller.searchResultsTableView.frame = myFrame;
but it doesn't work as well. Any suggestions besides implementing my own search display controller?
I also needed to change the frame of the dimming view but for a different reason. In my case I created a UISearchDisplayController and UISearchBar programmatically in a regular UIViewController not a UITableViewController. I was also using MFSideMenu which added to the complexity of the problem. What ended up happening was the dimming view was in the correct position initially but the next time the search was cleared the dimming view shifted leftwards and upwards by exactly half of it's size. Given the UISearchDisplayController you can find the dimming view like so.
for(UIView * v in controller.searchContentsController.view.subviews)
{
if([v isMemberOfClass:[UIControl class]])
{
v.frame = newFrame; //This frame should account for the UISearchBar
}
}
To handle for the initial incorrect frame you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView;
To handle for an incorrect frame on any subsequent clears you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView;
Note: this solution runs through the subviews of the searchContentsController which is one of the reasons I used isMemberOfClass instead of isKindOfClass (UIButton is a subclass of UIControl). Further discrimination would be required if you added a UIControl instance into your view (you could use tags to help determine which ones are yours).

Resources