how to set a rotated image as the image of a UIImageView after triggering UIRotationGesture? - uiimageview

I am creating an app, in which users can rotate an imageView on the screen, which will be saved as a UIImage for the next phase programatically. I have implemented all the required methods for rotating, which is done through UIRotationGesture, and everything works fine. The problem I have is that the rotation is not recorded for the image when I go to the next phase, and show the image in another view. I tried to add the gestureRecognizer to the UIImage of imageView, but I realised I can't do so. I also had a look at the Reset view after UIRotationGesture thread, and I think I'm having this problem because I haven't stored something, but as I'm new, I don't know what and how.
here's the code I used to rotate:
-(void)handleRotation:(UIRotationGestureRecognizer *)rotationGestureRecognizer
{
if([rotationGestureRecognizer state] == UIGestureRecognizerStateEnded) {
lastRotation = 0.0;
return;
}
CGFloat rotation = 0.0 - (lastRotation - [rotationGestureRecognizer rotation]);
CGAffineTransform currentTransform = [rotationGestureRecognizer view].transform;
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
[[rotationGestureRecognizer view] setTransform:newTransform];
lastRotation = [rotationGestureRecognizer rotation];
}
I'd appreciate it if anyone could shed a light on my problem.
Thanks a lot

Related

NSAnimation removes button background colour

I am working on a Mac app. I am trying to do a simple animation which makes an NSButton move down. The animation works really nicely, but when i do it, the background colour of my NSButton disappears for some reason. Here is my code:
// Tell the view to create a backing layer.
additionButton.wantsLayer = YES;
// Set the layer redraw policy. This would be better done in
// the initialization method of a NSView subclass instead of here.
additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.duration = 1.0f;
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
//additionButton.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
} completionHandler:nil];
Button move down animation:
Button after move down animation:
Update 1
Just to make it clear, I am not using a background image in my buttons. I am using a background NSColor which I set in the viewDidLoad method like so:
[[additionButton cell] setBackgroundColor:[NSColor colorWithRed:(100/255.0) green:(43/255.0) blue:(22/255.0) alpha:1.0]];
I presume this is an AppKit bug. There are a couple of ways you can work around it.
Workaround 1:
Don't use layers. The button you're animating seems to be small, and you might be able to get away with using a non layer-backed animation and still have it look decent. The button will redraw during each step of the animation, but it will animate correctly. That means this is really all you have to do:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
} completionHandler:nil];
Workaround 2:
Set the background color on the layer.
additionButton.wantsLayer = YES;
additionButton.layer.backgroundColor = NSColor.redColor.CGColor;
additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
} completionHandler:nil];
Workaround 3:
Subclass NSButtonCell, and implement -drawBezelWithFrame:inView:, drawing your background color there. Keep in mind that the parent view containing the button should be layer-backed, otherwise the button will still redraw on every step.

iCarousels Getting Stuck in iOS 7

