Center UITableView within a subView - ios

I'm ultimately trying to vertically center my table view cells within a UITableView if the total amount of cells * row height < the height of the tableView
My problem is that whenever I try to reference the tableview height (which should be resized when added to its superview. It returns original height of the view as defined in Interface Builder.
Here is the method that adds the subView with the tableView:
- (void)addMissonListView {
missionView = [BSMissionListView loadMissionView:YES];
BSList *list = (BSList *)self.dataModel;
missionView.missionList = list.arrayMissions;
missionView.delegate = self;
missionView.heightofSuperView = [NSNumber numberWithFloat:viewBottomContainer.frame.size.height];
[viewBottomContainer addSubview:missionView];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[BSUtility setConstraintsOnView:viewBottomContainer relativeTo:missionView withConstant:0];
}
This is the setsConstraintsOnView method that's called:
+ (void)setConstraintsOnView:(UIView *)superView relativeTo:(UIView *)subView withConstant:(int)constant
{
[subView setTranslatesAutoresizingMaskIntoConstraints:NO];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeBottom multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeTop multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeRight multiplier:1 constant:constant]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeLeft multiplier:1 constant:constant]];
}
The subview consists of a tableView inside of UIView. In Interface Builder, the size of the UIView is 320 X 586 and the tableView is 310 x 558.
viewBottomContainer's size in IB is 320 x 408
I'm trying to get the information about the view's and tableView's height in the configureGUI method. And once that works, I'll pass it to another method to create a contentOffset in the TableView. but the NSLog's keep returning the original values, not the resized values.
These are the methods from the BSMissionListView class:
- (void)awakeFromNib {
[super awakeFromNib];
[self configureGUI];
}
- (void)configureGUI {
[super configureGUI];
NSLog(#"Bounds height of the View is %f",self.bounds.size.height);
NSLog(#"Bounds height of the tableView is %f",self.tableViewMissionList.bounds.size.height);
[self setBackgroundColor:[UIColor clearColor]];
}
I'll be happy to post more code if that will be helpful. Thank you!

I was able to solve the problem by passing in the tableView's superView's height by ways of the method that the loaded the tableView nib.
-I added a CGFloat variable to the method that loads the nib in the BSMissionListView Class
-I passed in the viewBottomContainer's height and then used that to set the heightOfSuperView value in the newly modified method.
-heightOfSuperView's value is used to calculate to contentInset of the TableView to center it within the superView
Thank you to #Alex Renyolds for helping me out.
- (void)addMissonListView {
[viewBottomContainer setNeedsLayout];
[viewBottomContainer layoutIfNeeded];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLog(#"Height of bottomViewContrainer: %f", viewBottomContainer.bounds.size.height);
BSList *list = (BSList *)self.dataModel;
missionView = [BSMissionListView loadMissionView:YES heightOfSuperView:viewBottomContainer.bounds.size.height listArray:list.arrayMissions];
missionView.delegate = self;
[viewBottomContainer addSubview:missionView];
[viewBottomContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[BSUtility setConstraintsOnView:viewBottomContainer relativeTo:missionView withConstant:0];
}
BSMissionList
+ (BSMissionListView *)loadMissionView:(BOOL)isEditable heightOfSuperView:(CGFloat)height listArray:(NSMutableArray *)list {
BSMissionListView *missionListView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([BSMissionListView class]) owner:nil options:nil] lastObject];
missionListView.missionList = list;
missionListView.isEditable = isEditable;
missionListView.heightofSuperView = (height- 49);
[missionListView configureGUI];
return missionListView;
}

Related

UIStackView: Loading views from xib and updating height constraint of subView did not reflecting any changes?

I have the following hierarchy in my application
- UIScrollView
- UIStackView
- UIView 1 // load with xib and added in arrangedSubviews
- UIScrollView 1.1 // horizontal scrolling, fixed height constraint 38
- UIView 1.2 // called it childView. has fixed height 0 (I load the view from xib and add it here dynamically and update its height)
- UIView 1.2.1 // called it New View
- UIView 2
- UIView 3
So my problem is when I have loaded a view from xib and added it to UIView1.2 also increased height constraint 0 to a height of newly added sub-view but nothing will happen.UIView1.2height did not update expectedly .
self.constraintChildViewHeight.constant = 95;
[self layoutIfNeeded];
NewView *newView = (NewView *)[[[NSBundle mainBundle]loadNibNamed:NSStringFromClass([FieldPhotoView class]) owner:self options:nil]objectAtIndex:0];
[newView setTranslatesAutoresizingMaskIntoConstraints:false];
[self.childView addSubview:newView];
[self applyConstraintsToParent:self.childView andSubView:newView];
Method
- (void)applyConstraintsToParent:(UIView *)parentView andSubView:(UIView *)subView {
//constraints
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
[parentView addConstraint:leading];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
[parentView addConstraint:trailing];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[parentView addConstraint:top];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8];
[parentView addConstraint:bottom];
NSLayoutConstraint *equalWidth = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];
[parentView addConstraint:equalWidth];
leading.active = true;
trailing.active = true;
top.active = true;
bottom.active = true;
equalWidth.active = true;
}
#Edit1 - Child view constraints
#Edit2 - For better understanding, I want to achieve this functionality programmatically using xib's(In UIStoryBoard is just working fine.)
From your question what i understood is that you are not able to scroll your content in the scrollview 1.1.
Try the following steps :
For the scrollview 1.1
give top, bottom,leading, trailing constraints w.r.t View1.
give scrollview height constraint = 38
For the childview UIView 1.2
give top, bottom,leading, trailing constraints w.r.t scrollview1.1
pin childview w.r.t View1 for leading & trailing edges
give childview height constraints
give childview vertically center constraint.
For the newView UIView 1.2.1
Load view from nib.
Add it to the childview
Set its constraints - top, bottom, leading & trailing w.r.t childview.
This will make your content scrollable.
I have shared a sample project here: https://github.com/Abhie87/StackExchangeSample
Hope this will be helpful.
Tried to do the same and it worked as expected.
What I did:
Initial views hierarchy looks like this:
Stack view constraints are on the next image:
Each view in stack view has only its' height set by constraint (the button in last view I use to add/remove the new view to the stack view at index 0)
The view to add is loaded from .xib file called SomeView.xib view hierarchy and constraints for which are on the next image (constraint called View Height Constraint is set to 1 and is changed to 95 when SomeView is added to the stack view):
The function that is called when the button is tapped looks like this:
- (IBAction)buttonTap:(UIButton *)sender {
if (self.theView) {
[self.stackView removeArrangedSubview:self.theView];
[self.theView removeFromSuperview];
self.theView = nil;
} else {
self.theView = [[[NSBundle mainBundle] loadNibNamed:#"SomeView" owner:nil options:nil] firstObject];
self.theView.translatesAutoresizingMaskIntoConstraints = NO;
[self.stackView addSubview:self.theView];
[self.stackView insertArrangedSubview:self.theView atIndex:0];
self.theView.viewHeightConstraint.constant = 95;
}
}
Hope this will give you any suggestions in how to fix your situation

Using a custom UITableView containing an image view cell with UITableViewAutomaticDimension

In my new app, I am trying to adopt AutoLayout throughout. In one of my table views, I have an image view and a label. If I do this in my controller's viewDidLoad: method
self.tableView.rowHeight = UITableViewAutomaticDimension;
the row is not high enough to accommodate the image. (I have added fixed width and height constraints to the image view).
If I remove the statement, then the height of the cell is the height as set in IB.
Is there a method I have to implement in my UITableCell class to tell the AutoLayout system my minimum height requirement? Or did I do something wrong?
Well, I could never figure this out using IB. However, I did get success adding the constraints manually, so I thought it worth sharing my complete solution. (Remember: an image view, a label, dynamic text). I want my image to always be 80 by 80, no matter how big the content gets.
Enjoy.
-(id)initWithCoder:(NSCoder *)aDecoder {
if ( !(self = [super initWithCoder:aDecoder]) ) return nil;
// TODO: Add an imageview and populate it
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,300,20)];
_titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
_titleLabel.numberOfLines = 0;
[self.contentView addSubview:_titleLabel];
_albumArtImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
[_albumArtImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:_albumArtImageView];
NSMutableArray* constraints = [NSMutableArray new];
UIView* contentView = self.contentView;
[_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
// _titleLabel to contentView
[constraints addObject:[NSLayoutConstraint
constraintWithItem:_titleLabel
attribute:NSLayoutAttributeFirstBaseline
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeTop
// as font grow, space from top grows
multiplier:1.8
constant:30]];
// similarly for bottom
[constraints addObject:[NSLayoutConstraint
constraintWithItem:contentView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:_titleLabel
attribute:NSLayoutAttributeLastBaseline
multiplier:1.3
constant:8]];
[constraints addObject:[NSLayoutConstraint
constraintWithItem:contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:nil
attribute:0
multiplier:1.0
constant:80]];
// Now the imageView
[constraints addObject:[NSLayoutConstraint
constraintWithItem:_albumArtImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:80]];
[constraints addObject:[NSLayoutConstraint
constraintWithItem:_albumArtImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:80]];
// for horizontal
[constraints addObjectsFromArray:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-2-[_albumArtImageView]-5-[_titleLabel]-15-|"
options:0 metrics:nil views:NSDictionaryOfVariableBindings(_titleLabel,_albumArtImageView) ]];
[self.contentView addConstraints:constraints];
return self;
}

