I have a xib with a View, I changed the View class to be UIScrollView.
Now I have set contentSize to some big number (2000, 2000).
Here is my code:
This allows talking to my scrollview without casting every time:
- (UIScrollView *)scrollView {
return (UIScrollView *)self.view;
}
This I do in viewDidLoad:
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, 2600);
self.scrollView.scrollEnabled = YES;
I know viewDidLoad is getting called fine. But the scrollView is not reacting. I don't have any touch responders or anything that can absorb touches.
For this, first set delegate of scrollView inside your xib files then create an IBIoutlet for your scrollview. Remind that scrollView should connect with its refrencing outlet then Assign a propertyy as Strong to your scrollView.
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
and synthesize it.
#synthesize scrollView;
then try to set contentSize.
scrollView.delegate=self;
scrollView.contentSize = CGSizeMake(self.view.frame.size.width, 2600);
scrollView.scrollEnabled = YES;
I hope it works for you.
I have found a problem, I was setting superview too small.
Related
I have a UIView and inside this UIView I have another UIView, lets say parent and child UIView. I have set height and width of parent UIView to 400 in storyboard and set child view constraint to take 8px margin from top, left right and bottom from its superview.
But When I change size of parent view to 200, size of child view remain same. I have tried this in both viewdidload and viewdidappear
CGRect frm = self.mainTimerView.frame;
frm.size.width = size;
frm.size.height = size;
self.mainTimerView.frame = frm;
when I change parent view to 200 child should set it self to 200-16 height and width according to constraints.
You should not mix using uiconstraint with using frame.If you want to change the size when using uiconstraint, you should make the outlet of the constraint, and then change the constraint's constant property.Call layoutIfNeeded,then you can get the right frame.
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIButton *mainTimerView;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainTimerViewHeightConstraint;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view layoutIfNeeded];
NSLog(#"%#",#(self.mainTimerView.frame.size.width));
self.mainTimerViewHeightConstraint.constant = 100;
[self.view layoutIfNeeded];
NSLog(#"%#",#(self.mainTimerView.frame.size.height));
}
#end
Calling layoutIfNeeded aim to to force the layout of subviews before drawing, then viewDidLayoutSubviews will be called. Note that you can get the correct frame in viewDidLayoutSubviews. In other words, you can get the right frame after viewDidLayoutSubviews has been called.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(#"%#",NSStringFromCGRect(self.mainTimerView.frame));
}
1) Change the height constraint of the mainview.
2) Call the layoutIfNeeded method.
3) Do it on viewDidAppear.
Have you tried calling setNeedsLayout on the parent view? This call is used to ask a view to layout its subviews. From the setNeedsLayout's discussion section:
Call this method on your application’s main thread when you want to adjust the layout of a view’s subviews. This method makes a note of the request and returns immediately. Because this method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated. This behavior allows you to consolidate all of your layout updates to one update cycle, which is usually better for performance.
Calling layoutIfNeeded might be another option. But according to the docs, it forces an immediate layout, and therefore does not provide the consolidation benefit gained by using setNeedsLayout.
layoutIfNeeded documention:
Lays out the subviews immediately.
Plz create the IBOutlet of height Constraint of parent view.
IBOutlet NSLayoutConstraint *RedViewHeightconstraint;
- (void)viewWillAppear:(BOOL)animated
{
RedViewHeightconstraint.constant = 500;
}
it will work for you
You just need to create IBOutlets of constraints of height and width of mainTimerView. Once you do so add these lines of code :
constraintOuterViewHt.constant = 200
constraintOuterViewWidth.constant = 200
You don't need to do anything else.
Check screen shot here
I'm using PureLayout to implement AutoLayout of subviews in a UIView. But I don't know the best practice of organizing the code.
Should I put the AutoLayout related code in the init of the UIView, or the overridden methods such as updateConstraints and layoutSubviews?
For example, I want to create a subclass of UIView called PHView, and for any phview, there is a subview called centerView, it is always at the center of phview, and width/height is 0.3*phview's width/height.
https://www.dropbox.com/s/jaljggnymxliu1e/IMG_3178.jpg
#import "PHView.h"
#import "Masonry.h"
#interface PHView()
#property (nonatomic, assign) BOOL didUpdateConstraints;
#property (nonatomic, strong) UIView *centerView;
#end
#implementation PHView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor redColor];
self.translatesAutoresizingMaskIntoConstraints = NO;
}
return self;
}
- (UIView *)centerView {
if (!_centerView) {
_centerView = [UIView new];
_centerView.backgroundColor = [UIColor yellowColor];
[self addSubview:_centerView];
}
return _centerView;
}
-(void)updateConstraints {
if (!_didUpdateConstraints) {
_didUpdateConstraints = YES;
[self.centerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.mas_centerX);
make.centerY.equalTo(self.mas_centerY);
make.width.equalTo(self.mas_width).multipliedBy(0.3);
make.height.equalTo(self.mas_height).multipliedBy(0.3);
}];
}
[super updateConstraints];
}
#end
'didUpdateConstraints' aims to indicate you have added constraints, so you will only add constraints once.
in UIViewController:make phview top bottom left right 20 to the margin.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
PHView *myView = [PHView new];
[self.view addSubview:myView];
[myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(20, 20, 20, 20));
}];
}
You should add constraints when you are sure that view has been added to its superview. Basically, you should do it in superview's class any point after addSubview: is called.
To answer your questions:
1- in init methods, can you be sure of that view has been added as subview to a superview? it wouldn't be safe to assume that. maybe you can add constraints in init method of superview
2- layoutSubviews is in where autolayout code actually works. you can't add constraints in layoutSubviews. already playing with autolayout constraints are not cheap, therefore you should add/remove them as few as possible, doing so in a method that is called multiple times (i.e. layoutSubviews) is not the best practice.
Mechanism of autolayout is going to inner view from outer view, so subviews do not actually concern about constraints. it is superview's responsibility
Hope this helps you by understanding controller’s view hierarchy
How View Controllers Participate in the View Layout Process
The view controller’s view is resized to the new size.
If autolayout is not in use, the views are resized according to their autoresizing masks.
The view controller’s viewWillLayoutSubviews method is called.
The view’s layoutSubviews method is called. If autolayout is used to configure the view hierarchy, it updates the layout constraints by executing the following steps:
a.The view controller’s updateViewConstraints method is called.
b.The UIViewController class’s implementation of the updateViewConstraints method calls the view’s updateConstraints method.
c. After the layout constraints are updated, a new layout is calculated and the views are repositioned.
The view controller’s viewDidLayoutSubviews method is called.
Please refer this for more details
I am facing an issue while using storyboard auto layout with UIScrollView. I am updating a constraint of a UIScrollView, and after that setting the content size of scrollview. But UIScrollView constraint takes a bit of time to update and initially UIScrollView is not able to scroll because of more height than a view. I can not change the current implementation.
Is there any way, notification or any delegate method to check that the constraints are updated, so that I can do further changes?
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *scrollViewHieghtConstraint;
self.scrollViewHieghtConstraint.constant = 500;
[self.scrollView updateConstraints];
[self.scrollView setContentSize:CGSizeMake(0, 1000)];
You can use - (void)updateViewConstraints selector of UIViewController.
You can also use - (void)updateConstraints selector of UIView if you have extended said view.
You can't call -updateConstraints directly. You have to mark the view's constraints as dirty and iOS will call that method on the next update cycle.
On a UIViewController's implementation, add this when you need the constraint to change:
self.scrollViewHieghtConstraint.constant = 500;
[self.view setNeedsLayout];
I'm assuming that UIScrollView is a subview of the UIViewController's view. If it's not, then call -setNeedsLayout on the scrollview's parent view.
And if you need to know when the subviews are already on their right place, you have a callback for UIViewController, called -viewDidLayoutSubviews
This is my view layout in storybaord:
My Scroll view frame is set to be 320x568, and in my view controller I have set
self.scrollView.scrollEnabled = YES;
[self.scrollView setContentSize:CGSizeMake(320,1000)];//obviously content is larger than frame
However, the scroll still doesn't work. So I enabled vertical bounce, and found that every time when I drag down it just bounces back to original position. Can anyone help me fix this issue?
First turn off AutoLayout - click on scrollview and uncheck Use AutoLayout
In your .h file add the UIScrollViewDelegate for example:
#interface myViewController : UIViewController <UIScrollViewDelegate>
In your .m file in viewDidLoad set
self.myScrollView.delegate = self;
self.myscrollView.scrollEnabled = YES;
self.myscrollView.contentSize=CGSizeMake(320.0, 1094.0);
keep Bounces ON and set scrollview frame 320 X 960
In your .h file add the UIScrollViewDelegate for example:
#interface myViewController : UIViewController <UIScrollViewDelegate>
In your .m file in viewDidLoad set
self.myScrollView.delegate = self;
If you have one subview in your scrollview, as you do, the scrollview will scroll to the dimensions of that view. See the answer here for details.
view.userInteractionEnabled = YES
I am currently trying to program a game with a HP gauge represented by an UIImageView placed on a .xib file. I declared my IBOutlet as follows:
#property (weak, nonatomic) IBOutlet UIImageView *hpGaugeView;
and synthesized accordingly. It is also linked to the UIImageView instance in the xib.
In the xib, I set the length of hpGaugeView to 188 and height to 9.
Now this is what I did in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[hpGaugeView setFrame:CGRectMake(49, 18, 123, 9)];
NSLog(#"%f, %f", [hpGaugeView frame].size.width, [hpGaugeView frame].size.height);
}
Although NSLog tells me the hpGaugeView's width is 123, when I run it the length appears unchanged compared to the one in xib. All the other setFrame: or setBound: worked fine in other viewControllers, so I'm just wondering what's happening here.
It is a small but crucial problem that is bugging me for a few hours straight...
Edit:
When I turn off AutoLayout, the resizing worked; it creates a bunch of other problems though...
With autolayout on, the subviews have not been laid during the viewDidLoad. The constraints are calculated a bit later and you can override the viewDidLayoutSubviews method to set your frame:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Do any additional setup after subviews are laid.
[hpGaugeView setFrame:CGRectMake(49, 18, 123, 9)];
NSLog(#"%f, %f", [hpGaugeView frame].size.width, [hpGaugeView frame].size.height);
}
From the class reference:
viewDidLayoutSubviews
Notifies the view controller that its view just laid out its subviews.
I believe the problem is that you're not changing the frame of the UIImageView. The bounds refer to the position and size of the view in it's own coordinate system whereas the frame refers to the position and size in it's superview coordinate system.
If the clipsToBounds property is set yo YES, then nothing outside the frame of your view will be visible.
It turns out it is some weird weird interaction between the nib and the code...when I created the UIImageView programmatically it worked fine.
AutoLayout property worked only above IOS 6.0 only . Dragging of control to XIB instead of you can create the UIImageView through Code and add to current view. I have given the code for adding UIImageView programmatically.
Source Code:
// .h file
#property (weak, nonatomic) UIImageView *hpGaugeView;
//.m file
- (void)viewDidLoad
{
[super viewDidLoad];
hpGaugeView = [[UIImageView alloc] init];
[hpGaugeView setFrame:CGRectMake(49, 18, 123, 9)];
[self.view addSubview:hpGaugeView];
}
Initialize and add the hpGaugeView to subview inside the viewDidLoad function through programmatically.