I have implemented the iCarousels in my project. But after updating the app for iOS 7, my iCarousels are getting stuck in between. Its working fine in iOS 6 and 5. the problem in iOS 7 is that my scroll view below the iCarousel view is getting called first sometimes when I touch my carousel view. Can anyone help me out here?
Is the solution in the following method's return value:
- (CGFloat)carouselItemWidth:(iCarousel *)carousel
I tried many things here, and it works fine for few times, and again after some time it start getting stuck because the scroll view takes the touch from its subview(iCarousel view) and call its own delegate methods before the iCarousel's delegate method.
I am not using any gesture recognizer. I am using scroll view because i have iCarousel view and another view resting on UIScrollView, so that I can use pull to refresh as well.
The following delegate methods I am using, and changing in carouselItemWidth has decreased the stuck problem, but it still persists
- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
CGFloat tilt = 0.65f;
CGFloat spacing = 0.28f; // should be ~ 1/scrollSpeed;
CGFloat clampedOffset = fmaxf(-1.0f, fminf(1.5f, offset));
CGFloat itemWidth = 320;
CGFloat x = (clampedOffset * 0.5f * tilt + offset * spacing) * itemWidth;
CGFloat z = fabsf(clampedOffset) * -itemWidth * 0.5f;
transform = CATransform3DTranslate(transform, 0.0f, x, z);
transform = CATransform3DRotate(transform, -clampedOffset * M_PI_2 * tilt, -1.0f, 0.0f, 0.0f);
//DLog(#"offset: %f, %#", offset, [NSValue valueWithCATransform3D:transform]);
return transform;
}
- (NSUInteger)numberOfPlaceholdersInCarousel:(iCarousel *)carousel
{
//note: placeholder views are only displayed on some carousels if wrapping is disabled
return 0;
}
- (CGFloat)carouselItemWidth:(iCarousel *)carousel
{
//usually this should be slightly wider than the item views
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
{
return 270;
}
else
{
return 250;
}
}
- (BOOL)carouselShouldWrap:(iCarousel *)carousel
{
return NO;
}
The problem seems to be that when a scrollview receives a touch, it waits a second to see if it should handle it before passing it to the carousel.
You can (mostly) fix this by setting scrollView.delaysContentTouches = NO;
It's still a bit clunky if you try to swipe the carousel when the scrollview is moving/decelerating however. You will have to wait until it stops moving to interact with the carousel.
I'm investigating if there's a better way to fix this.
UPDATE:
I still don't have a proper general-purpose fix for this yet, but as a workaround, you can add this method to your local copy of iCarousel:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return [gesture isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
}
This forces iCarousel's pan gesture recogniser to take precedence over the one in the scrollView. If you combine this with the delaysContentTouches fix above, you shouldn't have any issues scrolling the carousel when it's inside a tableview or scrollview.

Soft scroll animation NSScrollView scrollToPoint:

I want to create soft animation between transitions in simply UI:
view that moved
When a call scrollToPoint: for move view to point that transition doesn't animate.
I'm newbe in Cocoa programming (iOS is my background). And I don't know how right use .animator or NSAnimationContext.
Also I was read Core Animation guide but didn't find the solution.
The source can be reach on Git Hub repository
Please help !!!
scrollToPoint is not animatable. Only animatable properties like bounds and position in NSAnimatablePropertyContainer are animated. You don't need to do anything with CALayer: remove the wantsLayer and CALayer stuff. Then with following code it is animated.
- (void)scrollToXPosition:(float)xCoord {
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:5.0];
NSClipView* clipView = [_scrollView contentView];
NSPoint newOrigin = [clipView bounds].origin;
newOrigin.x = xCoord;
[[clipView animator] setBoundsOrigin:newOrigin];
[_scrollView reflectScrolledClipView: [_scrollView contentView]]; // may not bee necessary
[NSAnimationContext endGrouping];
}
Swift 4 code of this answer
func scroll(toPoint: NSPoint, animationDuration: Double) {
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = animationDuration
let clipView = scrollView.contentView
clipView.animator().setBoundsOrigin(toPoint)
scrollView.reflectScrolledClipView(scrollView.contentView)
NSAnimationContext.endGrouping()
}
The proposed answers have a significant downside: If the user tries to scroll during an ongoing animation, the input will be cause jittering as the animation will forcefully keep on going until completion. If you set a really long animation duration, the issue becomes apparent. Here is my use case, animating a scroll view to snap to a section title (while trying to scroll up at the same time):
I propose the following subclass:
public class AnimatingScrollView: NSScrollView {
// This will override and cancel any running scroll animations
override public func scroll(_ clipView: NSClipView, to point: NSPoint) {
CATransaction.begin()
CATransaction.setDisableActions(true)
contentView.setBoundsOrigin(point)
CATransaction.commit()
super.scroll(clipView, to: point)
}
public func scroll(toPoint: NSPoint, animationDuration: Double) {
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = animationDuration
contentView.animator().setBoundsOrigin(toPoint)
reflectScrolledClipView(contentView)
NSAnimationContext.endGrouping()
}
}
By overriding the normal scroll(_ clipView: NSClipView, to point: NSPoint) (invoked when the user scrolls) and manually performing the a scroll inside a CATransaction with setDisableActions, we cancel the current animation. However, we don't call reflectScrolledClipView, instead we call super.scroll(clipView, to: point), which will perform other necessary internal procedures and then perform reflectScrolledClipView.
Above class produces better results:
Here is a Swift 4 extension version of Andrew's answer
extension NSScrollView {
func scroll(to point: NSPoint, animationDuration: Double) {
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = animationDuration
contentView.animator().setBoundsOrigin(point)
reflectScrolledClipView(contentView)
NSAnimationContext.endGrouping()
}
}
I know, it's a little bit off topic, but I wanted to have a similar method to scroll to a rectangle with animation like in UIView's scrollRectToVisible(_ rect: CGRect, animated: Bool) for my NSView. I was happy to find this post, but apparently the accepted answer doesn't always work correctly. It turns out that there is a problem with bounds.origin of the clipview. If the view is getting resized (e.g. by resizing the surrounding window) bounds.origin is somehow shifted against the true origin of the visible rectangle in y-direction. I could not figure out why and by how much. Well, there is also this statement in the Apple docs not to manipulate the clipview directly since its main purpose is to function internally as a scrolling machine for views.
But I do know the true origin of the visible area. It’s part of the clipview’s documentVisibleRect. So I take that origin for the calculation of the scrolled origin of the visibleRect and shift the bounds.origin of the clipview by the same amount, and voilà: that works even if the view is getting resized.
Here is my implementation of the new method of my NSView:
func scroll(toRect rect: CGRect, animationDuration duration: Double) {
if let scrollView = enclosingScrollView { // we do have a scroll view
let clipView = scrollView.contentView // and thats its clip view
var newOrigin = clipView.documentVisibleRect.origin // make a copy of the current origin
if newOrigin.x > rect.origin.x { // we are too far to the right
newOrigin.x = rect.origin.x // correct that
}
if rect.origin.x > newOrigin.x + clipView.documentVisibleRect.width - rect.width { // we are too far to the left
newOrigin.x = rect.origin.x - clipView.documentVisibleRect.width + rect.width // correct that
}
if newOrigin.y > rect.origin.y { // we are too low
newOrigin.y = rect.origin.y // correct that
}
if rect.origin.y > newOrigin.y + clipView.documentVisibleRect.height - rect.height { // we are too high
newOrigin.y = rect.origin.y - clipView.documentVisibleRect.height + rect.height // correct that
}
newOrigin.x += clipView.bounds.origin.x - clipView.documentVisibleRect.origin.x // match the new origin to bounds.origin
newOrigin.y += clipView.bounds.origin.y - clipView.documentVisibleRect.origin.y
NSAnimationContext.beginGrouping() // create the animation
NSAnimationContext.current.duration = duration // set its duration
clipView.animator().setBoundsOrigin(newOrigin) // set the new origin with animation
scrollView.reflectScrolledClipView(clipView) // and inform the scroll view about that
NSAnimationContext.endGrouping() // finaly do the animation
}
}
Please note, that I use flipped coordinates in my NSView to make it match the iOS behaviour.
BTW: the animation duration in the iOS version scrollRectToVisible is 0.3 seconds.
https://www.fpposchmann.de/animate-nsviews-scrolltovisible/

Cocos2d how to scale a sprite without scaling layer? Or, How to scale and crop sprite/layer?

