UIScrollView with subView Zooming - ios

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

Related

how to get a subview of UIScrollView using a CGPoint

I have a UIScrollView with a number of rectangular subviews lined up, of equal sizes. Then I need to be able to pass a CGPoint to that UIScrollView and I want it to give me the rectangular subview that contains the CGPoint. That's basically hitTest:event, except hitTest:event: doesn't work with UIScrollView once CGPoint goes beyond the UIScrollView bounds and doesn't look into its actual content.
What's everyone been doing about this? How to "hit test" on a UIScrollView content view?
Here's some code to illustrate the problem:
NSArray *rectangles = [self getBeautifulRectangles];
CGFloat rectangleLength;
rectangleLength = 100;
// add some rectangle subviews
for (int i = 0; i < rectangles.count; i++) {
UIView *rectangle = [rectangles objectAtIndex:i];
[rectangle setFrame:CGRectMake(i * rectangleLength, 0, rectangleLength, rectangleLength)];
[_scrollView addSubview:rectangle];
}
[_scrollView setContentSize:CGSizeMake(rectangleLength * rectangles.count, rectangleLength)];
// add scroll view to parent view
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0,0,320, rectangleLength)];
[containerView addSubview:_scrollView];
// compute CGPoint to center of first rectangle
CGPoint number1RectanglePoint = CGPointMake(0 * rectangleLength + 50, 50);
// compute CGPoint to center of fifth rectangle
CGPoint number5RectanglePoint = CGPointMake(4 * rectangleLength + 50, 50);
UIView *firstSubview = [containerView hitTest:number1RectanglePoint withEvent:nil];
UIView *fifthSubview = [containerView hitTest:number5RectanglePoint withEvent:nil];
if (firstSubview) NSLog(#"first rectangle OK");
if (fifthSubview) NSLog(#"fifth rectangle OK");
output: first rectangle OK
You should be able to loop through the scrollview subviews
+(UIView *)touchedViewIn:(UIScrollView *)scrollView atPoint:(CGPoint)touchPoint {
CGPoint actualPoint = CGPointMake(touchPoint.x + scrollView.contentOffset.x, scrollView.contentOffset.y + touchPoint.y);
for (UIView * subView in scrollView.subviews) {
if(CGRectContainsPoint(subView.frame, actualPoint)) {
NSLog(#"THIS IS THE ONE");
return subView;
}
}
//Nothing touched
return nil;
}
I guess you pass the wrong CGPoint coordinate to the hitTest:withEvent: method causing wrong behavior if the scroll view is scrolled.
The coordinate you pass to this method must be in the target views coordinate system. I guess your coordinate is in the UIScrollView's superview's coordinate system.
You can convert the coordinate prior to using it for the hit test using CGPoint hitPoint = [scrollView convertPoint:yourPoint fromView:scrollView.superview].
In your example you let the container view perform the hit testing, but the container can only see & hit the visible portion of the scroll view and thus your hit fails.
In order to hit subviews of the scroll view which are outside of the visible area you have to perform the hit test on the scroll view directly:
UIView *firstSubview = [_scrollView hitTest:number1RectanglePoint withEvent:nil];
UIView *fifthSubview = [_scrollView hitTest:number5RectanglePoint withEvent:nil];

Adding a UIButton to UITableView but can't scroll down to the button

I've got a UITableView and I want to add a button just underneath the table. The table is bigger than the screen so to get to the button, I need to scroll down to it. The problem is, the screen will scroll to just above where the button is. So I can drag the screen slightly and the button is visible but when I let go, the screen springs back and I can't get to the button.
The button is set up as follows
for(int i = 0; i < [buttonArray count]; i++)
{
UIButton *but = [buttonArray objectAtIndex:i];
float xPosition = but.frame.origin.x;
float xGap = (self.view.frame.size.width - (but.frame.size.width)*2)/3;
if ((i % 2 == 0) && (i < [buttonArray count] - 1))
{
xPosition = xGap;
}
else if ((i % 2 == 1) && (i < [buttonArray count] - 1))
{
xPosition = (xGap * 2) + but.frame.size.width;
}
else
{
xPosition = (self.view.frame.size.width - but.frame.size.width)/2;
}
//Add space between row of buttons
if(i > 0)
{
if(i % 2 == 0)
{
yPosition+=but.frame.size.height;
CGRect screenRect = [[tableView superview] bounds];
//Add extra space between row of buttons
float fGap = (screenRect.size.height / 100.0) * 4.5;
yPosition+=fGap;
}
}
CGRect rect = CGRectMake(xPosition, yPosition, but.frame.size.width, but.frame.size.height);
but.frame = rect;
[but addTarget:self action:#selector(genericMethod:) forControlEvents:UIControlEventTouchUpInside];
CGRect screenRect = [self.view bounds];
CGFloat fHeight = screenRect.size.height;
float fMargin = (fHeight / 100.0) * 0.8;
but.titleEdgeInsets = UIEdgeInsetsMake(0, 0, fMargin, 0);
[tableView addSubview:but];
}
}
So I go through an array of buttons (only one button in the array in my current case) and set up the frames and position them into columns and add it as a subview to the tableView. Things I've tried are:
Increase the table footer size. This just makes the gap between the table and button increase and I still can't scroll down as far as the button.
Add the button to the footer. This kind of worked. I could get to the button, but the button was always on screen, not just static below the table.
Add the button to a separate view below the table and add that view to the viewController.view. This didn't work at all and the button wasn't displayed.
Set the tableView.tableFooterView equal to the button. This also kind of worked and I could reach the button but it wouldn't work when there is more than one button in the array above.
You should create a custom view. Inside that view you should add all the buttons created using buttonArray. Now, assign this custom view to tableFooterView.
UIView *customView = [[UIView alloc] initWithFrame:properFrame];
for(int i = 0; i < [buttonArray count]; i++)
{
//...calculation of frame and other stuff
[customView addSubview:but];
}
tableView.tableFooterView = customView;
This should solve your scrolling issue.
If you want to make bottom view as stable,Take a UIView at the bottom
of UITableView.
If you will use TableFooterView,It will be at the end of cell .

how to show 4 different videos inside UIScrollView

I want to show 4 different animations inside UIScrollView in one time, all of them has 200+ frames, so I think that the best solution to solve this is to show videos. It costs less memory then animations. My videos are ~ 500x300 dimensions, and I want to show them as 250x150. How can I do this ?
this must be the solution of your problem
- (void)layoutScrollImages
{
UIView *view = nil;
NSArray *subviews = [self.scrollView subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UIView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
// set the content size so it can be scrollable
//[self.scrollView setContentSize:CGSizeMake((numImages * kScrollObjWidth), [self.scrollView bounds].size.height)];
[self.scrollView setContentSize:CGSizeMake((numImages * kScrollObjWidth), kScrollObjHeight)];
}

in UIScrollView setContentSize working only one

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)];

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