Trying to get the code so when if whiteDotDist < centerRadius - whiteDotRadius is executed all the code below it is active, and when the code below it is executed it becomes inactive again until the if whiteDotDist < centerRadius - whiteDotRadius is executed again. Sort of like a loop, so you have to keep going back and fourth from center to smallDot. Hard to explain over computer. Update it is giving me error 'Binary operator '<' cannot be applied to operands of type 'CGFloat' and 'Double'
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translation(in: self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPoint.zero, in: self.view)
let centerRadius = 37.5
let whiteDotRadius = 23.5
let whiteDotDist = hypot(center.center.x - whiteDot.center.x, center.center.y - whiteDot.center.y - whiteDot.center.y)
if whiteDotDist < centerRadius - whiteDotRadius {
resetTimer() }
if (whiteDot.frame.contains(smallDot.frame) && smallDot.image != nil) {
addOne += 1
score.text = "\(addOne)"
resetTimer()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(SecondViewController.startTimer), userInfo: nil, repeats: true)
smallDot.center = spawnRandomPosition()
}
}
}
Check to make sure:
All of the views have the same parent (so that they are all in the same coordinate system)
The Frames are tight around the circles. Change the background color of center, whiteDot, and smallDot to Red and post a picture
Even if you do that, your code checks if the bounding rects are inside each other, so it may look like the smallDot is outside the whiteDot (if it were in the corner), but the bounding frame is enclosed by whiteDot's frame.
If you want to check that the circles (not bounding boxes) are inside each other, get the distance between centers and make sure that that is within the (outer radius - smaller dot radius).
pseudo-code
let centerRadius = 100 // set this to radius of center circle
let whiteDotRadius = 10 // set this to whiteDot radius
let whiteDotDist = hypotf(center.center.x - whiteDot.center.x, center.center.y - whiteDot.center.y)
if whiteDotDist < centerRadius - whiteDotRadius {
// whiteDot is inside center
}
Related
I am trying to implement a transition between two controllers with Hero inside a navigation controller.
I have this method to handle a pan gesture:
#objc func pannedCell(gesture: UIPanGestureRecognizer) {
guard let animatedView = gesture.view as? HotelListingCollectionViewCell else {
return
}
let translation = gesture.translation(in: nil)
let screenHeight = self.rootView.bounds.height
let progress = ((-1 * translation.y) / screenHeight) * 2
print(progress)
switch gesture.state {
case .began:
self.initialViewPostion = animatedView.center
Hero.shared.defaultAnimation = .fade
let vc = ListingSearchViewController()
vc.viewModel.hotels.value = self.viewModel.hotels.value
self.navigationController?.pushViewController(vc, animated: true)
case .changed:
Hero.shared.update(progress)
let pos = CGPoint(
x: animatedView.center.x + translation.x,
y: self.rootView.hotelResultsCollectionView.center.y + translation.y
)
Hero.shared.apply(modifiers: [
.position(pos),
.useGlobalCoordinateSpace,
.useOptimizedSnapshot
], to: animatedView)
default:
if progress > 0.8 {
Hero.shared.finish()
} else {
Hero.shared.cancel()
}
}
}
The problem here is the pushViewController method takes ~ 1 second to execute so when I start dragging my view, it moves under my finger approx. 1 second after I started moving the finger on the screen.
I am doing it wrong ?
Thanks
Ok, I think I found something !
The issue seems to be related to operation I did in .began case of the gesture handler.
I moved the instantiation of the new view controller out of this call back to avoid overloading the UI thread during the animation and everything seems to work as expected.
Is there anyway to center the pivot of a model? Currently (by default) it is set to the bottom left. I want to set to the center. How can I do that?
Here is the image:
UPDATE: I added another node and added the chair as a child.
So, now it works better but it does not rotate all the way and it resets the position if I continue to rotate. Here is the code for the panned operation:
#objc func panned(recognizer :UIPanGestureRecognizer) {
var newAngleY :Float = 0.0
if recognizer.state == .changed {
let sceneView = recognizer.view as! ARSCNView
let touchPoint = recognizer.location(in: sceneView)
let translation = recognizer.translation(in: sceneView)
print(translation.x)
let scnHitTestResults = self.sceneView.hitTest(touchPoint, options: nil)
if let hitTestResult = scnHitTestResults.first {
if let parentNode = hitTestResult.node.parent {
newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180
newAngleY += currentAngleY
parentNode.eulerAngles.y = newAngleY
}
}
}
else if recognizer.state == .ended {
currentAngleY = newAngleY
}
}
You can change a pivot of the Chair using the pivotproperty of the SCNNode (Apple Documentation)
You can change this using an SCNMatrix4MakeTranslation e.g:
nodeToAdd.pivot = SCNMatrix4MakeTranslation(0,0,0)
This may solve the problem:
//1. Get The Bounding Box Of The Node
let minimum = float3(nodeToAdd.boundingBox.min)
let maximum = float3(nodeToAdd.boundingBox.max)
//2. Set The Translation To Be Half Way Between The Vector
let translation = (maximum + minimum) * 0.5
//3. Set The Pivot
nodeToAdd.pivot = SCNMatrix4MakeTranslation(translation.x, translation.y, translation.z)
You could also change it within within a program like Blender or Maya.
Alternatively, and probably much easier, is to edit the model in the SceneKit Editor itself.
If you cant fix it's pivot, then what you can do is to create an Empty Node, and then add the Chair as a child of this, ensuring that it is centered within that.
This way you should be able to transform it's rotation for example more accurately.
I am trying to set the limit the bounds of my image view, Or something to prevent the image/ view from zooming out or to the left as it looks pretty bad. Here is the problem Looks Good, Only should be able to zoom in... Zoomed out, Looks Bad
#IBAction func scaleView(_ sender: UIPinchGestureRecognizer) {
//print(self.my_new_fullimage)
self.view.transform = self.view.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
#IBAction func panView(_ gestureRecognizer: UIPanGestureRecognizer) {
// Move the anchor point of the view's layer to the touch point
// so that moving the view becomes simpler.
let piece = gestureRecognizer.view
//self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer)
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
// Get the distance moved since the last call to this method.
let translation = gestureRecognizer.translation(in: piece?.superview)
// Set the translation point to zero so that the translation distance
// is only the change since the last call to this method.
piece?.center = CGPoint(x: ((piece?.center.x)! + translation.x),
y: ((piece?.center.y)! + translation.y))
gestureRecognizer.setTranslation(CGPoint.zero, in: piece?.superview)
}
}
Anything can help.
-Thanks!
Wondering why when I drag the WhiteDot over the SmallDot it adds more than 1 to my UITextField? It varies from each time I run the iPhone simulator. Sometimes it adds 11, 2, 3, 5...etc. No idea why?
Another question, when the SmallDot disappears I also want it to spawn in a specified view. The Score is in top right corner.
class SecondViewController: UIViewController {
private var AddOne = 0
#IBOutlet weak var Score: UITextField!
}
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translation(in: self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPoint.zero, in: self.view)
if (WhiteDot.frame.contains(SmallDot.frame)) {
SmallDot.image = nil;
AddOne += 1
Score.text = "\(AddOne)"
}
}
}
You haven't provided much information.
A pan gesture recognizer reports a continuous stream of coordinates as the user pans with their finger. You're probably getting multiple "hits" when you drag one dot over another. You need to have some sort of test to see if the smaller dot has been eaten yet. Since you're already setting the image on the small dot to nil, you could check that:
if (WhiteDot.frame.contains(SmallDot.frame) && SmallDot.image != nil) {
SmallDot.image = nil;
AddOne += 1
Score.text = "\(AddOne)"
}
Note that variable names like whiteDot, smallDot, addOne, and score should start with a lower case letter. Class names and types should start with an upper case letter. This is a strong convention in Swift and you should follow it. The language doesn't force you to follow it, but I wouldn't hire a programmer who didn't.
I am rather new to iOS and swift 3 programming. At the moment I am working on a little learning project. I have the following problem.
On my screen I have 4 UIViews and 4 UIImageViews. The app allows to “drag and drop” an image onto one of the UIViews. This works pretty well so far. I use Pan Gesture Recognisers for the dragging of UIImages. But for some reason one of the UIViews seems to be in front of the image, meaning that I can drop the image to this area, but I cannot see it - it is layered under the UIView. The other three behave properly, the UIImageView is layered on top of the UIView. In the viewDidLoad() function of the view controller I set the background color of the UIViews to grey. If I don’t do that, the image will be displayed even on the fourth UIView.
I have checked for differences between the four UIView objects but cannot find any. I also tried to recreate the UIView by copying it from one of the the other three.
So my question is: Is there any property which defines that the UIView is layered on top or can be sent to back so that other objects are layered on top of it? Or is there anything else I am missing? Any hints would be very appreciated.
I was not quite sure which parts of the code are relevant, so I posted some of it (but still guess that this is rather a property of the UIView…)
This is where I set the background of the UIViews
override func viewDidLoad() {
super.viewDidLoad()
lbViewTitle.text = "\(viewTitle) \(level) - \(levelPage)"
if (level==0) {
print("invalid level => EXIT")
} else {
loadData(level: level)
originalPositionLetter1 = letter1.center
originalPositionLetter2 = letter2.center
originalPositionLetter3 = letter3.center
originalPositionLetter4 = letter4.center
dropArea1.layer.backgroundColor = UIColor.lightGray.cgColor
dropArea2.layer.backgroundColor = UIColor.lightGray.cgColor
dropArea3.layer.backgroundColor = UIColor.lightGray.cgColor
dropArea4.layer.backgroundColor = UIColor.lightGray.cgColor
}
}
This is the code that moves the image:
#IBAction func hadlePan1(_ sender: UIPanGestureRecognizer) {
moveLetter(sender: sender, dropArea: dropArea4, movedImage: letter1, originalPosition: originalPositionLetter1)
}
func moveLetter(sender: UIPanGestureRecognizer, dropArea : UIView, movedImage : UIImageView, originalPosition : CGPoint) {
let translation = sender.translation(in: self.view)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
if sender.state == UIGestureRecognizerState.ended {
let here = sender.location(in: self.view)
if (dropArea.frame.contains(here)) {
movedImage.center = dropArea.center
} else {
movedImage.center = originalPosition
}
}
}
sender.setTranslation(CGPoint.zero, in: self.view)
}
There are two methods that may be useful to you: UIView.sendSubview(toBack:) and UIView.bringSubview(toFront:).
What I think you should do is to call bringSubview(toFront:) when you start dragging the image:
func moveLetter(sender: UIPanGestureRecognizer, dropArea : UIView, movedImage : UIImageView, originalPosition : CGPoint) {
self.view.bringSubview(toFront: movedImage) // <--- add this line!
let translation = sender.translation(in: self.view)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
if sender.state == UIGestureRecognizerState.ended {
let here = sender.location(in: self.view)
if (dropArea.frame.contains(here)) {
movedImage.center = dropArea.center
} else {
movedImage.center = originalPosition
}
}
}
sender.setTranslation(CGPoint.zero, in: self.view)
}