Add subview to UITextView and using autolayout

I have problem with adding subview to UITextView using autoLayout:
in SubTextView : UITextView .m file:
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
_baseline.translatesAutoresizingMaskIntoConstraints = NO;
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints
{
[super updateConstraints];
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
But, this do not work... the self.baseline's frame is always (0, 0, 0, 0)...
Anyone can help?
Edited:
I set a break point at layout subviews, at that time, when layoutSubviews the subviews are inited, and the UITextView is :
<InventoryOverviewListingNameTextView: 0x7fd92b2a1800;
baseClass = UITextView;
frame = (234 36; 399 70.2);
text = '11111111';
clipsToBounds = YES;
gestureRecognizers = <NSArray: 0x7fd92cbd8ca0>;
layer = <CALayer: 0x7fd92cb08900>;
contentOffset: {-0, 10};
contentSize: {399, 70.199997425079346}>
But the frame of the baseline is:
<UIView: 0x7fd92cb90710;
frame = (0 -10; 399 0.5);
layer = <CALayer: 0x7fd92cb907e0>>.
Do not know how to make the y = -10 to y = 69.7.
I feel it is a little like scrollView, to which I solve by add a UIView as contentView on UIScrollView, and UIScrollView itself is added on another UIView. Then I set the contentView's top to scrollView's top, contentView's heigh, and !!!(this is important or autoLayout won't work properly)
contentView's left and right to UIView rather than scrollView.
But I do not see it works for UITextView...
Anyone can help?
The frame will always be (0,0,0,0) because that is the default one that comes whit [[UIView alloc] init].
Have you tried defining the view setting a frame?
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
//Add a frame, does not matter the size
_baseline = [[UIView alloc] initWithFrame:CGRectMake(0,0,20,20)];
//Just to make sure it's there let us give it a color
[_baseline setBackgroundColor:[UIColor green]];
[self addSubview:_baseline];
//Don't run this line just yet.
//[self setNeedsUpdateConstraints];
}
}
If that works, then we found the problem, if it does not, maybe the problem is on the - (void)updateConstraints method.
UPDATE:
So, I sat down and ran your code, I got a few crashes but eventually I managed to make the black box apear inside the text view. I found that the box was not instantiated when the view was looking to layout its subviews, so I tinkered around and here goes, this works, it throws a few constraints warnings but I guess you can work those by yourself.
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame]) {
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
if (!_baseline) {
_baseline = [[UIView alloc] init];
[self addSubview:_baseline];
[self setBackgroundColor: [UIColor blackColor]];
[self setNeedsUpdateConstraints];
}
}
- (void)updateConstraints {
[super updateConstraints];
if (_baseline) {
NSLayoutConstraint *constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTrailing
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.baseline
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[self addConstraint:constraint];
}
}

