I have been trying to implement an animation of sorts where a view rotates about a point when the user drags across the screen. I have implemented the part of it that controls the rotating (the part pertaining to this question). You can view all the code I have here if you would like to (it's short).
The code that controls the rotation is mainly in touchesBegan and touchesMoved:
var p = CGPoint.zero
// ...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
p = (touches.first?.location(in: self))!
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let dev = p.x - (touches.first?.location(in: self).x)!
let angle = 2 * CGFloat(M_PI) - atan(dev / p.y)
self.transform = CGAffineTransform(rotationAngle: angle)
}
Now here is the problem: when my finger drags below the initial middle of the view (the middle before any rotation), it works perfectly fine; however, when my finger drags above the initial middle of the view, the view is rotated to the wrong amount, and it jumps back and forth between two positions really fast, so it looks like there is a copy of the view.
I don't know if it is not working because of my general math/logic or with my implementation.
Why does it not work correctly when I drag above the initial middle of the view, and how can I fix it?
Related
So cell contains attributed text, with all content(sentence) tappable.(This part is working fine.)
And now need to get CGPoint on screen where user taps on any of content to show a popup menu to give multiple options.
Code that I tried, but it's not gets called as i have attributed text which is clickable. If i remove clickable attributed text then this function gets called.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let point = touches.first?.location(in: self.view)
}
Am I on the right path trying to drag an item from a UIScrollView and drop it over a second UIScrollView? Neither of the UIScrollViews will be altered in the end, but I'd like to have an image follow the touchMoved position until it's released over second UIScrollView.
I have extended the UIScrollView so I can see where a touch begins inside the UIScrollView.
extension UIScrollView {
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.next?.touchesBegan(touches, with: event)
print("touchesBegan")
}
}
to get the touched object
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
beginningLocation = touch.location(in: self.view)
let position = touch.location(in: itemScrollview)
print("itemScrollview: \(position)")
let whoHit = Int(position.x / 120)
print("whoHit: \(whoHit) \(GameData.items[whoHit].name)")
}
}
I'm trying to use touchesEnded to see if the user dragged up, but touchesEnded doesn't always get called. Works if the touches begin and end over the same UIScrollView, but when dragged off, touchesEnd doesn't get called.
Yo cannot "directly" drag an object (view, image view, label, button, etc) from one view to another (whether it's a UIView or UIScrollView or any other container).
Instead, you need to "move" the object you want to drag from the containing view to the super view (the "main" view), drag it around as a subview of the main view, and then check where it is released.
So, essentially, if you want to drag a UIImageView:
on first touch, move the image view from its containing view to the "main" view
translate the coordinates, so they are now relative to the "main" view
track the touch / drag in main view coordinates, re-positioning the image view as you go
on release, see if the end touch is inside the "target" container view
if so, add the image view as a subview of the target view (translating the position coordinates)
if it's not inside the target frame, either add it back to its original container, or drop it into the main view, or vaporize it, or whatever you desire.
I am trying to build a UIScrollView that you can navigate around by swiping but not by dragging. I want to instead for the drag gesture to be used for selecting areas of the scrollview.
So far I have implemented the drag gesture for selecting the area but not done the harder bit of changing the scrollview so the dragging gesture is not enabled.
I have a view which is a subview of the scrollview that contains the area selection code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
NSNotificationCenter.defaultCenter().postNotificationName(TOUCH_BEGAN, object: touches)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
NSNotificationCenter.defaultCenter().postNotificationName(TOUCH_MOVED, object: touches)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
NSNotificationCenter.defaultCenter().postNotificationName(TOUCH_ENDED, object: touches)
}
The notifications just get detected in the viewcontroller and select the area I want (the app is basically a wordsearch so the touch just selects the letters the user wants to select).
I'm unsure whether what I'm trying to do is actually possible and have looked around so much but haven't found any answers so far. I think the way to do it is by stopping the userinteraction of the scrollview and then manually program it by just detecting swipe gestures. But again I think that is very difficult and having tried it outside my limited swift knowledge.
I am trying to create a very simplified layout, something like Stripe's iOS app has which you can see here: https://stripe.com/img/dashboard_iphone/screencast.mp4
At around 0:06s the table view is swiped up and it moves up to the top of the window.
Are there any simple instructions in Swift that show this design pattern I see it everywhere but no idea how to create it
Add a UIView
Subclass that UIView with a custom class
In you custom UIView, you'll need a couple of variables and a few overrides. Make sure that user interaction is enabled on the UIView in your storyboard, then in your custom class add:
var startPosition: CGPoint?
var originalHeight: CGFloat?
After that, add the following:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
startPosition = touch?.locationInView(self)
originalHeight = self.frame.height
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let endPosition = touch?.locationInView(self)
let difference = endPosition!.y - startPosition!.y
let newFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y + difference, self.frame.width, self.frame.height - difference)
self.frame = newFrame
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
In UIScrollView.h:
// When the user taps the status bar, the scroll view beneath the touch which is closest to the status bar will be scrolled to top, but only if its `scrollsToTop` property is YES, its delegate does not return NO from `shouldScrollViewScrollToTop`, and it is not already at the top.
// On iPhone, we execute this gesture only if there's one on-screen scroll view with `scrollsToTop` == YES. If more than one is found, none will be scrolled.
var scrollsToTop: Bool // default is YES.
Initialize your scroll view delegate (unless you already have, probably in your viewDidLoad), and then set this to true.
Like this:
myScrollView.scrollsToTop = true
If you want to be sure your scroll view delegate allows this, check this method from UIScrollViewDelegate's protocol:
optional func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool // return a yes if you want to scroll to the top. if not defined, assumes YES
Please comment any concerns.
I am currently using SpriteKit + UIScrollView + SKCameraNode to create a scrolling game. I am currently trying to reverse the direction of the UIScrollView in the vertical direction, as the scroll view seems to do it backwards from what is intuitive.
My solution to this was to put the contentOffset from the start for the scroll view to the max height, and set the camera's position to the y position in the other direction.
However, the scrolling stops halfway down at (0, 320) when it should go to (0, 0).
Here is the code for the scroll view:
let contentSize = CGSize(width: 1575, height: 760) //adjust depending on level
self.height = contentSize.height
scrollView.contentSize = contentSize
scrollView.contentOffset = CGPointMake(0, contentSize.height / 2)
Here is the code when the scroll view scrolls:
let contentOffset = CGPointMake(scrollView.contentOffset.x, self.height - scrollView.contentOffset.y)
self.gScene.setContentOffset(contentOffset)
And here is the code for setting the content offset:
self.camera!.position = CGPointMake(ogPos.x + contentOffset.x, ogPos.y + contentOffset.y)
So the camera works and scrolls in the correct direction now; however, the scrolling's content offset gets stuck at (0, 320) for no reason, and it refuses to scroll further down. If you have any ideas I'd love to hear them! Thanks!
EDIT: I also tried making the UIScrollView's contentSize with a negative height, but then it cannot scroll at all.
I just discovered that it stops short by the size of the view.
It is not a good idea to mix UIKit and SpriteKit a lot, because they work differently (different positions, different units, etc).
If you want to drag the MainScene around, you can do this:
In our MainScene we create:
let background:SKSpriteNode = SKSpriteNode(imageNamed: "background")
var lastTouch : CGPoint?
We will add background to our scene, and every SKNode as a child to this background (platforms, the player, or whatever you want to show).
Then, in touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent), we want to get the user touch and move this background depending in the user touch movement. A basic implementation would be:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
var touch : UITouch = touches.first as! UITouch
lastTouch = touch .locationInNode(self)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch moves */
var touch : UITouch = touches.first as! UITouch
var touchLocation : CGPoint = touch .locationInNode(self)
//We uptade the background position
background.position = CGPointMake(background.position.x + (touchLocation.x - lastTouch!.x), background.position.y + (touchLocation.y - lastTouch!.y))
//We update the lastTouch
lastTouch = touchLocation;
}
If you wanted to move automatically the MainScene, you should move background in the update: method.
I discovered that it was stopping short by the size of the view's frame. I fixed the issue by simply adding an inset the size of the view's frame so it goes that much farther.