how to detect which scene? (swift/spritekit/xcode) - xcode

I am using this code for my background in the gameviewcontroller:
let yourImage = UIImage(named: welkeLevel)
let imageview = UIImageView(image: yourImage)
self.view?.addSubview(imageview)
But I have a begin (menu) scene where I do not want this background and is still showing up.
How do I detect which scene I am (Gamescene / Menuscene etc.)?
Or is there another way?

SKScene is a subclass of SKNode. You can use the name property to do this. Just set the name of Gamescene or Menuscene to 'game' or 'menu' like this:
scene.name = "game"
And check the property like so:
if self.name == "game"{
//Do something.
println("game")
} else if self.name == "menu
//Do something else.
println("menu")
}

I have worked out the same issue with a replay button. I have to identify which scene the user is in and reload the same when user clicks replay button.
1) Add a extension function to detect current game level
extension SKScene {
static func sceneWithClassNamed(className: String, fileNamed fileName: String) -> SKScene? {
if let SceneClass = NSClassFromString("JumperCrab.\(className)") as? SKScene.Type,
let scene = SceneClass(fileNamed: fileName) {
return scene
}
return nil
}
func replayCurrentScene(currentLevelName: String,currentGameSceneIndex:Int)
{
GameViewController().getReplayCurrentSceneDetailsToVC(currentLevelName, currentGameSceneIndex: currentGameSceneIndex)
}
}
2) Call the above extension method in every game scene.
override func didMoveToView(view: SKView) {
self.replayCurrentScene( "Round 1", currentGameSceneIndex: 0)
}
3) Set up a similar method at the View Controller.
func getReplayCurrentSceneDetailsToVC(currentLevelName: String,currentGameSceneIndex:Int)
{
NSUserDefaults.standardUserDefaults().setInteger(currentGameSceneIndex, forKey: "LevelInt")
NSUserDefaults.standardUserDefaults().synchronize()
NSUserDefaults.standardUserDefaults().setObject(currentLevelName, forKey: "LevelName")
NSUserDefaults.standardUserDefaults().synchronize()
}
4) Create variables
//GAME LEVEL
var gameLevelSceneNameArray = NSArray()
var currentGameSceneAtVC:String?
var currentGameSceneIndexAtVC:Int?
5) Make my level list array
override func viewDidLoad() {
super.viewDidLoad()
//Game Scenes
gameLevelSceneNameArray = ["GameScene","SecondRound","ThirdRound", "FourthRound","FifthRound","SixthRound","SeventhRound", "EighthRound","NinethRound"]
}
6) Apply on the Replay Game button .
#IBAction func replayButtonClicked(sender: AnyObject) {
currentGameSceneIndexAtVC = NSUserDefaults.standardUserDefaults().integerForKey("LevelInt")
currentGameSceneAtVC = NSUserDefaults.standardUserDefaults().objectForKey("LevelName") as? String
self.moveToSelectedGameLevel(currentGameSceneAtVC!, gameSceneName: self.gameLevelSceneNameArray[currentGameSceneIndexAtVC!] as! String)
}
7) Set up a game scene navigation too (using switch case )
func moveToSelectedGameLevel(levelNameString:String, gameSceneName:String)
{
let skView = self.view as! SKView
switch(levelNameString)
{
case "Round 1":
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
}
case "Round 2" :
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
}
case "Round 3":
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
default:
print("")
}
}

Related

UIViewControllerTransitioningDelegate presenting doesn't work (though dismissing does)

