in UIScrollView setContentSize working only one - ios

In my application, I have some information in UIScrollView.
Content in UIScrollView can be changed by filter.
First time, when ViewController loads, I put all items in ScrollView, and everything working perfect.
If user wants to apply some filter to ScrollView, I remove all old items, make new, and set a new setContentSize:
- (void) displayCars:(NSMutableArray*)cars {
NSArray *viewsToRemove = [scrollView subviews];
for (UIView *v in viewsToRemove) [v removeFromSuperview];
int itemHeight = 100;
CGFloat scrollViewHeight = cars.count * itemHeight;
if (scrollViewHeight < scrollView.frame.size.height) scrollViewHeight = scrollView.frame.size.height;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, scrollViewHeight)];
for (int i=0; i < cars.count; i++) {
VitoCarsAdv * adv = [cars objectAtIndex:i];
VIOneCar *carView = [[VIOneCar alloc] init];
[carView setFrame:CGRectMake (0, (i * itemHeight), scrollView.frame.size.width, itemHeight)];
[carView setData:adv];
[scrollView addSubview:carView];
}
}
ScrollView is filling up by new items, but the contentSize is the same, like in previous situation, where was all items inside. Actually, it is not changed second time.
It is not comfortable behaviour for user, can somebody advice me, where I should look?
Please note, that Autolayout is swithed off for this View Controller.
Thanks.

You're doing it right I think you just mistyped some code. Here's what you wrote:
int itemHeight = 100;
CGFloat scrollViewHeight = cars.count * itemHeight;
if (scrollViewHeight < scrollView.frame.size.height) {
scrollViewHeight = scrollView.frame.size.height;
}
With that code, the scrollViewHeight will never shrink to a smaller size. You are setting a minimum size of whatever the scrollView.frame.size.height currently is. Is that your intended function? If not, it you should just remove the if statement leaving you with:
CGFloat scrollViewHeight = cars.count * itemHeight;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, scrollViewHeight)];

Related

iOS: UIScrollView leaving space on bottom, How can I have space on top