iPad App setup: SceneA contains layerA - 1024x768. Push a button in layerA, layerB drops down over top using a CCMoveTo action. LayerB is only 800x600 so you can see layerA behind it (think of an overlayed pause screen type effect). LayerB contains an 800x600 sprite that the user can zoom in on by pressing a button. The zoom effect is simply a combination of CCScaleTo and CCMoveTo to keep it centered on the part it's zooming in on. However, when the sprite scales, so does layerB overtop of layerA. Is there a way to scale the sprite within a contained window?
LayerB should use the GL_SCISSOR_TEST to trim the outside of itself. You can easily google for more information about it, it basically defines a rect and then uses glScissoron it to remove the outside. I have a class I extend when I need to do this, that goes as follows:
//
// CCNodeClip.h
//
// Created by Ignacio Orlandoni on 7/29/11.
//
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface CCNodeClip : CCLayer {
}
-(void)preVisit;
-(void)postVisit;
#end
-
//
// CCNodeClip.m
//
// Created by Ignacio Orlandoni on 7/29/11.
//
#import "CCNodeClip.h"
#implementation CCNodeClip
-(void)visit {
[self preVisit];
[super visit];
[self postVisit];
}
-(void)preVisit {
if (!self.visible)
return;
glEnable(GL_SCISSOR_TEST);
CGPoint position = [self position];
//I don't remember if this rect really serves for both orientations, so you may need to change the order of the values here.
CGRect scissorRect = CGRectMake(position.x, position.y, [self contentSize].width, [self contentSize].height);
// CCLOG(#"Scrissor Rect: X: %02f, Y:%02f, W: %02f, H: %02f", scissorRect.origin.x, scissorRect.origin.y, scissorRect.size.width, scissorRect.size.height);
// Handle Retina
scissorRect = CC_RECT_POINTS_TO_PIXELS(scissorRect);
glScissor((GLint) scissorRect.origin.x, (GLint) scissorRect.origin.y,
(GLint) scissorRect.size.width, (GLint) scissorRect.size.height);
}
-(void)postVisit {
glDisable(GL_SCISSOR_TEST);
}
#end
With that imported into LayerB, you can now define it as a CCNodeClip instead of CCLayer.
Some links...
glScissor << cocos2d Forum
Circle shape clipping with opengl-es in cocos2d << StackOverflow
Cocos2d iPhone - Sprite cliping/mask/frame << StackOverflow
Another Cocos2D gem: ClippingNode << Learn-Cocos2d.com
As a side note...
CCScaleTo + CCMoveTo can be avoided if the anchor point for the sprite is centered, so the image stays centered in the container as it scales. (.anchorPoint = ccp(0.5, 0.5);)

PinthToZoom and Pan of UIView with multiple images

I'm developing an iphone application with an UIView that have an image as a background and multiple buttons with image as background. The image is an image of a country and the buttons are the regions/states of that country.
The problem is that the screen is too much little to display all this data, so I want to enable pinch-to-zoom of the UIView which allows user to have a better view of the country.
Inside 'viewDiDLoad' of my UIViewController, I added:
UIPinchGestureRecognizer *twoFingerPinch = [[[UIPinchGestureRecognizer alloc]
initWithTarget:self
action:#selector(twoFingerPinch:)]
autorelease];
[[self view] addGestureRecognizer:twoFingerPinch];
and I added twoFingerPinch method:
- (void)twoFingerPinch:(UIPinchGestureRecognizer *)recognizer {
if (recognizer.scale > minValue){
CGAffineTransform transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale);
self.view.transform = transform;
}
}
and it work correctly, the problem is that the UIView becomes bigger but user can't navigate inside.
So I switched my UIView to a UIScrollView and inside 'twoFingerPinch' method I added:
if (recognizer.state == UIGestureRecognizerStateEnded){
float scaleForView = mLastScale;
UIScrollView *tempScrollView=(UIScrollView *)self.view;
int newWidth = self.view.frame.size.width * scaleForView;
int newHeight = self.view.frame.size.height * scaleForView;
tempScrollView.contentSize=CGSizeMake(newWidth,newHeight);
mLastScale = 1.0;
}
but it doesn't work well: the UISrollView becomes much bigger but it's not bigger as the image scaled and also is not centered where pinchToZoom started.
How can i solve this problem?
Thank You

Resources