Update UITableView size based on UIView constraints

I have a UIView with a UITableView inside.
In a UIViewController, I instantiate the UIView like this:
viewMain = [ViewMain alloc]initwithframe:cgrectmake(0,0,200,500)];
Inside this view, in the method initWithFrame:(CGRect)frame, I instantiate the UITableView like this:
_tableView = [UITableView alloc]iniWithFrame:CGRectMake(0,0,frame.size.width,frame.size.height)];
The problem is when I apply constraints to viewMain, how can I update de size of the tableView based on the viewMain layout constraints?
you would need to add constraints to the childview (i.e. autolayout itself if the superview changes), like so:
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
[_tableView addConstraint:left];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[_tableView addConstraint:top];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
[_tableView addConstraint:width];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:viewMain attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_tableView attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
[_tableView addConstraint:height];
Not sure if you also need to do this:
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
before adding the constraints.
If you are setting table view with frame then you will not able to update layout using constraints. If you want to update your table view according to your parent view, use constraints from storyboard to update constraints.
Basically you have to call -setNeedUpdateConstraintsand -updateConstraintsIfNeeded on the cell in -tableView:cellForRowAtIndexPath:.
See a very detailed explanation and sample code in this answer.
Here is a great Tutorial by Ray Wenderlich.
You can also try this in your tableViewController:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (IS_IOS8_OR_ABOVE) {
return UITableViewAutomaticDimension;
}
//self.prototypeCell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.prototypeCell.bounds));
[self configureCell:self.prototypeCell forRowAtIndexPath:indexPath];
[self.prototypeCell updateConstraintsIfNeeded];
[self.prototypeCell layoutIfNeeded];
return [self.prototypeCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
I would set the tableView's frame equal to the mainView's frame.
i.e.:
_tableView = [UITableView alloc]iniWithFrame:CGRectMake(mainView.frame.origin.x,mainView.frame.origin.y,mainView.frame.size.width,mainView.frame.size.height)];