I have scrollView in which subview are getting added from bottom, like adding bricks in wall (in my case only one brick), then next brick on top of that, like that so on. As with default UIScrollView ContentSize, Scrolling will happen on top. How can I make scrolling to bottom. If I scroll down I should be able to see other bricks which are added one above the other.
As you can see in below screen shots, start button should go down instead of of up.
Any kind of help be very appreciated.
Thanks
Sample Code:
CGFloat initialYPoint = 30;
for (OUSTLevel* currentLevel in self.course.sortedLevelsList) {
NSArray* nibList = [[UINib nibWithNibName:#"OUSTLearningMapPlayLevelCell" bundle:nil] instantiateWithOwner:self options:nil];
OUSTLearningMapPlayLevelCell* playLevelCell = nil;
for (UIView* childView in nibList) {
if ([childView isKindOfClass:[OUSTLearningMapPlayLevelCell class]]) {
playLevelCell = (OUSTLearningMapPlayLevelCell*)childView;
break;
}
}
CGFloat cellHeight = CGRectGetHeight(playLevelCell.frame);
CGFloat yPoint = CGRectGetHeight(self.scrollview.frame) - (cellHeight + initialYPoint);
CGRect cellFrame = CGRectMake(0, yPoint, viewWidth, cellHeight);
[playLevelCell setFrame:cellFrame];
[self.scrollview addSubview:playLevelCell];
initialYPoint += cellHeight;
if (!landingNodeLevelData.locked) {
[playLevelCell.unlockedView setHidden:NO];
[playLevelCell.lockedView setHidden:YES];
// Frame calculation for placing pin
CGFloat centerPoint = playLevelCell.unlockedView.frame.size.width / 4;
pinXPoint = centerPoint + 5;
pinYPoint = yPoint - 30;
}
}
CGRect pinFrame = CGRectMake(pinXPoint, pinYPoint, 80, 80);
[self.pinImageView setFrame:pinFrame];
[self.scrollview addSubview:self.pinImageView];
[self.view bringSubviewToFront:self.pinImageView];
self.scrollview.contentSize = CGSizeMake(self.view.frame.size.width, initialYPoint);
self.scrollview.contentInset = UIEdgeInsetsMake(30.0, 0.0, 30.0, 0.0);
CGPoint bottomOffset = CGPointMake(0, self.scrollview.contentSize.height - self.scrollview.bounds.size.height+ 30);
Edit: I am not using constraint layout, I am calculating & setting frame.
It looks like you could make things a bit easier on yourself.
If you Reverse the order of your "brick" stacking, you can be filling the view from the Top-Down instead of Bottom-Up ... which is much more logical and easier to think about.
This is a modification to your pastebin code. It should work fine... I didn't have your data / classes / xib views / etc, but I'm pretty sure I simulated it so this will run without error:
// start with 30-pt "padding" at the top
CGFloat initialYPoint = 30;
// reverse the array of OUSTLevel objects so we're going from top-down instead of bottom-up
NSArray *reversed = [[self.course.sortedLevelsList reverseObjectEnumerator] allObjects];
for (OUSTLevel* currentLevel in reversed) {
NSArray* nibList = [[UINib nibWithNibName:#"OUSTLearningMapPlayLevelCell" bundle:nil] instantiateWithOwner:self options:nil];
OUSTLearningMapPlayLevelCell* playLevelCell = nil;
for (UIView* childView in nibList) {
if ([childView isKindOfClass:[OUSTLearningMapPlayLevelCell class]]) {
playLevelCell = (OUSTLearningMapPlayLevelCell*)childView;
break;
}
}
CGFloat cellHeight = CGRectGetHeight(playLevelCell.frame);
// we're going top-down now, so Y coordinate will be a simple increment
CGFloat yPoint = initialYPoint;
CGRect cellFrame = CGRectMake(0, yPoint, viewWidth, cellHeight);
[playLevelCell setFrame:cellFrame];
[self.scrollview addSubview:playLevelCell];
initialYPoint += cellHeight;
if (!landingNodeLevelData.locked) {
[playLevelCell.unlockedView setHidden:NO];
[playLevelCell.lockedView setHidden:YES];
// Frame calculation for placing pin
CGFloat centerPoint = playLevelCell.unlockedView.frame.size.width / 4;
pinXPoint = centerPoint + 5;
pinYPoint = yPoint - 30;
}
}
CGRect pinFrame = CGRectMake(pinXPoint, pinYPoint, 80, 80);
[self.pinImageView setFrame:pinFrame];
[self.scrollview addSubview:self.pinImageView];
[self.view bringSubviewToFront:self.pinImageView];
// add 30-pt "padding" at the bottom
initialYPoint += 30;
// set contentSize
self.scrollview.contentSize = CGSizeMake(cellWidth, initialYPoint);
// we want to initially see the "Start Rocket" visible at the bottom of the scroll view
CGFloat yOffset = scrollview.contentSize.height - scrollview.bounds.size.height;
scrollview.contentOffset = CGPointMake(0, yOffset);
Hope it helps - if anything's not clear, ask away :)

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

content jumps on zooming out with UIScrollView

I want help with my UIScrollView sample.
I created a simple program that scrolls and zooms the content (UIImageView). It works fine, except that the content frequently disappears to the right-bottom when I try zooming out. But since I set minimumZoomScale to 1.0f, it is actually not zooming out, only the content is jumping out of the view. And what is even more weird is that I cannot scroll up after this. Apparently content size is messed up as well.
The setup I have in my sample code is as in the figure below.
When I checked the status after (trying) zooming out, I found two wrong things.
_scrollView.contentSize is 480x360, which should not be smaller than 1000x1000
_scrollView.bounds jumped to the top somehow (i.e., _scrollView.bounds.origin.y is always 0)
To cope with the two items above, I added following code in my UIScrollViewDelegate and now it works fine.
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
if(scrollView == _scrollView && view == _contentView)
{
// Setting ivars for scrollViewDidZoom
_contentOffsetBeforeZoom = _scrollView.contentOffset;
_scrollViewBoundsBeforeZoom = _scrollView.bounds;
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if(scrollView == _scrollView)
{
// If you zoom out, there are cases where ScrollView content size becomes smaller than original,
// even though minimum zoom scale = 1. In that case, it will mess with the contentOffset as well.
if(_scrollView.contentSize.width < CONTENT_WIDTH || _scrollView.contentSize.height < CONTENT_HEIGHT)
{
_scrollView.contentSize = CGSizeMake(CONTENT_WIDTH, CONTENT_HEIGHT);
_scrollView.contentOffset = _contentOffsetBeforeZoom;
}
// If you zoom out, there are cases where ScrollView bounds goes outsize of contentSize rectangle.
if(_scrollView.bounds.origin.x + _scrollView.bounds.size.width > _scrollView.contentSize.width ||
_scrollView.bounds.origin.y + _scrollView.bounds.size.height > _scrollView.contentSize.height)
{
_scrollView.bounds = _scrollViewBoundsBeforeZoom;
}
}
}
However, does it need to come down to this? This is a very simple sequence, and it is hard to believe that Apple requires us to put this kind of effort. So, my bet is I am missing something here...
Following is my original code. Please help me find what I am doing wrong (or missing something)!
#define CONTENT_WIDTH 1000
#define CONTENT_HEIGHT 1000
>>>> Snip >>>>
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
_scrollView.contentSize = CGSizeMake(CONTENT_WIDTH, CONTENT_HEIGHT);
_scrollView.backgroundColor = [UIColor blueColor];
_scrollView.maximumZoomScale = 8.0f;
_scrollView.minimumZoomScale = 1.0f;
_scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
_scrollView.scrollEnabled = YES;
_scrollView.delegate = self;
[self.view addSubview:_scrollView];
_contentView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"sample.jpg"]]; // sample.jpg is 480x360
CGPoint center = (CGPoint){_scrollView.contentSize.width / 2, _scrollView.contentSize.height / 2};
_contentView.center = center;
[_scrollView addSubview:_contentView];
_scrollView.contentOffset = (CGPoint) {center.x - _scrollView.bounds.size.width / 2, center.y - _scrollView.bounds.size.height / 2};
}
- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
if(scrollView == _scrollView)
{
return _contentView;
}
return nil;
}
I created a quick sample project and had the same issue you described using the code you pasted. I don't exactly know what the "proper" way to zoom is in iOS but I found this tutorial which says that you need to recenter your contentView after the scrollView has been zoomed. I would personally expect it to be automatically re-centered given that it is the view you're returning in the viewForZoomingInScrollView delegate method but apparently not.
- (void)centerScrollViewContents {
CGSize boundsSize = _scrollView.bounds.size;
CGRect contentsFrame = _contentView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
_contentView.frame = contentsFrame;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so we need to re-center the contents
[self centerScrollViewContents];
}
The code above is not written by me but is simply copied from the tutorial. I think its pretty straightforward. Also, centring the contentView seems to be a lot more elegant then constantly changing the bounds and content size of the scrollview so give it a try.
If anyone is having an issue of bouncing when you zooming out resulting background to show, try removing bounces (Bounces Zoom) in Interface Builder.
I was able to fix this problem using the delegate answer that adjusted the rates after zoom... but then I remembered I was using auto-layout, and just adding constraints for centering horizontally and vertically (in addition to the constraints tying the image to each edge of the scroll view) solved the issue for me without using the delegate methods.
Olshansk answer in swift 5
func scrollViewDidZoom(_ scrollView: UIScrollView) {
centerScrollViewContents()
}
func centerScrollViewContents() {
let boundsSize = scrollView.bounds.size;
var contentsFrame = container.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0;
} else {
contentsFrame.origin.x = 0.0;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0;
} else {
contentsFrame.origin.y = 0.0;
}
container.frame = contentsFrame;
}

