i have a scollview and i want to display suppose 10 images. frame width is 320 for iphone and 768 for ipad.
something like this image
middle image will be our current selection(mountain image according to this snapshot).
Now i want to scroll left or right to check all the images.
If waterfall is at position 1 mountain at position 2 and hut at position 3, when i scroll right i want to show mountain at position 1 hut at position2 and a new image at position 3.
PLZ tell me how to do this
You have to adjust and set the contentOffset of the scrollView based on the size of the image. You must also set the necessary contentSize for the scrollView which will enable the scrolling.
hey use content offset.
and use this code u get what you want.
this is a scrollview for two image at a time seen on iPhone and when a image scroll at middle of iPhone the label set midlle image label.. as you set width 320 change to 320
-(void)scrollViewDidScroll:(UIScrollView *)inscrollView{
p = scrollView.contentOffset;
NSLog(#"x = %f, y = %f", p.x, p.y);
[self loadDescLbl];
}
-(void)loadDescLbl
{
int point;
point = p.x+160;
int i;
i = point / 160;
i = i-1;
if(i > fetArray.count-1){
i = fetArray.count -1;
}
if(i == -1)
{
i = 0;
}
imageView[i].highlighted = YES;
if([yourArray objectAtIndex:i] isEqualToString:#""]){
priceLbl.text = #"";
}
else {
priceLbl.text = [NSString stringWithFormat:#"%#", [yourArray objectAtIndex:i]];
}
[scroll addSubview:priceLbl];
// }
}
just set point = p.x
and i = point;
if you get problem then tell me...
f all of your images are of the same size you could add 2 buttons (left right) ... and when you tap one of them change the content offset's frame with the width of one picture ...
if you don't want buttons you can obtain the same effect with UISwipeGestureRecognizer
-(IBAction)changeVisibleRect:(UIButton *)btn {
[UIView beginAnimation:#"animation name" context:nil];
[UIView setAnimationDuration:0.3]; //or how much you want in seconds
[scrollview.contentOffset setFrame:CGRectMake(pictureSize * i, 0, pictureSizeWidth, pictureSizeHeight)]; //i = the number of the picture
[UIView commitAnimations];
}
CGRectMake(pictureSize * i, 0, pictureSizeWidth, pictureSizeHeight)] ... instead of pictureSize * i ... you could asign you left or right buttons tags and ... make a
if(btn.tag == 1) {
[UIView beginAnimation:#"animation name" context:nil];
[UIView setAnimationDuration:0.3]; //or how much you want in seconds
[scrollview.contentOffset setFrame:CGRectMake(scrollView.contentOffset.frame.origin.x - pictureSizeWidth, 0, pictureSizeWidth, pictureSizeHeight)]; //i = the number of the picture
[UIView commitAnimations];
}
assuming your left button has set tag = 1;
for the SwipeGesture it would be the same animation code / idea
Related
I want to create an animation similar to app opens in iPhone in iOS7. In this animation it just shows that app is opening from which point and closing at same point.
Can anyone please help me?
#property (weak, nonatomic) IBOutlet UIImageView *bg;
#property (weak, nonatomic) IBOutlet UIImageView *cal;
…
bool nowZoomed = NO;
CGRect iconPosition = {16,113,60,60}; // customize icon position
- (CGRect)zoomedRect // just a helper function, to get the new background screen size
{
float screenWidth = UIScreen.mainScreen.bounds.size.width;
float screenHeight = UIScreen.mainScreen.bounds.size.height;
float size = screenWidth / iconPosition.size.width;
float x = screenWidth/2 - (CGRectGetMidX(iconPosition) * size);
float y = screenHeight/2 - (CGRectGetMidY(iconPosition) * size);
return CGRectMake(x, y, screenWidth * size, screenHeight * size);
}
- (IBAction)test
{
float animationDuration = 0.3f; //default
if (nowZoomed) // zoom OUT
{
[UIView animateWithDuration:animationDuration animations:^{ // animate to original frame
_cal.frame = iconPosition;
_bg.frame = UIScreen.mainScreen.bounds;
} completion:^(BOOL finished) {
[UIView animateWithDuration:animationDuration/2.0f animations:^{ // then fade out
_cal.alpha = 0.0f;
} completion:^(BOOL finished) {
_cal.hidden = YES;
}];
}];
}
else // zoom IN
{
_cal.alpha = 0.0f;
_cal.hidden = NO;
[UIView animateWithDuration:animationDuration/2.0f animations:^{ // fade in faster
_cal.alpha = 1.0f;
}];
[UIView animateWithDuration:animationDuration animations:^{ // while expanding view
_cal.frame = UIScreen.mainScreen.bounds;
_bg.frame = [self zoomedRect];
}];
}
nowZoomed = !nowZoomed;
}
you can test it, by creating a sample project like this:
make two screenshots from simulator like I did (homescreen and calendar view) or grab these two: homescreen / calendar
add 2 image views and 1 button into storyboard
make the background image view as big as the whole screen
and the other image view with this dimensions: {16,113,60,60}
create an IBOutlet for both (the very first two lines of code)
set the button action target to -(void)test
the storyboard picture (left) and animation transition (right)
I personally prefer to use CGAffineTransformMakeScale() and setting -[CALayer affineTransform] in this case.
affineTransform is super easy to use and comes with a few nice, implicit benefits from Core Animation. Examples being that does things like handling of changing the frame's origin for you implicitly and making it really easy to reset back to the initial size if needed -- you never lost it in the first place!
[UIView animateWithDuration:0.3 animations:^{
view.layer.affineTransform = CGAffineTransformMakeScale(10.0, 10.0); // To make a view larger:
otherView.layer.affineTransform = CGAffineTransformMakeScale(0.0, 0.0); // to make a view smaller
}];
and
// To reset views back to their initial size after changing their sizes:
[UIView animateWithDuration:0.3 animations:^{
view.layer.affineTransform = CGAffineTransformIdentity;
otherView.layer.affineTransform = CGAffineTransformIdentity;
}];
As far as I know, that animation is made using screenshots. It updates the frame of the view and simultaneously makes a smooth transition from the app logo to the screenshot from the app. I have imitated the opening of the iPod (music) application from the bottom right corner of the device to the screen size:
UIView * v = [[UIView alloc]init];
CGSize size = self.view.bounds.size;
CGRect frameInitial = CGRectMake(size.width - 30, size.height - 30, 20, 20);
CGRect frameFinal = CGRectMake(0,0, size.width, size.height);
[v setFrame:frameInitial];
Then use the lines below when you want to animate the frame size:
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[v setFrame:frameFinal];
} completion:nil];
Edit: Did not realize that the zooming also included the background. The code below is not tested (I am not at work) so expect some defects and typos.
Imagine you have two layers on the view controller's view. Directly on the vc there is the app you want to be opened, lets call it finalView. And on the top layer there is the window with all apps, which will zoom and fade into your app, which is a view behind it. Lets call it firstView.
Initial cond: firstView has a frame of 320 x 480 (It is a window with all the app icons). It has an alpha of 1. finalView has the same frame and alpha, but it is behind firstView.
Final cond: finalView will still have the same frame and alpha. But firstView will zoom into bottom right corner (will have a huge frame) and fade out (alpha -> 0).
//Initial cond: (Or better yet use IB)
CGRect frameInitial = CGRectMake(0,0, self.view.size.width, self.view.size;
CGRect frameFinal = CGRectMake(self.view.size.width * -4 ,self.view.size.height * -5, self.view.size.width * -5,self.view.size.width * -6);
[v setFrame:frameInitial];
Then use the lines below when you want to animate the frame size:
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[v setFrame:frameFinal];
} completion:nil];
I have small repo that uses a UICollectionViewFloatLayout to create the zoom effect, https://github.com/MichaelQuan/ios7ZoomEffect. It is still a work in progress but the basic idea is there
The layout code is:
#interface ExpandingCollectionViewLayout ()
#property (nonatomic, assign) CGRect selectedCellFrame;
#property (nonatomic, strong) NSIndexPath *selectedIndexPath;
#end
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *layoutAttributes = [super layoutAttributesForElementsInRect:rect];
[layoutAttributes enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL *stop) {
[self _transformLayoutAttributes:obj];
}];
return layoutAttributes;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
[self _transformLayoutAttributes:layoutAttributes];
return layoutAttributes;
}
- (void)_transformLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
if (self.selectedIndexPath != nil)
{
if ([layoutAttributes.indexPath isEqual:self.selectedIndexPath]) {
// set the frame to be the bounds of the collectionView to expand to the entire collectionView
layoutAttributes.frame = self.collectionView.bounds;
} else {
//scale = collectionView.size / cell_selected.size
//translate = (scale - 1)(cell_i.center - cell_selected.center) + (collectionView.center - cell_selected.center)
CGRect collectionViewBounds = self.collectionView.bounds;
CGRect selectedFrame = self.selectedCellFrame;
CGRect notSelectedFrame = layoutAttributes.frame;
// Calculate the scale transform based on the ratio between the selected cell's frame and the collection views bound
// Scale on that because we want everything to look scaled by the same amount, and the scale is dependent on how much the selected cell has to expand
CGFloat x_scale = collectionViewBounds.size.width / selectedFrame.size.width;
CGFloat y_scale = collectionViewBounds.size.height / selectedFrame.size.height;
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(x_scale, y_scale);
// Translation based on how much the selected cell has been scaled
// translate based on the (scale - 1) and delta between the centers
CGFloat x_zoomTranslate = (x_scale - 1) * (CGRectGetMidX(notSelectedFrame) - CGRectGetMidX(selectedFrame));
CGFloat y_zoomTranslate = (y_scale - 1) * (CGRectGetMidY(notSelectedFrame) - CGRectGetMidY(selectedFrame));
CGAffineTransform zoomTranslate = CGAffineTransformMakeTranslation(x_zoomTranslate, y_zoomTranslate); //Translation based on how much the cells are scaled
// Translation based on where the selected cells center is
// since we move the center of the selected cell when expanded to full screen, all other cells must move by that amount as well
CGFloat x_offsetTranslate = CGRectGetMidX(collectionViewBounds) - CGRectGetMidX(selectedFrame);
CGFloat y_offsetTranslate = CGRectGetMidY(collectionViewBounds) - CGRectGetMidY(selectedFrame);
CGAffineTransform offsetTranslate = CGAffineTransformMakeTranslation(x_offsetTranslate, y_offsetTranslate);
// multiply translations first
CGAffineTransform transform = CGAffineTransformConcat(zoomTranslate, offsetTranslate);
transform = CGAffineTransformConcat(scaleTransform, transform);
layoutAttributes.transform = transform;
}
}
}
To expand a cell using this layout code, set both the selectedCellFrame and selectedIndexPath to the cell you want expanded and call performBatchUpdates:completion: on the collection view. To collapse set selectedCellFrame = CGRectNull and selectedIndexPath = nil and call performBatchUpdates:completion:
I have a UICollectionView with 6 pages, and paging enabled, and a UIPageControl. What I want is, when I came to the last page, if I drag to right, UICollectionView reloads from first page seamlessly.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
// The key is repositioning without animation
if (collectionView.contentOffset.x == 0) {
// user is scrolling to the left from image 1 to image 10.
// reposition offset to show image 10 that is on the right in the scroll view
[collectionView scrollRectToVisible:CGRectMake(collectionView.frame.size.width*(pageControl.currentPage-1),0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];
}
else if (collectionView.contentOffset.x == 1600) {
// user is scrolling to the right from image 10 to image 1.
// reposition offset to show image 1 that is on the left in the scroll view
[collectionView scrollRectToVisible:CGRectMake(0,0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];
}
pageControlUsed = NO;
}
It doesn't work like I want. What can I do?
Here's what I ended up with for my UICollectionView (horizontal scrolling like the UIPickerView):
#implementation UIInfiniteCollectionView
- (void) recenterIfNecessary {
CGPoint currentOffset = [self contentOffset];
CGFloat contentWidth = [self contentSize].width;
// don't just snap to center, since this might be done in the middle of a drag and not aligned. Make sure we account for that offset
CGFloat offset = kCenterOffset - currentOffset.x;
int delta = -round(offset / kCellSize);
CGFloat shift = (offset + delta * kCellSize);
offset += shift;
CGFloat distanceFromCenter = fabs(offset);
// don't always recenter, just if we get too far from the center. Eliza recommends a quarter of the content width
if (distanceFromCenter > (contentWidth / 4.0)) {
self.contentOffset = CGPointMake(kCenterOffset, currentOffset.y);
// move subviews back to make it appear to stay still
for (UIView *subview in self.subviews) {
CGPoint center = subview.center;
center.x += offset;
subview.center = center;
}
// add the offset to the index (unless offset is 0, in which case we'll assume this is the first launch and not a mid-scroll)
if (currentOffset.x > 0) {
int delta = -round(offset / kCellSize);
// MODEL UPDATE GOES HERE
}
}
}
- (void) layoutSubviews { // called at every frame of scrolling
[super layoutSubviews];
[self recenterIfNecessary];
}
#end
Hope this helps someone.
I've been using the Street Scroller sample to create an infinite scroller for images. That works fine until I wanted to set pagingEnabled = YES; Tried tweaking around the recenterIfNecessary code and finally realized that it's the contentOffset.x that has to match the frame of the subview that i want visible when paging stops. This really isn't going to work in recenterIfNecessary since you have no way of knowing it will get called from layoutSubviews. If you do get it adjusted right, the subview may pop out from under your finger. I do the adjustment in scrollViewDidEndDecelerating. So far I haven't had problems with scrolling fast. It will work and simulate paging even when pagingEnabled is NO, but it looks more natural with YES.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[super scrollViewDidEndDecelerating:scrollView];
CGPoint currentOffset = [self contentOffset];
// find the subview that is the closest to the currentOffset.
CGFloat closestOriginX = 999999;
UIView *closestView = nil;
for (UIView *v in self.visibleImageViews) {
CGPoint origin = [self.imageContainerView convertPoint:v.frame.origin toView:self];
CGFloat distanceToCurrentOffset = fabs(currentOffset.x - origin.x);
if (distanceToCurrentOffset <= closestOriginX) {
closestView = v;
closestOriginX = distanceToCurrentOffset;
}
}
// found the closest view, now find the correct offset
CGPoint origin = [self.imageContainerView convertPoint:closestView.frame.origin toView:self];
CGPoint center = [self.imageContainerView convertPoint:closestView.center toView:self];
CGFloat offsetX = currentOffset.x - origin.x;
// adjust the centers of the subviews
[UIView animateWithDuration:0.1 animations:^{
for (UIView *v in self.visibleImageViews) {
v.center = [self convertPoint:CGPointMake(v.center.x+offsetX, center.y) toView:self.imageContainerView];
}
}];
}
I have not used UICollectionView for infinite scrolling, but when doing it with a UIScrollView you first adjust your content offset (instead of using scrollRectToVisible) to the location you want. Then, you loop through each subview in your scroller and adjust their coordinates either to the right or left based on the direction the user was scrolling. Finally, if either end is beyond the bounds you want them to be, move them to the far other end. Their is a very good WWDC video from apple about how to do infinite scrolling you can find here: http://developer.apple.com/videos/wwdc/2012/
I have a UIScrollView. I want to implement an infinite left side-scrolling effect. How can this be possible?
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageWidth = scrollView.frame.size.width/7;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
Lbl_Rate.text=[NSString stringWithFormat:#" pagenumber==>%d",page ];
int p=page%10;
if(p==0){
page=1;
SCrl_Wheel.contentOffset = CGPointMake(0, 0);
}
}
I had trid to set contentoffset by SCrl_Wheel.contentOffset = CGPointMake(0, 0);
But it is not actually setting contentoffset.x=0;
And this is making :-[SCrl_Wheel setContentOffset:CGPointMake(0,0) animated:YES]; my loop as infinite loop.
Add this setContentSize: to your code.
[SCrl_Wheel setContentSize:CGSizeMake(-100000,50)];
Now it would scroll like the way you want to and why hav u added your y = 0 ?? then the height of the UIScrollView would be 0....
EDIT:
[SCrl_Wheel setContentOffset:CGPointMake(1000, 0.0)];
Add this code ull b getting both sides scrolling ur way :) I tested it ...
To add Left Side scrolling effect, you can create a scrollview with very large width and then set its initial contentOffset to midpoint of contentwidth of scrollview :
scrollView.contentSize = CGSizeMake(100000, 60);
scrollView.contentOffset = CGPointMake(scrollView.contentSize.width/2, 0);
This is the code I used for scrolling left:
HorizontalScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0,50,yourView.frame.size.width,tbv.frame.size.height)];
[HorizontalScroll setContentSize:CGSizeMake(930,340)];
[HorizontalScroll setBackgroundColor:[UIColor redColor]]; // color is set just to know where the scroll view is , remove this line afterwards.
[self.view addSubview:HorizontalScroll];
[HorizontalScroll addSubview:yourView];
Edit: See the answer below.
I finally give up and come here to ask you for my problem...
I'm using a UIScrollView for a scrolling menus with little icons.
On each page, with paging enabled, there's an icon in the center, and 2 and a half other visible icons on the left and right. I can move from one icon to its neighbour, and that is fine, but the point is that if I do a fast scrolling, it will not move from more than 3 icons, which is the width of the screen.
What I would want is to be able to scroll on more than 3 icons, and that the magnet behaviour is only triggered when it's slowing down.
I've tried to schedule the scroll view to calculate its velocity, and set the pagingEnabled attribute to NO when it's moving fast and YES again when it's slowing down, but as soon as it is set to YES, the view comes back very fast at its original position, as if it was not detecting that I had brought it to a new page. Would anyone know why it does this? And if I have a way to tell the view "ok, now the paging is enabled but look, you're 15 pages later. Just center on the current page, don't come back at the beginning."
Here's my update function (if it can help):
-(void)update:(ccTime)dt
{
float velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
self.previousOffset = self.scrollView.contentOffset.y;
CCLOG(#"Velocity: %f", velocity);
if(self.scrollView.pagingEnabled)
{
if(velocity > 100)
{
self.scrollView.pagingEnabled = NO;
}
}
else
{
if(velocity < 100)
{
self.scrollView.pagingEnabled = YES;
}
}
}
I finally found a solution, which was pretty obvious, but that I did not see at the beginning, by using setContentOffset on the scrollView.
Here is the new update function:
-(void)update:(ccTime)dt
{
float velocity = 1000;
if(self.previousOffset)
{
velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
}
self.previousOffset = self.scrollView.contentOffset.y;
if(velocity < 300)
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfScreen = screenSize.width/2;
CCLayer *panel = (CCLayer *)[self getChildByTag:1];
SQScrollViewMenu *menu = (SQScrollViewMenu *)[panel getChildByTag:1];
SQMissionItem *currentItem = (SQMissionItem *)[menu getChildByTag:currentPage];
float contentOffsetY = [self.scrollView contentOffset].y;
CCLOG(#"Currentpage: %i ; currentoffsetY: %f", currentPage, contentOffsetY);
float distance = contentOffsetY + [currentItem position].x - halfScreen + panel.position.x + menu.position.x + panel.parent.position.x - 60;
CCLOG(#"Which gives a distance of: %f", distance);
[self.scrollView setContentOffset:CGPointMake(0, distance) animated:YES];
self.previousOffset = 0;
[self unschedule:#selector(update:)];
CCLOG(#"Is unscheduled");
}
}
And it is almost working... at least, it is on simulator. But as soon as I try it on my iPhone 4, it does not work anymore. It always go into that update function, but 7 times among 8, it just blocks the scrollView as it is and does not drags it back to the position I give it... but sometimes it does.
Would anyone have an idea? I found similar issues on the net, but none of them could resolve this...
I wonder if there is someone out there that can shed some light as to what is happening in my app. I have created a multiline text box using the UITextView. When the view loads it is a single line textbox and as the user types out their message I increase the size according. Pretty much how the SMS app works.
The problem is it is doing something rather funky when adding new lines. What happens is when you get to end of the line when it is meant to add a new line it seems to add 2 new lines and as soon as you enter another character the 2nd additional line disappears and I am left with just the 1 new additional line.
I have outputted my values to the console when as each letter is added. All the values are correct, even when the 2nd additional line is added, mathematically and code wise the height value of the textview is the same with and without the this 2nd line for 1 character. (The same results happen when using the debugger to inspect the various values)
I have placed my function below for you code boffins to cast your eye over and tell me what I am doing wrong.
-(void) keyPressed: (NSNotification*) notification{
// check if there is text in the text box
if (chatTextView.hasText)
{
chatTextButton.enabled = YES;
CGSize expectedSize = [[[notification object]text] sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(210,9999) lineBreakMode:UILineBreakModeWordWrap];
NSInteger expectedHeight = expectedSize.height;
if (expectedHeight < 30)
{
[chatTextView scrollRectToVisible:CGRectMake(0,0,1,1) animated:NO];
}
if (expectedHeight >= 30 && expectedHeight <= 126)
{
//text view resizing
CGRect frameTextView = chatTextView.frame;
NSInteger frameTextW = frameTextView.size.width;
frameTextView.size.height = expectedHeight + 12;
chatTextView.frame = frameTextView;
//chat view resizing
CGRect frameChat = chatTextBoxView.frame;
NSInteger frameChatH = frameChat.size.height;
NSInteger frameChatY = frameChat.origin.y;
frameChat.origin.y = 202 - (expectedHeight + 27);
frameChat.size.height = (expectedHeight + 12);
chatTextBoxView.frame = frameChat;
//main view resizing
CGRect frameMain = self.view.frame;
NSInteger frameMainH = frameMain.size.height;
frameMain.size.height = 247 - (expectedHeight + 27);
self.view.frame = frameMain;
NSLog(#"==== EXPECTED HEIGHT %d =====",expectedHeight);
NSLog(#"==== CHAT TEXT WIDTH %d =====",frameTextW);
NSLog(#"==== CHAT VIEW HEIGHT %d =====",frameChatH);
NSLog(#"==== CHAT VIEW LOCATY %d =====",frameChatY);
NSLog(#"==== MAIN VIEW HEIGHT %d =====",frameMainH);
NSLog(#"==============================");
[chatTextView scrollRectToVisible:CGRectMake(0,0,1,1) animated:NO];
}
if (expectedHeight > 126)
{
chatTextView.scrollEnabled = YES;
}
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];
[UIView commitAnimations];
[self scrollThread];
}
If anyone can help please please, I don' have much hair left. Many thanks in advance.