iOS - Pure AutoLayout and UIScrollView not scrolling

This is my first time using UIScrollViews with a pure Autolayout approach. This is what the view hierarchy looks like
view
-scrollview
--view1
--view2
--view3
scrollview should contain view1|view2|view3 in that order.
I set the scrollviews width, height, centerx and bottom space to superview. The view1, view2 and view3 that are created all have their width and height constraints setup in their updateConstraints method. Additionally, some constraints are provided in code. What is the reason this scrollview is not scrolling from left to right? I have read literally all of the guides I can find online about creating and adding subviews to a UIScrollView programmatically with auto layout. I found some mention about having to provide four different constraints, leading, trailing, top and bottom for each view added as a subview to the scrollview. Are these the only NSLayoutAttributes that one can specify? How do attributes such as NSLayoutAttribueLeft or NSLayoutAttribueRight relate? I have read documentation on Apples website as well, specifically https://developer.apple.com/library/ios/technotes/tn2154/_index.html. I am attaching the setup I currently have. Everything is done via code.
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = #[ [[PCCGenericRating alloc] initWithTitle:#"Easiness"
andMessage:#"WHAT A JOKERRRR"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]],
[[PCCGenericRating alloc] initWithTitle:#"Joker"
andMessage:#"WHAT A JOKERRRR"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]],
[[PCCGenericRating alloc] initWithTitle:#"Difficulty"
andMessage:#"YOu are not difficult at all"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]]
];
[self initView];
}
- (void)initView {
CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
CGFloat heightDifference = navigationBarHeight + statusBarHeight;
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.delegate = self;
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.scrollView];
//setup constraints
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0f
constant:-heightDifference]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.0]];
[self.dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PCCGenericRating *rating = (PCCGenericRating *)obj;
PCCGenericRatingView *ratingView = [self createViewWithRating:rating];
[self.scrollView addSubview:ratingView];
int multiplier = (idx == 0) ? 1 : (int) (idx + 1) ;
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:ratingView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:multiplier
constant:0.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:ratingView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.0f]];
}];
}
- (PCCGenericRatingView *)createViewWithRating:(PCCGenericRating *)rating {
PCCGenericRatingView *view = [PCCGenericRatingView genericRatingViewWithTitle:rating.title andMessage:rating.message];
return view;
}
Upon printing out the scrollview constraints, they look okay to me:
po self.scrollView.constraints
<__NSArrayM 0x115b051f0>(
<NSLayoutConstraint:0x1145d9290 PCCGenericRatingView:0x114579880.centerX == UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145d9410 PCCGenericRatingView:0x114579880.centerY == UIScrollView:0x11458d4b0.centerY>,
<NSLayoutConstraint:0x1145d9dd0 PCCGenericRatingView:0x1145d9560.centerX == 2*UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145d9e40 PCCGenericRatingView:0x1145d9560.centerY == UIScrollView:0x11458d4b0.centerY>,
<NSLayoutConstraint:0x1145da6b0 PCCGenericRatingView:0x1145d9e90.centerX == 3*UIScrollView:0x11458d4b0.centerX>,
<NSLayoutConstraint:0x1145da730 PCCGenericRatingView:0x1145d9e90.centerY == UIScrollView:0x11458d4b0.centerY>
)
Here is a screenshot of what it looks like:
I find it odd that the last element in the datasource is the first view controller showing up in the scrollview, when it should be the last view. It also doesn't scroll left to right as it should.
Make sure your top_constraint for the view1 and bottom_constraint for view3 will be as per your scrollView's constraints. Otherwise scrollview's contentSize: {0, 0}.
Wherever you are printing your constraints, try printing scrollview.contentSize, it will likely be 0,0 and that is where your problem is. As far as I know, and as you mentioned in your post, you have to explicitly set the subviews of a scrollview to the scrollviews top bottom left and right constraints. Setting these automatically sets the contentSize of the scrollview which will enable it to scroll. It looks like you are only setting centerX and centerY constraints which will not set the scrollviews contentSize to what you need.
Try setting these programatically (this is pseudocode but you get the idea):
view1.topConstraint = scrollView.topConstraint
view1.leftConstraint = scrollView.leftConstraint
view3.bottomConstraint = scrollView.bottomConstraint
view3.rightConstraint = scrollView.rightConstraint
If you set all of those correctly, your scrollview will scroll properly. Just remember to check the contentsize, and if the contentsize is 0,0 then your constraints aren't properly set up.

Resources