I am trying to implement a custom appearing/disappearing animation for a modal UIViewController in my app.
I have published the code showing this error here.
Here is the content related:
/// The view controller from which I'm trying to display the modal
class ViewController: UIViewController {
#IBAction func tapped() {
/// The modal showing
ModalTestViewController.show()
}
//...
}
/// The displayed modal
open class ModalTestViewController: TransitioningModalViewController {
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
// 1. when I put transitioningDelegate here, case 1
}
public required init?(coder: NSCoder) {
fatalError()
}
open override func viewDidLoad() {
super.viewDidLoad()
// 2. if I put transitioningDelegate here, case 2
}
#IBAction func tapped() {
// a tap on the overlayView of my modal
dismiss(animated: true)
}
static func show() {
let modal = ModalTestViewController()
modal.modalPresentationStyle = .overCurrentContext
DispatchQueue.main.async {
UIApplication.shared.delegate?.window??.rootViewController?.present(modal, animated: true)
}
}
}
/// The default modal view controller, which all modals in my app should inherit
open class TransitioningModalViewController: UIViewController {
// MARK: View Properties
#IBOutlet weak var overlayView: UIView!
}
extension TransitioningModalViewController: UIViewControllerTransitioningDelegate {
public func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerPresenter()
}
public func animationController(
forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerDismisser()
}
}
private final class TransitioningModalViewControllerPresenter: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
private final class TransitioningModalViewControllerDismisser: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let fromViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.from
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
UIView.animate(
withDuration: duration
) {
fromViewController.overlayView.alpha = 0.0
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
The idea behind this is that the modal appearance should not be the usual bottom-to-top animation, but instead the overlay view should go from hidden to an alpha of 0,65.
Case 1: when I put transitioningDelegate = self in init(), the animation is killed and nothing happens.
Case 2: when I put it into the viewDidLoad(), the appearing animation is the default bottom-to-top one, but the disappearing one is the expecting one (with the overlay view vanishing).
It looks like something is wrong with the initial transitioningDelegate setting but I can't find what.
Thank you for your help!
In your original code, you are setting the delegate here:
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
transitioningDelegate = self
}
However, .present(...) is called before viewDidAppear(...), so the controller is presented with default slide-up animation.
Setting the delegate in init() doesn't work, because we have override the default presentation process... and the presented controller's view is never added to the view hierarchy.
This "quick fix" should do the job...
First, in ModalTestViewController, move setting the delegate to init():
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
}
then, in TransitioningModalViewControllerPresenter, add these lines before the animation:
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
// add these lines \/
// get the "from" view controller
let fromVC = transitionContext.viewController(forKey: .from)!
// get the "to" view controller's view
let toView = transitionContext.view(forKey: .to)!
// set the frame of the "to" view to the initialFrame (the current frame) of the "from" VC
toView.frame = transitionContext.initialFrame(for: fromVC)
// get the transition container view
let container = transitionContext.containerView
// add the "to" view to the view hierarchy
container.addSubview(toView)
// add these lines /\
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
Personally, to make this more flexible, I would get rid of your overlayView and set the alpha on the controller's view itself.

Would someone please share a current SpriteKit texture atlas sample to pull apart

