How can I swipe between two UIViews using Pan Gesture Handler with Swift?
I would like to be able to swipe left and begin to reveal the next UIView, but snap back if the gesture isn't complete (to a certain point).
Currently using Swipe Gesture (but it doesn't animate and show preview halfway through)
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.Right:
println("Swiped right")
loadView1()
case UISwipeGestureRecognizerDirection.Left:
println("Swiped left")
loadView2()
default:
break
}
}
}
How can I make the gesture start revealing the next UIView and snap back if the gesture isn't complete?
You should check the start and end position of your swipe and load the different views if the distance isn't long enough:
var startLocation:CGPoint!
var endLocation:CGPoint!
if(gesture.state == .Began){
startLocation = gesture.locationInView(self.view)
} else if(gesture.state == .Ended){
endLocation = gesture.locationInView(self.view)
var distance = endLocation.x - startLocation.x
}
Related
I am trying to custom annotate images on image view using CAShapeLayers. This include adding shapes(like rectangle, ellipse, line) over the image view, touch them, move them around and pinch-zoom them. I am able to recognise the touch of the layer over the image view, but unsuccessful when tried to move it around. As I try to move the layer, it flies to a corner.
Code and output as below:
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func drawRectangle(_ sender: UIButton) { // to initially place the shape at the centre of the image view.
drawRectangleWithRect(CGRect(x: imageView.bounds.midX - 100, y: imageView.bounds.midY - 100, width: 200, height: 200))
}
func drawRectangleWithRect(_ rect: CGRect) {
let path = UIBezierPath(rect: rect)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
//shapeLayer.bounds = (shapeLayer.path?.boundingBox)!
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 1
imageView.layer.addSublayer(shapeLayer)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let point = touch!.location(in: imageView)
guard let sublayers = imageView.layer.sublayers else { return }
for sublayer in sublayers {
if let sublayer = sublayer as? CAShapeLayer, let path = sublayer.path {
//if path.contains(point) {
sublayer.position = point
//}
}
}
}
}
Tapping on Rectangle button creates the layer at the centre of image view as above. But when I start to touch and move the layer just a bit it goes off to the right bottom corner and disappears.
Now tweaked a bit - uncommented the line shapeLayer.bounds = (shapeLayer.path?.boundingBox)! inside the method drawRectangleWithRect(_ rect: CGRect). Then tapping on the Rectangle button placed the layer at the left top of the view and not at the centre :( . But moving it around now made the layer move around with my finger (though there is a delay). But also that this made the layer move even when image view is touched outside the layer. I understand that some if condition inside the touchesMoved method is needed that properly recognises the layer area but the commented one in the above code did not work properly.
What am I missing to properly place the layer at centre of the view and move it around without any delay ?
Can I actually pinch zoom the layers effectively ?
What is the best recommended way to annotate images like this ? Adding layers with touch recognition or adding shapes as subviews with pan gestures ?
I just struggled quite a while with this, so will document it for others.
Here's the problem I was having. With an iPad app, supporting iOS7, I have a modal view controller that has a text field near the bottom the modal. Thus, when the keyboard appears, I wanted to move that modal up so the text field would still be visible with the keyboard present. With iOS8, this problem has a pretty clean solution (e.g., see Moving a modally presented UIViewController up when keyboard appears on iPad with iOS8). With iOS7 I was using self.myNavController.view.superview.center for repositioning, but ran into problems when trying to move the modal given the appearance of the keyboard. The coordinate CGPoint adjustments I was using would not move the modal in the right direction with all four rotations/orientations of the iPad.
The problem in part lies in how iOS7 does the rotation-- with transforms. However, I was unable to resolve the issue using CGPointApplyAffineTransform, or conversion of points using views (e.g., convertPoint:fromView:).
The solution I found to this problem involved a few steps:
1) I found it necessary to change the center of the modal (an assignment to self.myNavController.view.superview.center) relative to the center of the screen. I computed the center of the screen based on [UIScreen mainScreen].bounds.size. For some example code, I used the method screenCenter below.
// Adapted from: http://stackoverflow.com/questions/24150359/is-uiscreen-mainscreen-bounds-size-becoming-orientation-dependent-in-ios8
+ (CGSize) screenSize;
{
CGSize screenSize = [UIScreen mainScreen].bounds.size;
CGSize rotatedSize;
if ([UIDevice ios7OrEarlier] && [[SMRotation session] isLandscape]) {
rotatedSize = CGSizeMake(screenSize.height, screenSize.width);
}
else {
rotatedSize = screenSize;
}
return rotatedSize;
}
+ (CGPoint) screenCenter;
{
CGSize size = [self screenSize];
CGPoint center = CGPointMake(size.width/2.0, size.height/2);
return center;
}
2) Now, given that you have computed the amount that you have to shift the modal upwards (e.g., given the keyboard height and your modal height and the position of the text field on the modal), call this amount dy. I next found it necessary if the app was in an inverted rotation (upside down portrait or landscape), to change the sign of dy before applying it to the CGPoint center position I was calculating. Something like this:
CGPoint newCenter = [SMRotation screenCenter];
if ([SMRotation session].isInverted) {
dy = -dy;
}
newCenter.y += dy;
With some of the code for isInverted here:
- (BOOL) isInverted;
{
switch (self.interfaceOrientation) {
case UIInterfaceOrientationPortraitUpsideDown:
case UIInterfaceOrientationLandscapeRight:
return YES;
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationUnknown:
return NO;
}
}
3) Then, if the app was in landscape I found it necessary to swap the x and y coordinates. Something like this:
if ([SMRotation session].isLandscape) {
newCenter = CGPointMake(newCenter.y, newCenter.x);
}
4 Finally, I did the assignment to update the center of the modal:
self.myNavController.view.superview.center = newCenter;
I'm building a new project using ECSlidingViewController v2. The first time I swipe to open the left under view, it works great. But when I swipe the second time, I get an EXC_BAD_ACCESS crash. The reference is to the NSMapTable in the controller.
- (void)updateTopViewGestures {
BOOL topViewIsAnchored = self.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredLeft ||
self.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredRight;
UIView *topView = self.topViewController.view;
if (topViewIsAnchored) {
...
} else {
...
---> [self.customAnchoredGesturesViewMap removeAllObjects];
}
}
Taking reference from #uiroshan answer Closing ECSlidingViewController menu
You can use the following code:
- (IBAction)showSlidingMenu:(id)sender
{
[self.slidingViewController anchorTopViewToRightAnimated:YES];
if ([self.slidingViewController currentTopViewPosition] == ECSlidingViewControllerTopViewPositionAnchoredRight)
{
[self.slidingViewController resetTopViewAnimated:YES];
}
}
Here,
I am animating the top view controller to Right, so I am checking whether top view position is ECSlidingViewControllerTopViewPositionAnchoredRight
To animating the top view controller to Left
ECSlidingViewControllerTopViewPositionAnchoredLeft
How can one detect when a drag is happening or a swipe on the track-pad or magic-mouse is occurring in a Mac OSX app being made in Xcode.
By drag I mean the user has clicked on the left or right edge of the window and the mouse is held down and now moving away from that side-of-the-window horizontally.
I am trying to run code on left drag or right swipe (on magic-mouse or track-pad) and another set of code on right drag or left swipe (on magic-mouse or track-pad).
Here are some definitions of the gestures that I am talking about:
A left drag is when the right side of the window is clicked and held on and the cursor moves to the left.
A right drag is when the left side of the window is clicked and held on and the cursor moves to the right.
A top drag is when the top on the window, under the frame / where the traffic-lights are, is being dragged down.
A top swipe is a swipe that starts on the top of the track-pad or magic-mouse and goes down.
In pseudo-code what I am trying to achieve is like:
if( right-drag || left-swipe ){
/*run code*/
}
else if( left-drag || right-swipe ){
/* run different code */
}
else if( top-drag || top-swipe ){
/* run other code */
}
else{
/* do nothing */
}
Cocoa Event Handling Guide is the place to start
Especially the part on Handling Gesture Events.
They tell you how to deal with:
Pinching movements (in or out) are gestures meaning zoom out or zoom
in (also called magnification).
Two fingers moving in opposite semicircles is a gesture meaning
rotate.
Three fingers brushing across the trackpad surface in a common
direction is a swipe gesture.
Two fingers moving vertically or horizontally is a scroll gesture.
and more...
In particular the method:
- (void)swipeWithEvent:(NSEvent *)event
from NSResponder is your best bet. T*his event informs the receiver that the user has begun a swipe gesture. The event will be sent to the view under the touch in the key window.*
Taken from the same document, below is an example on how to Handle s swipe gesture:
- (void)swipeWithEvent:(NSEvent *)event {
CGFloat x = [event deltaX];
CGFloat y = [event deltaY];
if (x != 0) {
swipeColorValue = (x > 0) ? SwipeLeftGreen : SwipeRightBlue;
}
if (y != 0) {
swipeColorValue = (y > 0) ? SwipeUpRed : SwipeDownYellow;
}
NSString *direction;
switch (swipeColorValue) {
case SwipeLeftGreen:
direction = #"left";
break;
case SwipeRightBlue:
direction = #"right";
break;
case SwipeUpRed:
direction = #"up";
break;
case SwipeDownYellow:
default:
direction = #"down";
break;
}
[resultsField setStringValue:[NSString stringWithFormat:#"Swipe %#", direction]];
[self setNeedsDisplay:YES];
}
Cocos2d is prety new for me so i don't know what i should do with this situation:
I want to make a game thats something like risk. Now i made a background image like a world map (just to test). and on this map i want a swipe gesture so i can move accross the map on my ipad ( the map is prety big so i want to swipe it arround).
My problem is i don't know what the objects are called i should use. And how i can implement the gestures the best way (do i need to calculate the movement myself?).
Thanks!
Stefan.
You could maybe connect UIKit's Pan Gesture Recognizer to CCDirector's view and handle pan gesture in CCLayer class. In that way, you could have handle method which moves background with every pan movement. (Code with cocos2d 1.0.1, similar could be done with 2.0 version)
UIPanGestureRecognizer* pan = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)] autorelease];
CCDirector* director = [CCDirector sharedDirector];
[[director openGLView] addGestureRecognizer:pan];
Handler method would look like this:
- (void)handlePanGesture:(UIGestureRecognizer*)gestureRecognizer {
// If there is more than one pan gesture recognizer connected with this method, you should remember pan and check if gestureRecognizer is equal to pan
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan: {
// Do something that needs to be done when pan gesture started
break;
}
case UIGestureRecognizerStateChanged: {
// Get pan gesture recognizer translation
CGPoint translation = [(UIPanGestureRecognizer*)gestureRecognizer translationInView:gestureRecognizer.view];
// Invert Y since position and offset are calculated in gl coordinates
translation = ccp(translation.x, -translation.y);
// Here you should move your background, probably in oposite direction of translation vector, something like
background.position = ccp(background.position.x - translation.x, background.position.y - translation.y);
// Refresh pan gesture recognizer
[(UIPanGestureRecognizer*)gestureRecognizer setTranslation:CGPointZero inView:gestureRecognizer.view];
break;
}
case UIGestureRecognizerStateEnded: {
// Do some work that should be done after panning is finished
break;
}
default:
break;
}
}
I think you're looking for this to add an object:
CCSprite *objectName = [CCSprite spriteWithFile:#"fileName.png"];
[self addChild:objectName];
By default, I believe the object will be in the bottom left corner.