UIScrollView with subView Zooming

I Want to implement a scrollview that has several UIViews inside of it. The left-most item needs to be larger than the rest of the items. So my problem is this. Whenever an item is leaving the screen to the left (its origin.x is less than 15), I need to scale the item down from 470x440 to 235x220 pixels. This is fairly simple to implement. The problem is that the item that is moved to the left of pixel 480 needs to be zoomed in from 235x220 pixels to 470x440 pixels AND it needs to be moved to the left by 235 pixels (so as to not cover the item to its right, but rather move into the space that the leaving element left when it "shrunk".
I have tried a few different approaches to this, but I cannot the the animation to look good, and there is a bunch of glitches here and there.
Does anyone have any idea how I might go about implementing this type of feature ? Note that I do not want to zoom, but I want to resize the elements inside the scroll view in such a way that the left-most element (that is visible on the screen) is double the size of the other elements.
In case anyone else might be interested, I ended up with the following inside scrollViewDidScroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float contentOffset = scrollView.contentOffset.x;
int leavingElementIndex = [_scrollView indexOfElementLeavingScene:scrollView.contentOffset.x];
int entereingElementIndex = leavingElementIndex + 1;
if (leavingElementIndex >= 0 && contentOffset > 0) {
CGRect leavingFrame = [[[scrollView subviews] objectAtIndex:leavingElementIndex] frame];
CGRect enteringFrame = [[[scrollView subviews] objectAtIndex:entereingElementIndex] frame];
float scalePerentage = (contentOffset - (_scrollView.smallBoxWidth * leavingElementIndex))/(_scrollView.smallBoxWidth);
enteringFrame.size.width = _scrollView.smallBoxWidth + (_scrollView.smallBoxWidth * scalePerentage);
enteringFrame.size.height = _scrollView.smallBoxHeight + (_scrollView.smallBoxHeight * scalePerentage);
enteringFrame.origin.x = [_scrollView leftMostPointAt:entereingElementIndex] - (_scrollView.smallBoxWidth * scalePerentage);
[[[scrollView subviews] objectAtIndex:entereingElementIndex] setFrame:enteringFrame];
leavingFrame.size.width = _scrollView.largeBoxWidth - (_scrollView.smallBoxWidth * scalePerentage);
leavingFrame.size.height = _scrollView.largeBoxHeight - (_scrollView.smallBoxHeight * scalePerentage);
[[[scrollView subviews] objectAtIndex:leavingElementIndex] setFrame:leavingFrame];
//Reset the other visible frames sizes
int index = 0;
for (UIView *view in [scrollView subviews]) {
if([view isKindOfClass:[SlidingView class]] && index > entereingElementIndex) {
CGRect frame = view.frame;
frame.size.width = _scrollView.smallBoxWidth;
frame.size.height = _scrollView.smallBoxHeight;
frame.origin.x = [_scrollView leftMostPointAt:index];
[view setFrame:frame];
}
index++;
}
}
}
This is what it looks like in the end:
End Result http://stuff.haagen.name/iOS%20scroll%20resize.gif