My background is Arduino and I have found Swift a bit opaque. There are a lot of outdated tutorials out there too. I am working on just getting an animated sprite on screen. I have a small animated .png sequence that I will use, saving it into a ball.atlas folder and copying it to Assets.xcassets.
I have found what I believe was posted by Knight0fDragon. Running it shows "worked" in the console, but I don't understand how to assign it to a player so to speak and thus show up on the simulator. If someone would be willing to share the additional code needed to do so, I could pull it apart.
Here is the code:
let textureAtlas = SKTextureAtlas(named: "example")
var textureArray = [SKTexture]()
var frames:[SKTexture] = []
for index in 1 ... 59 {
let textureName = "example_\(index)"
let texture = textureAtlas.textureNamed(textureName)
frames.append(texture)
textureArray = frames
print("worked")
I can't share a texture atlas with you but I help explain a little see below
let textureAtlas = SKTextureAtlas(named: "example")
var textureArray = [SKTexture]()
var frames:[SKTexture] = []
for index in 1 ... 59 {
let textureName = "example_\(index)"
let texture = textureAtlas.textureNamed(textureName)
frames.append(texture)
textureArray = frames
print("worked")
The line that gives "let textureName = "example_(index)" is where KoD gives you the naming convention for all the sprites in the Atlas. The atlas itself for this bit of code is called "example". All the sprites saved in the Atlas are assigned the following name "example_X" (X is an integer). example_1, example_2, example_3 etc... etc until you get to example_59. If you don't want to make 59 textures then you need to edit the for loop giving the new value.
hmmmmm OK have a look here
import SpriteKit
class GameScene: SKScene{
//MARK: PROPERTIES
var lastUpdateTime: TimeInterval = 0
var dt: TimeInterval = 0
var myAnimation = SKAction()
let myAnimaTimeInterval: TimeInterval = 0.1
let mySprite = SKSpriteNode(texture: SKTextureAtlas(named: "example").textureNamed("example_1"))
//MARK: INITIALISATION
override func sceneDidLoad() {
}
override func didMove(to view: SKView) {
addChild(mySprite)
mySprite.position = CGPoint.zero
setAnimation()
runAnimation(node: mySprite)
}
//MARK: GAME LOOP
override func update(_ currentTime: TimeInterval) {
updateGameLoopTimer(currentTime: currentTime)
}
override func didEvaluateActions() {
}
override func didFinishUpdate() {
}
//MARK: TOUCHES
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
//MARK: HELPER FUNCTIONS
private func setAnimation() {
let textureAtlas = SKTextureAtlas(named: "example")
var textureArray: [SKTexture] = []
for index in 2...59 {
let textureName = "example_\(index)"
let texture = textureAtlas.textureNamed(textureName)
textureArray.append(texture)
}
for index in (1...58).reversed() {
let textureName = "example_\(index)"
let texture = textureAtlas.textureNamed(textureName)
textureArray.append(texture)
}
let animation = SKAction.animate(with: textureArray, timePerFrame: myAnimaTimeInterval)
myAnimation = SKAction.repeatForever(animation)
}
private func runAnimation(node: SKSpriteNode) {
node.run(myAnimation, withKey: "myAnimation")
}
private func stopAnimation(node: SKSpriteNode) {
node.removeAction(forKey: "myAnimation")
}
public func updateGameLoopTimer(currentTime: TimeInterval) {
if lastUpdateTime > 0 {
dt = currentTime - lastUpdateTime
} else {
dt = 0
}
lastUpdateTime = currentTime
}
//MARK: CLEAN-UP
deinit {
}
}
I've taken a few liberties with the first sprite for the character which made me change the loop a little.
You can attach the animation to any sprite using the runAnimation method and stop the animation with the stopAnimation method. I've just added the animation to the didMoveToView call.
Try replacing your gameScene file with the above. OK now with the game loop timer ... maybe there are better ways to time the game.... but for you and me it is OK.
Try it again

How to update a Status Item created by AppDelegate from NSViewController

I'm trying to create a Countdown Timer application that runs in the Menu Bar, with no window or dock icon. I've been building this off of mostly tutorials I find online and I know the code is kind of messy (I plan to clean up after it functions properly). The issue I'm running into. In the AppDelegate I create the StatusBar item with no issue, but I can't figure out how to update it from the viewController. It instead is creating a new StatusBar item.
//AppDelegate info
class AppDelegate: NSObject, NSApplicationDelegate
{
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let popover = NSPopover()
func applicationDidFinishLaunching(_ aNotification: Notification)
{
menuBarRefresh(self)
}
func menuBarRefresh(_ sender: Any?)
{
if let button = item.button
{
button.image = NSImage(named: NSImage.Name("2"))
//button.title = initialTime.stringValue
button.action = #selector(togglePopover(_:))
}
popover.contentViewController = TimerViewController.freshController()
}
#objc func togglePopover(_ sender: Any?)
{
if popover.isShown
{
closePopover(sender: sender)
}
else
{
showPopover(sender: sender)
}
}
func showPopover(sender: Any?)
{
if let button = item.button
{
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
}
}
func closePopover(sender: Any?)
{
popover.performClose(sender)
}
//Controller code
import Cocoa
import AVFoundation
//Checking to ensure entered data is numeric
extension String
{
var isNumeric: Bool
{
let range = self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted)
return (range == nil)
}
}
class TimerViewController: NSViewController
{
//Here's the texts fields for the user to enter content.
#IBOutlet var hourInput: NSTextField!
#IBOutlet var minuteInput: NSTextField!
#IBOutlet var secondInput: NSTextField!
//This is the label used to display the counter
#IBOutlet var initialTime: NSTextField!
//Here are the variables we're going to need
var hours = Int() //Place holder for the hours
var minutes = Int() //Place holder for the hours
var seconds = Int() //Place holder for the hours
var timer = Timer() //The timer we'll use later
var audioPlayer = AVAudioPlayer() //The audio player
var timeRemaining = Int() //Place holder for the total 'seconds' to be counted
var firstRun = Bool()
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
override func viewDidLoad()
{
super.viewDidLoad()
getData() //Pull last saved time from Core Data and load it.
hourInput.stringValue = "\(hours)" //Loading the hours into the hours field
minuteInput.stringValue = "\(minutes)" //Loading the minutes into the minutes field
secondInput.stringValue = "\(seconds)" //Loading the seconds into the seconds field
initialTime.stringValue = "00:00:00" //Resetting the 'counter' to 0
firstRun = true
updateStatusBar(self)
//Here we load up the audio file for the 'done' chime. If not available we print the catch
do
{
let audioPath = Bundle.main.path(forResource: "Done", ofType: "m4a")
try audioPlayer = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath!))
}
catch
{
print("No Joy")
}
/* if let button = item.button
{
button.image = NSImage(named: NSImage.Name("2"))
button.title = initialTime.stringValue
button.action = #selector(togglePopover(_:))
}
*/ }
}
// MARK: Storyboard instantiation
extension TimerViewController
{
static func freshController() -> TimerViewController
{
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
let identifier = NSStoryboard.SceneIdentifier("TimerViewController")
guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? TimerViewController
else
{
fatalError("Why can't I find TimerViewController? - Check Main.storyboard")
}
return viewcontroller
}
}
//Button actions follow
extension TimerViewController
{
#IBAction func clearButton(_ sender: Any)
{
clearFields()
timer.invalidate()
audioPlayer.stop()
}
#IBAction func pauseButton(_ sender: Any)
{
timer.invalidate()
}
#IBAction func quitButton(_ sender: Any)
{
exit(0)
}
#IBAction func startButton(_ sender: Any)
{
grabData()
setData()
timeRemaining = (hours*3600)+(minutes*60)+seconds
if timeRemaining <= 0
{
initialTime.stringValue = "Enter Time"
}
else
{
displayTime()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.startCountDown), userInfo: nil, repeats: true)
clearFields()
updateStatusBar(self)
}
}
}
//MARK: Other Functions
extension TimerViewController
{
func displayTime()
{
let secondsDisplay = String(format: "%02d", (timeRemaining%60))
let minutesDisplay = String(format: "%02d", (timeRemaining%3600)/60)
initialTime.stringValue = "\(timeRemaining/3600):\(minutesDisplay):\(secondsDisplay)"
}
func grabData()
{
hours = hourInput.integerValue
minutes = minuteInput.integerValue
seconds = secondInput.integerValue
}
func clearFields()
{
hourInput.stringValue = ""
minuteInput.stringValue = ""
secondInput.stringValue = ""
initialTime.stringValue = "00:00:00"
}
func setData()
{
setHour()
setMinute()
setSecond()
}
func getData()
{
getHour()
getMinute()
getSecond()
}
#objc func showTimer(_ sender: Any?)
{
print("Are we here")
}
#objc func startCountDown()
{
timeRemaining -= 1
displayTime()
updateStatusBar(self)
print(timeRemaining)
if timeRemaining == 0
{
timer.invalidate()
audioPlayer.play()
}
}
/* func setNeedsStatusBarAppearanceUpdate()
{
button.image = NSImage(named: NSImage.Name("2"))
button.action = #selector(showTimer(_:))
}
*/
func updateStatusBar(_ sender: Any?)
{
if let button = item.button
{
button.image = NSImage(named: NSImage.Name("2"))
button.action = #selector(showTimer(_:))
button.title = initialTime.stringValue
}
//let menu = NSMenu()
//menu.addItem(NSMenuItem(title: "Clear Timer", action: #selector(AppDelegate.theDv2), keyEquivalent: "R"))
//menu.addItem(NSMenuItem(title: "Quit Timer", action: #selector(AppDelegate.quit), keyEquivalent: "Q"))
//item.menu = menu
}
}
//There's a bunch of CoreData stuff after here but I left that out. I'm just using CoreData mainly to learn how to and functional reason is to store and load the last used time
As it currently works, I get two StatusBar items instead of creating one with the AppDelegate then updating that one from the ViewController.
Yup... Id-10-t error here. Just had to declare 'item' outside the class and all is well. After getting some good sleep and time away from the computer I realized I was not declaring 'item' globally.

