momentum rotation of image in iOS - ios

I'm new to iOS graphics and animation and would like to know to how to accomplish the effect of momentum rotation of an image upon flick as seen in the video below.
http://www.youtube.com/watch?v=ZIQs-OWgkgI (now broken)
Even when not flicked the images have a nice sway.
Thanks.

That is pretty slick. It is rotating the view around a point (center of top) and then changing the amount of time it takes to rotate algorithmically, reversing when appropriate. I can't give you code, but I think if you watch this video demo from Prof. Hegarty you will have the tools you need. He rotates around a point outside the view - you can simply rotate around the edge of the view (and don't shrink the view). Check it out:
Part 1: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/node/291
Part 2: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/node/293
You'll definitely want to download the videos off iTunes U (free), as there is a lot of explanation.
Good luck,
Damien

I checked with the author of this and was informed that it was implemented using the Box2D physics library. I'm going to give that a shot. Thanks for the responses!

//this code can be used to rotate an image having both back and front
rotate = [UIButton buttonWithType:UIButtonTypeCustom];
[rotate addTarget:self action:#selector(rotate1)forControlEvents:UIControlEventTouchDown];
rotate.frame = CGRectMake(137.5, 245, 45, 46);
[rotate setBackgroundImage:[UIImage imageNamed:[NSString stringWithFormat:#"viewing.png"]] forState:UIControlStateNormal];
[self.view addSubview:rotate];
int count;
count=0;
-(void)rotate1
{
count=count+1;
NSLog(#"rotate");
[UIView transitionWithView:imagecircle // use the forView: argument
duration:1 // use the setAnimationDuration: argument
options:UIViewAnimationOptionTransitionFlipFromLeft
// check UIViewAnimationOptions for what options you can use
animations:^{ // put the animation block here
imagecircle.image = imagecircle.image;
}
completion:NULL];
if(count%2==0)
{
NSLog(#"image.str.%#",appDelegate.imageNameString);
[imagecircle setImage:[UIImage imageNamed:appDelegate.imageNameString]];
[labellocation removeFromSuperview];
[labeldate removeFromSuperview];
[self.imagecircle addSubview:labelfrom];
}
else
{
[imagecircle setImage:[UIImage imageNamed:#"TGP_BACK.png"]];
[labelfrom removeFromSuperview];
[self.imagecircle addSubview:labellocation];
[self.imagecircle addSubview:labeldate];
}
}

Related

Vuforia IOS cylinder example - Add button

I have successfully built the sample application from Vuforia. I am interested in taking the Cylinder Tracking and replacing the soccer ball that rotates around the soda can with a button that does not rotate but sits at a certain position in relation to the can (the center of the tracking image).
I am new to iOS development and am not sure quite how to do this. Ideally this would be done programmatically.
I was able to add a button to the CylinderTargetsViewController.mm using the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:#"Gossamer Rules" forState:UIControlStateNormal];
[button sizeToFit];
button.center = CGPointMake(320/2, 60);
[button addTarget:self action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)buttonPressed:(UIButton *)button {
UIApplication *mySafari = [UIApplication sharedApplication];
NSURL *myURL = [[NSURL alloc]initWithString:#"http://www.example.com"];
[mySafari openURL:myURL];
}
However this adds a button to the screen whether the target is found or not and it has no spatial relationship to the target.
I tried moving this code into CylinderTargetsEAGLView.mm but got an error on line [self.view addSubview:button]; saying view was not a property of self.
I realize this is probably not the right approach. I'm thinking the button needs to be rendered as an openGl object or something so that it is 3 dimensional and can be seen from an angle when moving the target.
Has anyone done this or have any clue how it may be done?
I have decided to use the Vuforia Unity Package as there is a much steeper learning curve trying to learn pure OpenGL programming. The Unity route has much larger community support, and more tutorials / information are available.

How To Remove pre - Fill of UISlider

I am having troubles with implementation if UISlider.
everything else is working fine except that when Slider thumb is at its minimum position it should not show minimum track image at all.
Screen shot of my iPad Simulator is as follows :-
in this picture, the thumb nob is at its minimum position but still it is showing "green minimum Track image" which it shouldn't
is there any way to come around this problem ?
code in my viewDidLoad method is :-
SLDR.continuous=NO;
[SLDR setMinimumTrackImage:[[UIImage imageNamed:#"U14.png"] stretchableImageWithLeftCapWidth:7 topCapHeight:0]forState:UIControlStateNormal];
[SLDR setMaximumTrackImage:[[UIImage imageNamed:#"U31.png"] stretchableImageWithLeftCapWidth:0 topCapHeight:0] forState:UIControlStateNormal];
[SLDR setThumbImage:[UIImage imageNamed:#"U9.png"] forState:UIControlStateNormal];
[SLDR setThumbImage:[UIImage imageNamed:#"U9.png"] forState:UIControlStateHighlighted];
Please tell me what am i missing, Thanks in advance :)
The standard slider thumbImage sits on top of the slider so it covers min positions. Yours is above so the end is exposed. Try creating a new image, by flipping your max image horizontally. Then add a valueCahnged to the slider like this...
- (IBAction)sliderValueChanged:(id)sender{
if (SLDR.value == 0) {
[SLDR setMinimumTrackImage:[[UIImage imageNamed:#"U31-flipped.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:0]forState:UIControlStateNormal];
} else {
[SLDR setMinimumTrackImage:[[UIImage imageNamed:#"U14.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:0]forState:UIControlStateNormal];
}
Hope this helps!

UIScrollView scrolling too slowly and never calling scrollViewDidEndDecelerating

The iPad app I'm working on is a book. To jump to a specific page, the user can press a button that overlays a view top of the current view, displaying images of thumbnails of each page in the book.
When the user goes through the book sequentially and displays this thumbnails menu, the scrolling animation is smooth and fine if the user showed the menu . The problem happens if the user calls showBookmarkMenu after having loaded about fifteen pages, the scrollview animation is very very slow, and the scrollview doesn't catch touches anymore.
I noticed that scrollViewDidEndDecelerating gets called when the scrolling animation is normal and smooth (shortly after loading the app), but it doesn't get called after the user has gone through several pages. So one hypothesis is that the CPU is struggling with the animation of the positioning of the scrollview's content. I ran the app using Instruments' Activity Monitor, but there are times when the app uses 97% and more of the CPU and the scrollview scrolls fine...
Any thoughts on this issue? I've posted my code below.
MainClass.m
//Called when user presses the open/close bookmark menu button
-(IBAction)toggleBookmarksMenu{
if([bookMarkMenu isHidden]){
[currentPage.view addSubview:bookMarkMenu];
[bookMarkMenu showBookmarkMenu];
}
else{
[bookMarkMenu hideBookmarksMenu];
}
}
ScrollViewClass.h
#interface BookmarkManager : UIView<UIScrollViewDelegate>{
UIScrollView *thumbnailScrollView;
}
#property (strong, nonatomic) UIScrollView *thumbnailScrollView;
#property (strong) id <BookmarkManagerDelegate> bookmarkManagerDelegate;
-(void)showBookmarkMenu;
-(void)hideBookmarksMenu;
#end
ScrollViewClass.m
-(void)showBookmarkMenu{
[self setHidden:NO];
[UIView animateWithDuration:0.5
animations:^{
self.center = CGPointMake(512, 384);
}
];
}
-(void)hideBookmarksMenu{
[UIView animateWithDuration:1
animations:^{
self.center = CGPointMake(512, -120);
}
completion:^(BOOL finished){
[self setHidden:YES];
[self removeFromSuperview];
}
];
}
-(id)init{
self = [super initWithFrame:CGRectMake(0, 0, 1024, 768)];
if(self){
[self setBackgroundColor:[UIColor clearColor]];
self.center = CGPointMake(512, 0);
thumbnailScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 120)];
[thumbnailScrollView setBackgroundColor:[UIColor clearColor]];
thumbnailScrollView.showsHorizontalScrollIndicator = NO;
//Add the UIButtons with images of the thumbnails
for(int i = 0; i < totalPages; i++){
UIButton *pageThumbnail = [UIButton buttonWithType:UIButtonTypeCustom];
pageThumbnail.frame = CGRectMake(0, 0, 125, 95);
[pageThumbnail setBackgroundImage:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/p%d_thumb.png", [[NSBundle mainBundle] resourcePath], i]] forState:UIControlStateNormal];
[thumbnailScrollView addSubview:pageThumbnail];
[pageThumbnail addTarget:self action:#selector(thumbnailTapped:) forControlEvents:UIControlEventTouchDown];
}
[self addSubview:thumbnailScrollView];
[thumbnailScrollView setContentSize:CGSizeMake(totalPages * 125 + (20*(totalPages+1)), 120)];
[thumbnailScrollView setDelegate:self];
[self setHidden:YES];
}
return self;
}
I have to go with possible low memory issue.
A possible alternative to using a slew of buttons is using UITableView. The way your code is currently working, it loads up ALL the buttons with images. For a large book this could be painful.
Using UITableView you only use as much memory as you see (about). And, since each image is loaded dynamically, your memory usage is only as much as is displayed. That would be how I would go about it (actually, I'm doing that now, just not with a book).
A shot in the dark, based on your observation that the scrolling becomes slow after loading 15 pages or so: possibly your device is busy handling a low memory condition. In such cases, as you possibly know, a system wide notification is sent to a considerable number of apps/objects for them to recover as much memory as possible.
Could you check if at more or less the same time when the scrolling becomes slow your app is executing didReceiveMemoryWarning?
If you confirm that the issue could be related to memory saturation/reclaiming, then I would suggest implementing a lazy loading scheme for your images:
you only load images when you are required to display them;
you only keep in memory 3-5 images total, to ensure a smooth scrolling.
The basic step requires id providing your delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
implementation. Here you will preload images:
knowing your position, you know your current image (say, image number N);
unload images N-2, N+2;
load images N-1, N+1.
The images to load/unload I provided are fine if you just want one "buffer" image.
In any case, if you google "iso scroll view lazy loading" you will find plenty of info.
Turns out it wasn't a low memory issue, but an overly busy CPU issue.
It is the CPU that does the calculations required for the scrollview's scrolling animations, and when the scrolling becomes this slow I thought I'd try to figure out why I was using 97% of the CPU in the first place. Turns out that past page 15, I had CPU-intensive recursive functions (calculating UIBezierPaths for another part of the app) caught in an infinite loop. The app was calculating hundreds of UIBezierPaths a second, and there reached a point where the CPU just couldn't keep up with the calculations for the scrollview's animation.
Once I made sure the recursive functions stopped calling themselves when they were not needed, CPU usage remained under 20% throughout the app, and the scrollview performed perfectly well.

iPhone Custom UISlider preload and rounded edges issues

I'm trying to implement a custom UISlider, I've extended it with a class called UISliderCustom which has the following code:
#implementation UISliderCustom
- (id)initWithCoder:(NSCoder *)aDecoder{
if(self == [super initWithCoder:aDecoder]){
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, 200, 13);
UIImage *slideMin = [[UIImage imageNamed:#"slideMinimum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
UIImage *slideMax = [[UIImage imageNamed:#"slideMaximum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
[self setThumbImage:[UIImage imageNamed:#"slideThumb.png"] forState:UIControlStateNormal];
[self setThumbImage:[UIImage imageNamed:#"slideThumb.png"] forState:UIControlStateHighlighted];
[self setMinimumTrackImage:slideMin forState:UIControlStateNormal];
[self setMaximumTrackImage:slideMax forState:UIControlStateNormal];
}
return self;
}
#end
I ran into two small problems
When I slide over the slider to one of the edges (progress = 0.0 / progress = 1.0), I can clearly see "left overs" in the sides, im not sure how to handle that as well, unfortunately :)
Slider images:
Problem:
I see the regular UISlider (blue and silver) for a couple of seconds, and only then the custom graphics is loaded, or when i actually move the slider. I'm not sure why this is happening.. EDIT: This only happens in the simulator, works fine now.
Thanks in advance for any assistance :)
Shai.
You have no need to subclass UISlider to achieve this effect, and if you did you certainly wouldn't set the track images in the drawRect method. drawRect should contain drawing code only, it is called whenever any part of the control needs redrawing.
Set the thumb and track images in a separate method, either within your subclass (called from initWithFrame and initWithCoder) or in the object that creates the slider in the first place. This only needs to be done once, when the slider is first created. Don't override drawRect.
You don't need to call awakeFromNib manually either, unless you have some specific code in there as well? That would be a common place to set custom images in a subclass, if you only ever used the slider from IB.
For the square ends, the problem is that the extreme edge of your track image is square, so it is showing around the thumb. Make both ends of the track image rounded, with a 1px stretchable area in the middle, like this:
I just had a very similar problem myself. It turned out that the size (width x height) of the slider that I added in interface builder didn't match the sizes of the images I was using to customize the slider. Once I made them match, those "leftovers" at the ends of the slider went away.

UIButton doesn't listen to content mode setting?

firstButton is a UIButton of type Custom. I'm programmatically putting three of them across each cell of a table, thusly:
[firstButton setImage:markImage forState:UIControlStateNormal];
[firstButton setContentMode:UIViewContentModeScaleAspectFit];
[cell.contentView addSubview:firstButton];
Elsewhere, I'm telling it to clipToBounds. What I get is a crop of the center square of the image, rather than an aspect-scaled rendering of it. I've tried this lots of ways, including setting the mode property on firstButton.imageView, which also doesn't seem to work.
I had the same problem. I see this question is a little old, but I want to provide a clear and correct answer to save other folks (like me) some time when it pops up in their search results.
It took me a bit of searching and experimenting, but I found the solution. Simply set the ContentMode of the "hidden" ImageView that is inside the UIButton.
[[firstButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[firstButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
Perhaps that's what Dan Ray was alluding to in his accepted answer, but I suspect not.
If you're dealing with the UIButton's image (as opposed to it's backgroundImage), setting the contentMode on the UIButton itself or on its imageView has no effect (despite what other answers say).
Alternatively do this instead:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Or size your image accordingly.
OR just use a UIImageView (which properly respects contentMode) with a UITapGestureRecognizer attached to it, or a transparent UIButton on top of it.
Rather than setting the contentMode on the button itself, you'll want to set contentHorizontalAlignment and contentVerticalAlignment properties and (crucially) the contentMode for the button's imageView for any kind of aspect fill or fit:
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.imageView?.contentMode = .scaleAspectFit
You can also do other things like aligning the button's image to the top. If you don't need an aspect fill or fit, you just can set the alignment by itself:
button.contentVerticalAlignment = .top
After a couple of hours of confusion, here's how I got it to work under iOS 3.2. As dusker mentioned, using setBackgroundImage instead of setImage did the job for me.
CGRect myButtonFrame = CGRectMake(0, 0, 250, 250);
UIImage *myButtonImage = [UIImage imageNamed:#"buttonImage"];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:myButtonImage forState:UIControlStateNormal];
[myButton setFrame: myButtonFrame];
[myButton setContentMode: UIViewContentModeScaleAspectFit];
The answer is to use a UIImageView with all the lovely Content Mode settings you want, and then layer a custom button on top of it. Dumb that you can't do that all in one shot, but it appears that you can't.
These two things (which are quite hard to find initially) will stretch your UIButton image to fit the button size:
one should always try to set such in the Storyboard rather than code.
Found a fix for this. Set the adjustsImageWhenHighlighted property of UIButton to NO.
UIButton *b = [[UIButton alloc] initWithFrame:rect];
[b setImage:image forState:UIControlStateNormal];
[b.imageView setContentMode:UIViewContentModeScaleAspectFill];
[b setAdjustsImageWhenHighlighted:NO];
Hope this helps. Feel free to comment below, I will follow up on any questions that you have.
My answer is similar to Kuba's. I needed my image to be programatically set.
UIImage *image = [[UIImage alloc] initWithContentsOfFile:...];
[button setBackgroundImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill; //this is needed for some reason, won't work without it.
for(UIView *view in button.subviews) {
view.contentMode = UIViewContentModeScaleAspectFill;
}
Only solution which worked for me:
[button setImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Swift 3
self.firstButton.imageView?.contentMode = .scaleAspectFill
For anyone experiencing this on iOS 15 and Xcode 13, see Matt's answer in this other question.
The behavior of Xcode changed and now defaults UIButtons from the library to the plain style, which prevents the child image from scaling as expected.
Instead of setImage try setBackgroundImage
I believe we have a simple interface builder issue here - apparently the IB ignores any content-mode changes AFTER you have set the image-property.
the solution is as simple: set the content mode, remove previously set image-names (make sure you remove it in all states, default, highlighted etc.), then re-enter the desired image-names in all desired states - et voilĂ .
I also advice to have a look at the adjustsImageWhenHighlighted UIButton property to avoid weird deformations of the image, when the button is pressed.
In trying to figure this out, my method got a bit hackier as time went on, and I wound up subclassing UIButton and overriding setHighlighted:
For me it works to just knock down the image alpha to .5, because they're on a black background.
However, it only works if I comment out [super setHighlighted:] (where it appears the image-stretchy code is going on), which just doesn't feel like the right way to solve this at all...everything seems to be working fine, though. We'll see how it holds up as I keep working on it.
- (void)setHighlighted:(BOOL)highlight {
if (highlight) {
[self.imageView setAlpha:.5];
} else {
[self.imageView setAlpha:1];
}
// [super setHighlighted:highlight];
}
If anyone looking for answer that work in iOS 6 and iOS 7 and storyboard:
You can set image in your storyboard:
And then:
for(UIView* testId in self.subviews) {
if([testId isKindOfClass:[UIImageView class]])
[testId setContentMode:UIViewContentModeScaleAspectFill];
}
If the UIButton does not seem to listen to the layout constraint settings, do check whether the images are larger than the button size. Always use the #2x and #3x images for retina resolutions.

Resources