UIScrollView Direction RTL for Right to Left Languages

Can we change the behavior of UIScrollView that it scrolls RTL content in it's reverse mode.
You can achieve it by rotating UIScrollView in Child views by 180 (Pi).
self.scrollView.transform = CGAffineTransformMakeRotation(M_PI);
subView.transform = CGAffineTransformMakeRotation(M_PI);
count = 6;
[self.scrollView setFrame:CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, 320, 480)];
[pageControl setNumberOfPages:count];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * count, self.scrollView.frame.size.height);
for(int i=count-1; i>=0; i--) { //start adding content in reverse mode
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * (count - i - 1); //ar
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
[imageView setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", i]]];
[self.scrollView addSubview:imageView];
}
//scroll to the last frame as it's the first page for RTL languages
CGRect frame;
frame.origin.x = scrollView.frame.size.width * (count - 1);
frame.origin.y = 0;
frame.size = scrollView.frame.size;
[self.scrollView scrollRectToVisible:frame animated:NO];
The page control also needs to be indicate the last dot as the first dot (or first page)
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// Update the page when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = (page % count);
}
I try it for xCode 7 and iOS 9 work good.
[self._imagesScrollView setTransform:CGAffineTransformMakeScale(-1, 1)];
I m not sure if I understand the question correctly but I take it like this: You want to have a (let's say) horizontal UIScrollView where you can add elements from right to left and when the contents exceed the scroll view frame you want to be able to scroll to the left rather than to the right in order to see the last elements.
In this case you can do this. For every new element calculate the total width you need and set it as contentOffset:
[scrollView setContentSize:CGSizeMake(self.childView.frame.size.width,
scrollView.frame.size.height)];
Above I have a childView where the elements are added. It is placed at the right most position inside the scrollView minus 1 element width. Every time an element is added call:
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x + kElementWidth,0.0)];
which will offset everything to the left. Then:
self.videoQueueCollectionView.center = CGPointMake(self.childView.center.x + kElementWidth,
self.childView.center.y);
That will snap eveything back to where it was BUT with the contentOffset set to the left. Which means that you can now scroll to the right.
I use this code for Swift 3
self.scrollView.transform = CGAffineTransform(scaleX:-1,y: 1);
You can achieve this by setting semanticContentAttribute
self.scrollView.semanticContentAttribute = .forceRightToLeft

Resources