Not able to Rotate my 3D objects

I had created one project using ARKit and SceneKit framework. In which I am working with file extension .dae, the files are locally available in my project as shown in below screenshot.
Here I had applied many gestures on this virtual object such as Tap Gesture(When I tap on camera screen, it places the virtual object there), same way Pinch Gesture and Pan Gesture. All of these gestures are working perfectly fine. Now I wanted to apply rotation gesture, for which I got stuck how to do that, also I am not getting any such available sources to achieve this.
Below is my working code so far,
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
private var movedObject: SCNNode?
private var hud :MBProgressHUD!
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.autoenablesDefaultLighting = true
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene()
sceneView.scene = scene
registerGestureRecognizers()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
private func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(recognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
self.sceneView.addGestureRecognizer(tapGestureRecognizer)
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinched(recognizer:)))
self.sceneView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveObject(recognizer:)))
panGestureRecognizer.maximumNumberOfTouches = 1
panGestureRecognizer.minimumNumberOfTouches = 1
self.sceneView.addGestureRecognizer(panGestureRecognizer)
let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(rotateObject(recognizer:)))
self.sceneView.addGestureRecognizer(rotationGestureRecognizer)
}
#objc func pinched(recognizer :UIPinchGestureRecognizer) {
if recognizer.state == .changed {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = self.sceneView.hitTest(touch, options: nil)
if let hitTest = hitTestResults.first {
let chairNode = hitTest.node
let pinchScaleX = Float(recognizer.scale) * chairNode.scale.x
let pinchScaleY = Float(recognizer.scale) * chairNode.scale.y
let pinchScaleZ = Float(recognizer.scale) * chairNode.scale.z
chairNode.scale = SCNVector3(pinchScaleX,pinchScaleY,pinchScaleZ)
recognizer.scale = 1
}
}
}
#objc func moveObject(recognizer: UIPanGestureRecognizer) {
print("Move object")
if recognizer.state == .began {
print("Pan state began")
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let result = sceneView.hitTest(tapPoint ?? CGPoint.zero, options: nil)
if result.count == 0 {
return
}
let hitResult: SCNHitTestResult? = result.first
if (hitResult?.node.name == "free_car_1") {
movedObject = hitResult?.node
} else if (hitResult?.node.parent?.name == "free_car_1") {
movedObject = hitResult?.node.parent
}
if (movedObject != nil) {
print("Holding an Object")
}
}
if recognizer.state == .changed {
print("Pan State Changed")
if (movedObject != nil) {
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(tapPoint ?? CGPoint.zero, types: .featurePoint)
let result: ARHitTestResult? = hitResults.last
let matrix: SCNMatrix4 = SCNMatrix4((result?.worldTransform)!)
//SCNMatrix4FromMat4((result?.worldTransform)!)
let vector: SCNVector3 = SCNVector3Make(matrix.m41, matrix.m42, matrix.m43)
movedObject?.position = vector
print("Moving object position")
}
}
if recognizer.state == .ended {
print("Done moving object homeie")
movedObject = nil
}
}
#objc func tapped(recognizer :UITapGestureRecognizer) {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = sceneView.hitTest(touch)
guard let hitTest = hitTestResults.first?.node else {
let hitTestResultsWithExistingPlane = sceneView.hitTest(touch, types: .existingPlane)
let chairScene = SCNScene(named: "ShelbyWD.dae")!
guard let chairNode = chairScene.rootNode.childNode(withName: "ShelbyWD", recursively: true) else {
return
}
if let hitTestAvailable = hitTestResultsWithExistingPlane.first {
chairNode.position = SCNVector3(hitTestAvailable.worldTransform.columns.3.x,hitTestAvailable.worldTransform.columns.3.y,hitTestAvailable.worldTransform.columns.3.z)
self.sceneView.scene.rootNode.addChildNode(chairNode)
return
}
return
}
hitTest.removeFromParentNode()
}
#objc func rotateObject(recognizer :UIRotationGestureRecognizer)
{
}
}
Can anyone help me out to apply rotation gesture on my object?
Thank you!
In order to rotate an SCNNode, the 1st thing you need to do, is create a variable to store the rotationAngle around the YAxis or any other that you wish to perform the rotation on e.g:
var currentAngleY: Float = 0.0
Then have some way to have detected to node you wish to rotate, which in my example I am calling currentNode e.g.
var currentNode: SCNNode!
In my example I will just rotate around the YAxis.
You can use a UIPanGestureRecognizer like so:
/// Rotates An Object On It's YAxis
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func rotateObject(_ gesture: UIPanGestureRecognizer) {
guard let nodeToRotate = currentNode else { return }
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
nodeToRotate.eulerAngles.y = newAngleY
if(gesture.state == .ended) { currentAngleY = newAngleY }
print(nodeToRotate.eulerAngles)
}
Or if you wish to use a UIRotationGesture you can do something like this:
/// Rotates An SCNNode Around It's YAxis
///
/// - Parameter gesture: UIRotationGestureRecognizer
#objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed{
isRotating = true
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
isRotating = false
}
}
Hope it helps...

