I want to load images from a URL and then assign them as background images for different UIButtons. I need to do this when a view appears.
My problem is that when I try to make the images fade in when they are loaded, the animation does not start until all of the images have loaded. I think this is because the animation code is read but that before it has time to be executed, the program starts loading a new image.
How can I make images fade in one after the other?
The following code is used to get the image (calling downloadImageByPrice atURL) and then does the animation.
-(void) obtainImage:(int)i atURL:(NSString *)URLString{
UIImage *image = [self downloadImageByPrice:i atURL:URLString];
// Make images fade in when they have been found
[[_buttonArray objectAtIndex:i] setAlpha:0.0];
[[_buttonArray objectAtIndex:i] setImage:image forState:UIControlStateNormal];
[UIView beginAnimations:#"fadeIn" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.8];
[[_buttonArray objectAtIndex:i] setAlpha:1.0];
[UIView commitAnimations];
}
This method is used to load all the images one after the other. I want my loop to wait until each image has appeared before starting to load the next one.
-(void) loadAllImagesAtURL:(NSString *)URLString{
for(int i =0; i<[_buttonArray count];i++){
[self obtainImage:i atURL:URLString];
}
}
I have tried using selectors, or the completion^ method with no luck, but my understanding of those concepts is still pretty low.
Thank you
Check this out:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) NSMutableArray *buttonArray;
-(void) loadAllImagesAtURL:(NSString *)URLString;
- (UIImage *)downloadImageAtURL:(NSString *)urlString;
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize buttonArray = _buttonArray;
- (void)viewDidLoad {
[super viewDidLoad];
[self setButtonArray:[NSMutableArray array]];
int margin = 20;
int buffer = 8;
int width = 100;
int height = 50;
for (int i = 0; i < 4; i = i + 1) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setAlpha:0.0];
[btn setFrame:CGRectMake(margin + ((width + buffer) * i), margin, width, height)];
[btn setTitle:[NSString stringWithFormat:#"%d", i] forState:UIControlStateNormal];
[[self buttonArray] addObject:btn];
[[self view] addSubview:btn];
}
[self loadAllImagesAtURL:#"https://www.google.com/images/srpr/logo3w.png"];
}
-(void) loadAllImagesAtURL:(NSString *)URLString {
static int i = -1;
i = i + 1;
if (i < [[self buttonArray] count]) {
UIImage *image = [self downloadImageAtURL:URLString];
// Make images fade in when they have been found
[[_buttonArray objectAtIndex:i] setImage:image forState:UIControlStateNormal];
[UIView animateWithDuration:1.0
animations:^{
[[_buttonArray objectAtIndex:i] setAlpha:1.0];
}
completion:^(BOOL finished) {
[self loadAllImagesAtURL:URLString];
}
];
}
}
- (UIImage *)downloadImageAtURL:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
return image;
}
#end
For me, this creates 4 buttons (that are initially invisible) that fade in when the image is downloaded. Is this what you want?
Related
I have a UIScrollView with paging enabled. However i want this scrollview to scroll right when a certain amount of time has passed. Is there a easy way to implement this or do i need to use a timer and programmatically scroll the scrollview?
Something like this website has in its header: http://denederlandsewateren.nl/ [notice the text "De Nederlandse Wateren" changes each x seconds]
You need a little method to help you scroll to the exact page, this is not implemented, but it's very simple, let's assume your scrollview is called myScrollview
This assumes you're scrolling horizontally, you can switch the code to fit your needs
-(void)scrollToPage:(NSInteger)aPage{
float myPageWidth = [myScrollView frame].size.width;
[myScrollView setContentOffset:CGPointMake(aPage*myPageWidth,y) animated:YES];
}
Then you setup a timer to call this method every 5 seconds for example, like this:
[[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(scrollPages)
userInfo:Nil
repeats:YES] fire];
Next step is to create the method that will be fired by the timer and scroll to the next page, I called it -(void)scrollPages, it also requires that you have two global variables called currentPage and numberOfPages, both are of type NSInteger
-(void)scrollPages{
[self scrollToPage:currentPage%numberOfPages];
currentPage++;
}
If something doesn't make sense let me know !
Found this on stackoverflow
[scrollView setContentOffset:CGPointMake(x, y) animated:YES];
To do slideshows with UIScrollView, you arrange all images in the scroll view, set up a repeated timer, then -setContentOffset:animated: when the timer fires.
In your .h file create outlets for scrollview and paging
#property (weak, nonatomic) IBOutlet UIScrollView *sliderScrollView;
#property (weak, nonatomic) IBOutlet UIPageControl *sliderPageControll;
#property (nonatomic,strong) NSArray *sliderImagesArray;
#property (nonatomic,strong) NSString *sliderIndexString;
#property (nonatomic,strong) NSString *currentPageIndexString;
after that in your .m file
[self.sliderScrollView setPagingEnabled:YES];
[self.sliderScrollView setBounces:NO];
[self.sliderScrollView setAlwaysBounceVertical:NO];
[self.sliderScrollView setShowsHorizontalScrollIndicator:NO];
self.sliderScrollView.alwaysBounceVertical = NO;
NSArray *imagesArray = self.sliderImagesArray;
for (int i = 0; i < [imagesArray count]; i++){
CGFloat xOrigin = i * self.imageSliderScrollView.frame.size.width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.sliderScrollView.frame.size.width, self.sliderScrollView.frame.size.height)];
imageView.userInteractionEnabled = YES;
NSString *imgStr = [serviceImgArray objectAtIndex:i];
NSString *bikeStr = [NSString stringWithFormat:#"%#", imgStr];
NSURL *imageURL = [NSURL URLWithString:[bikeStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[imageView sd_setImageWithURL:imageURL
placeholderImage:[UIImage imageNamed:#"profilepic_bg"]];
[imageView setContentMode:UIViewContentModeScaleAspectFill];
[self.sliderScrollView addSubview:imageView];
}
// for adding autorotate scrolling
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(autoStartSlide:) userInfo:nil repeats:YES];
self.sliderPageControll.numberOfPages = imagesArray.count;
self.sliderPageControll.currentPage = 0;
self.currentPageIndexString = [NSString stringWithFormat:#"%ld",(long)0];
[self.sliderScrollView setContentSize:CGSizeMake(self.sliderScrollView.frame.size.width * [imagesArray count], self.sliderScrollView.frame.size.height)];
if ([self.sliderPageControll respondsToSelector:#selector(setPageIndicatorTintColor:)]) {
self.sliderPageControll.currentPageIndicatorTintColor = [UIColor redColor];
self.sliderPageControll.pageIndicatorTintColor = [UIColor whiteColor];
}
CGRect frame = self.sliderScrollView.frame;
frame.origin.x = frame.size.width * [[NSString stringWithFormat:#"%#",self.sliderIndexString] integerValue];
frame.origin.y = 0;
[self.sliderScrollView scrollRectToVisible:frame animated:NO];
self.sliderPageControll.currentPage = [[NSString stringWithFormat:#"%#",self.sliderIndexString] integerValue];
after that implement scrollview delegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSInteger scrollIndex = (NSInteger)(self.sliderScrollView.contentOffset.x / self.sliderScrollView.frame.size.width);
self.sliderPageControll.currentPage = scrollIndex;
self.currentPageIndexString = [NSString stringWithFormat:#"%ld",(long)self.sliderPageControll.currentPage];
}
Here implement your autotrotate method.
- (void)autoStartSlide:(id)sender {
if(pagger.numberOfPages > pagger.currentPage+1) {
[UIView animateWithDuration:0.5 animations:^{
slider.contentOffset = CGPointMake(slider.contentOffset.x + slider.frame.size.width, 0);
} completion:^(BOOL finished) {
}];
}
else {
slider.contentOffset = CGPointMake(slider.contentOffset.x - slider.frame.size.width, 0);
[UIView animateWithDuration:0.5 animations:^{
slider.contentOffset = CGPointMake(slider.contentOffset.x + slider.frame.size.width, 0);
} completion:^(BOOL finished) {
slider.contentOffset = CGPointMake(0, 0) ;
}];
}
}
Here is a Swift 5 Compatible one
var currentPage = 1;
var slideTimer : Timer?
ViewDidLoad
slideTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(scrollPages), userInfo: nil, repeats: true)
viewDidDisappear
slideTimer?.invalidate()
Functions
#objc func scrollPages() {
scrollToPage(page: CGFloat(currentPage%slides.count));
currentPage+=1;
}
func scrollToPage(page : CGFloat) {
let pageWidth = scrollView.frame.width
scrollView .setContentOffset(CGPoint(x: pageWidth * page, y: 0.0), animated: true)
}
I have enabled my Cocoa Touch app to be navigable by swiping left or right to alter positions in history. The animation is kind of done like Android's "card" style. Where swiping to the left (<--) just moves the current screen image out of the way, while showing the previous view beneath it.
This works fine, but when I want to swipe the other way (-->), to go back, I need to get the previous image, and move that over the current view. Now, I had this working if I only store the previous image, but what if I go <-- a few times, then I will not have enough images.
So, the solution is obvious, use an NSMutableArray and just throw the latest image at the front of the array, and when you swipe the other way, just use the first image in the array. However, the image never shows when I start the animation. It just shows nothing. Here's the required methods you should see:
-(void)animateBack {
CGRect screenRect = [self.view bounds];
UIImage *screen = [self captureScreen];
[imageArray insertObject:screen atIndex:0]; //Insert the screenshot at the first index
imgView = [[UIImageView alloc] initWithFrame:screenRect];
imgView.image = screen;
[imgView setHidden:NO];
NSLog(#"Center of imgView is x = %f, y = %f", imgView.center.x, imgView.center.y);
CGFloat startPointX = imgView.center.x;
CGFloat width = screenRect.size.width;
NSLog(#"Width = %f", width);
imgView.center = CGPointMake(imgView.center.x, imgView.center.y);
[self.view addSubview: imgView];
[self navigateBackInHistory];
[UIView animateWithDuration:.3 animations:^{
isSwiping = 1;
imgView.center = CGPointMake(startPointX - width, imgView.center.y);
} completion:^(BOOL finished){
// Your animation is finished
[self clearImage];
isSwiping = 0;
}];
}
-(void)animateForward {
CGRect screenRect = [self.view bounds];
//UIImage *screen = [self captureScreen];
UIImage *screen = [imageArray objectAtIndex:0]; //Get the latest image
imgView = [[UIImageView alloc] initWithFrame:screenRect];
imgView.image = screen;
[imgView setHidden:NO];
NSLog(#"Center of imgView is x = %f, y = %f", imgView.center.x, imgView.center.y);
CGFloat startPointX = imgView.center.x;
CGFloat width = screenRect.size.width;
NSLog(#"Width = %f", width);
imgView.center = CGPointMake(imgView.center.x - width, imgView.center.y);
[self.view addSubview: imgView];
[UIView animateWithDuration:.3 animations:^{
isSwiping = 1;
imgView.center = CGPointMake(startPointX, imgView.center.y);
} completion:^(BOOL finished){
// Your animation is finished
[self navigateForwardInHistory];
[self clearImage];
isSwiping = 0;
}];
}
-(void)clearImage {
[imgView setHidden:YES];
imgView.image = nil;
}
-(void)navigateBackInHistory {
[self saveItems:self];
[self alterIndexBack];
item = [[[LEItemStore sharedStore] allItems] objectAtIndex:currentHistoryIndex];
[self loadItems:self];
}
-(void)navigateForwardInHistory {
[imageArray removeObjectAtIndex:0]; //Remove the latest image, since we just finished swiping this way.
[self saveItems:self];
[self alterIndexForward];
item = [[[LEItemStore sharedStore] allItems] objectAtIndex:currentHistoryIndex];
[self loadItems:self];
}
Note that imgView is a class-level UIImageView and imageArray is a class level array.
Any ideas? Thanks.
Edit
Here's the code at the top of my .m to initalize it. Still does not work.
.....
NSMutableArray *imageArray;
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [imageArray initWithObjects:nil];
It looks like you forgot to create the array. Something like this at the appropriate time would do (assuming ARC):
imageArray = [NSMutableArray array];
Glad that worked out.
I have an UIImageView in a UITableviewCell. When it is tapped, the UIImageView should animated to be displayed fullscreen. When the image is tapped when it is fullscreen it should shrink back to the original position.
How can this be achieved?
Add a gesture recognizer to the view controller.
Add the gesture Recognizer to your header file
#interface viewController : UIViewController <UIGestureRecognizerDelegate>{
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
}
In your viewDidLoad add this:
isFullScreen = false;
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen)];
tap.delegate = self;
Add the following delegatemethod:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
{
BOOL shouldReceiveTouch = YES;
if (gestureRecognizer == tap) {
shouldReceiveTouch = (touch.view == yourImageView);
}
return shouldReceiveTouch;
}
Now you just need to implement your imgToFullScreen method.
Make sure you work with the isFullScreen Bool (fullscreen if it is false and back to old size if it's true)
The imgToFullScreen method depends on how you want to make the image become fullscreen.
One way would be:
(this is untested but should work)
-(void)imgToFullScreen{
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
//save previous frame
prevFrame = yourImageView.frame;
[yourImageView setFrame:[[UIScreen mainScreen] bounds]];
}completion:^(BOOL finished){
isFullScreen = true;
}];
return;
} else {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[yourImageView setFrame:prevFrame];
}completion:^(BOOL finished){
isFullScreen = false;
}];
return;
}
}
The code from #AzzUrr1, small error corrections (brackets) and tapper implemented slightly different.
Worked for me. Now it would be great to have this implemented with a scrollView, that the user can zoom in/out if the picture is bigger.. Any suggestion?
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>{
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
}
#property (nonatomic, strong) UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
isFullScreen = FALSE;
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen)];
tap.delegate = self;
self.view.backgroundColor = [UIColor purpleColor];
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[_imageView setClipsToBounds:YES];
_imageView.userInteractionEnabled = YES;
_imageView.image = [UIImage imageNamed:#"Muppetshow-2.png"];
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen:)];
tapper.numberOfTapsRequired = 1;
[_imageView addGestureRecognizer:tapper];
[self.view addSubview:_imageView];
}
-(void)imgToFullScreen:(UITapGestureRecognizer*)sender {
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
//save previous frame
prevFrame = _imageView.frame;
[_imageView setFrame:[[UIScreen mainScreen] bounds]];
}completion:^(BOOL finished){
isFullScreen = TRUE;
}];
return;
}
else{
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[_imageView setFrame:prevFrame];
}completion:^(BOOL finished){
isFullScreen = FALSE;
}];
return;
}
}
I ended up using MHFacebookImageViewer. Integration is easy, no subclassing UIImageView, and it also has image zooming and flick dismiss.
Although it requires AFNetworking (for loading larger image from URL), you can comment out some code (about 10 lines) to remove this dependency. I can post my AFNetworking-free version if someone needs it. Let me know :)
One possible implementation would be to use a modal view controller with UIModalPresentationFullScreen presentation style.
Just finished a version in swift, just download and add into your project:
GSSimpleImageView.swift
And usage:
let imageView = GSSimpleImageView(frame: CGRectMake(20, 100, 200, 200))
imageView.image = UIImage(named: "test2.png")
self.view.addSubview(imageView)
I am trying to make an inheritance of a UIButton. Which if clicked will play a sound , scales up. And when finishes playing sound scales back to the original state.
Everything is working except the image is not showing.
The title and background do scale up, and the sound is being played.
Anyone a hint?
soundButton.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#interface soundButton : UIButton <AVAudioRecorderDelegate, AVAudioPlayerDelegate>{
BOOL isPlaying;
AVAudioPlayer *audioPlayer;
}
#property (nonatomic)BOOL isPlaying;
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
- (id)initWithFrame:(CGRect)frame;
-(void)loadSound:(NSString*)audiofile;
-(void)touchUpInside;
#end
soundButton.m
#import "soundButton.h"
#implementation soundButton
#synthesize isPlaying;
#synthesize audioPlayer;
void audioRouteChangeListenerCallback (
void *inUserData,
AudioSessionPropertyID inPropertyID,
UInt32 inPropertyValueSize,
id *inPropertyValue);
-(BOOL)isPlaying{
return audioPlayer.isPlaying;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addTarget:self action:#selector(touchUpInside) forControlEvents:UIControlEventTouchUpInside];
UIImage *image = [UIImage imageNamed:#"kers.png"];
[self setBackgroundImage:image forState:UIControlStateNormal];
}
return self;
}
-(void)loadSound:(NSString*)audiofile{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:audiofile
ofType:nil]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer setVolume:1.0];
[audioPlayer prepareToPlay];
audioPlayer.currentTime = 0;
}
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
[audioPlayer setCurrentTime:0];
self.transform = CGAffineTransformMakeScale(1.5,1.5);
self.alpha = 1.0f;
[UIView beginAnimations:#"fish" context:nil];
[UIView setAnimationDuration:1];
self.transform = CGAffineTransformMakeScale(1.0,1.0);
self.alpha = 1.0f;
[UIView commitAnimations];
self.isPlaying = NO;
}
-(void)touchUpInside{
//Check if something is playing If YES=>stop it
if (self.isPlaying) {
[audioPlayer stop];
[audioPlayer setCurrentTime:0];
} else {
self.transform = CGAffineTransformMakeScale(1.0,1.0);
self.alpha = 1.0f;
[UIView beginAnimations:#"fish" context:nil];
[UIView setAnimationDuration:1];
self.transform = CGAffineTransformMakeScale(1.5,1.5);
self.alpha = 1.0f;
[UIView commitAnimations];
}
// Create an asynchronous background queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:
^{
[audioPlayer play];
}];
}
#end
implemented in ViewdidLoad in the viewController:
UIImage *image = [[UIImage alloc]initWithContentsOfFile:#"cherry.png"];
fishButton = [[soundButton alloc] initWithFrame:CGRectMake(323, 312, 123, 123)];
[fishButton setImage:image forState:UIControlStateNormal];
[fishButton loadSound:#"chime1.mp3"];
[fishButton setTitle:#"fish" forState:UIControlStateNormal];
[fishButton setBackgroundColor:[UIColor yellowColor]];
[fishButton setShowsTouchWhenHighlighted:YES];
[self.view addSubview:fishButton];
replace this
UIImage *image = [[UIImage alloc]initWithContentsOfFile:#"cherry.png"];
with this
UIImage *image = [UIImage imageNamed:#"cherry.png"];
or if you really need to use initWithContentsOfFile use URL link like
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"cherry" ofType:#"png"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:filePath];
This is my code for adding sound while UIScrollview is scrolling in iPhone SDK. In this code, how to add the sound while using touch-events in scrolling?
Please give me your idea and suggestion or sample.
const CGFloat kScrollObjHeight = 151.0;
const CGFloat kScrollObjWidth =320.0;
const NSUInteger kNumImages = 5;
- (void)layoutScrollImages
{
UIImageView *view = nil;
NSArray *subviews = [scrollView1 subviews];
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UIImageView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
[scrollView1 setContentSize:CGSizeMake((kNumImages * kScrollObjWidth), [scrollView1 bounds].size.height)];
}
- (void)viewDidLoad
{
self.view.backgroundColor = [UIColor clearColor];
[scrollView1 setBackgroundColor:[UIColor whiteColor]];
[scrollView1 setCanCancelContentTouches:NO];
scrollView1.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView1.clipsToBounds = YES;
scrollView1.scrollEnabled = YES;
scrollView1.pagingEnabled = YES;
NSUInteger i;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"snap%d.jpg", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i;
[scrollView1 addSubview:imageView];
[imageView release];
[self layoutScrollImages];
}
[super viewDidLoad];
}
add UIScrollViewDelegate to your interface and then play sound while the scrolling is in task in scrollViewWillBeginDragging state and stop scrolling while in scrollViewDidEndDragging state.
You can use AVAudioPlayer to play sound on iOS devices
UPDATE:
Tells the delegate when the scroll view is about to start scrolling the content.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
Also, Tells the delegate when dragging ended in the scroll view.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
Implement these in your code & put in your custom behavior
you mast use UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
//play sound here
}