Best way to load image url swift 2 in UITableView

I want to create A Ui TableView with a list of image link with swift 2:
for example : var images = ["link1","link2",...,linkN"]
I create a custom cell to display the image :
let cell = tableView.dequeueReusableCellWithIdentifier(CurrentFormTableView.CellIdentifiers.ImageCell, forIndexPath: indexPath) as! ImageCell
cell.urlImageView.tag = indexPath.row
cell.displayImage(images[index.row])
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
And here I have my custom cell to load my image :
import UIKit
class ImageCell: UITableViewCell {
#IBOutlet weak var urlImageView: UIImageView!
#IBOutlet weak var loadingStatus: UIActivityIndicatorView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func loadImageFromUrl(url: String, view: UIImageView){
if view.image == nil {
self.startLoading()
// Create Url from string
let url = NSURL(string: url)!
// Download task:
// - sharedSession = global NSURLCache, NSHTTPCookieStorage and NSURLCredentialStorage objects.
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
// if responseData is not null...
if let data = responseData{
// execute in UI thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.stopLoading()
view.image = UIImage(data: data)
})
}
}
// Run task
task.resume()
}
}
func displayImage(imageUrl: String){
imageView?.image = nil
if imageUrl != nil && imageUrl != "" {
print(imageUrl)
loadImageFromUrl(imageUrl,view: urlImageView)
} else {
loadingStatus.hidden = true
}
}
func startLoading(){
loadingStatus.hidden = false
loadingStatus.startAnimating()
}
func stopLoading(){
loadingStatus.hidden = true
loadingStatus.stopAnimating()
}
}
The problem is that, she times the images are loading correctly, and sometimes, one image or more "override the other" so I have multiple identical images instead of see all my different images. How it is possible ? Where is my mistake ?
You're not implementing the reuse of the cells, meaning that the imageView's image of one cell will be the same as another that it was reused from, until the new image has loaded.
To prevent this, implement the -prepareForReuse: method:
override func prepareForReuse() {
super.prepareForReuse()
urlImageView.image = nil
// cancel loading
}
Furthermore, you shouldn't be doing any network-related code in the view layer, it should be done in the view controller. This will allow you to implement caching mechanisms if the image has already been downloaded for a specific cell, as well as alter the state of other views.
i.e. In your view controller:
var cachedImages = [String: UIImage]()
func cellForRow...() {
let imageURL = imageURLs[indexPath.row]
if let image = cachedImages[imageURL] {
cell.urlImageView.image = cachedImages[imageURL]
}
else {
downloadImage(indexPath, { image in
if let image = image {
cachedImages[imageURL] = image
cell.urlImageView.image = image
}
})
}
}
func downloadImage(indexPath: NSIndexPath, callback: () -> (UIImage?)) {
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
// if responseData is not null...
if let data = responseData {
// execute in UI thread
dispatch_async(dispatch_get_main_queue(), {
callback(UIImage(data: data))
})
}
else {
dispatch_async(dispatch_get_main_queue(), {
callback(nil)
})
}
}
// Run task
task.resume()
}

Resources