How can I move the 'player' position to be where the user touches the screen? I want them to be able to move it constantly by tapping different places. This is what I have so far but it's not working:
class GameScene: SKScene {
let player = SKSpriteNode(imageNamed: "Spaceship")
override func didMoveToView(view: SKView) {
// 2
backgroundColor = SKColor.whiteColor()
// 3
player.position = CGPoint(x: size.width/2, y: size.height/2)
// 4
addChild(player)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let player = SKSpriteNode(imageNamed:"Spaceship")
player.position = location
}
}
In the line let player = SKSpriteNode(imageNamed:"Spaceship") in touchesBegan you are creating a new instance of the spaceship node.
This node is not in the scene and setting the position of the new node does not change the position of the existing player node.
Just try resetting the position of you player node instead.
Try
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch?
if let location = touch?.locationInNode(self)
{
player.position = location
}
}
Related
I'm using swiftui and I would like to display the compass button.
The map portion of my code is derived from this tutorial:
https://www.morningswiftui.com/blog/build-mapview-app-with-swiftui
I've looked at sample code to display the compass on the map but I have not been able to find an example that I can get working with my swiftui code.
Actually compass is showing, but only when you try to turn your map. If you want to see compass button for all the time, you can add your own button in makeUIView func:
struct RootMapView: View {
var body: some View {
MapView()
}
}
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.showsCompass = false // hides current compass, which shows only on map turning
let compassBtn = MKCompassButton(mapView: map)
compassBtn.frame.origin = CGPoint(x: 20, y: 20) // you may use GeometryReader to replace it's position
compassBtn.compassVisibility = .visible // compass will always be on map
map.addSubview(compassBtn)
return map
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
I have gameCenterButton in VC1. Its purpose is to take the user to Game Center's Leaderboards where they can see High Scores. If the user decides to authenticate with Game Center, then I want to change gameCenterButton's state (un-grey and enable). In my GameKitHelper class I have these:
func authenticateLocalPlayer() {
GKLocalPlayer.local.authenticateHandler =
{ (viewController, error) in
self.gameCenterEnabled = false
if viewController != nil {
self.authenticationViewController = viewController
NotificationCenter.default.post(name: NSNotification.Name(
GameKitHelper.PresentAuthenticationViewController),
object: self)
} else if GKLocalPlayer.local.isAuthenticated {
self.gameCenterEnabled = true
}
}
}
extension GameKitHelper: GKGameCenterControllerDelegate {
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
In VC1 I have this:
#IBOutlet weak var gameCenterButton: UIButton!
#IBAction func gameCenter(_ sender: UIButton) {
GameKitHelper.sharedInstance.showGKGameCenterViewController(viewController: self)
}
I'm thinking that inside of extension GameKitHelper I can do ...
if gameCenterEnabled == true {
gameCenterButton.isEnabled = true // How do I allow for this?
gameCenterButton.alpha = 1 // How do I allow for this?
How do I allow gameCenterButton state to change outside of it's class. Is there something I need to do in AppDelegate?
Put var gameCenterEnabled = false outside (above) of your GameKitHelper class, thus making it "global". You will likely be prompted to remove the self. in self.gameCenterEnabled = false and in self.gameCenterEnabled = true. Do so.
Now, you can reference gameCenterEnabled in VC1's class and change gameCenterButton's state like this:
// code to determine gameCenterButton's state based on gameCenterEnabled's status
if gameCenterEnabled == false {
self.gameCenterButton.isEnabled = false
self.gameCenterButton.alpha = 0.37
} else {
self.gameCenterButton.isEnabled = true
self.gameCenterButton.alpha = 1
}
I'm trying to create an effect over my custom buttons (SKSpriteNodes) and other UI objects that are also SKSpriteNode subclasses so that when the mouse hovers over them, they expand slightly to indicate that the user is hovering over them. Once the mouse leaves the vicinity of the sprite, the sprite should go back to normal size.
I initially tried this with the mouseMoved method, but undoing the scaling effect is proving to be an issue. Is there a better way to handle this maybe in the subclasses themselves? Ideas?
One way to do this is to simply keep track of the currently hovered node (if any). Something like this.
I've added a method to recursively search a nodes parents to try and find a suitable hoverable parent. SKSpriteNode in my example, but Producer in your case unless I'm mistaken.
class GameScene: SKScene {
var hoverNode: SKSpriteNode?
//Setup
override func mouseMoved(theEvent: NSEvent) {
let location = theEvent.locationInNode(self)
if let node = findHoverable(self.nodeAtPoint(location)) {
if (node == hoverNode) { return }
node.removeAllActions()
node.runAction(SKAction.scaleTo(1.2, duration: 0.5))
hoverNode = node
} else {
if let hoverNode = hoverNode {
hoverNode.removeAllActions()
hoverNode.runAction(SKAction.scaleTo(1.0, duration: 0.5))
self.hoverNode = nil
}
}
}
private func findHoverable(node: SKNode) -> SKSpriteNode? {
if let node = node as? SKSpriteNode {
return node
}
if let parent = node.parent {
if let parent = parent as? SKSpriteNode {
return parent
} else {
return findHoverable(parent)
}
} else {
return nil
}
}
}
I have a program that has a NSTableView populated with files to be uploaded. Once the file is sent, the Text Cell with the file's name gets a hyperlink placed into it (the array data is given an NSMutableString with an NSLinkAttributeName attribute). How do I allow users to click this link to open the webpage in their default browser?
After much searching and trying multiple methods, this is what I came up with as a solution.
Creating a custom class that extends NSTableViewCell:
class TableViewCellCursor: NSTableCellView {
internal var active = false
//MARK: - View Life Cycle
override func awakeFromNib() {
superview?.awakeFromNib()
self.createTrackingArea()
}
//MARK: - IBActions
override func mouseEntered(theEvent: NSEvent) {
if (NSCursor.currentCursor() == NSCursor.arrowCursor() && active) {
NSCursor.pointingHandCursor().set()
}
}
override func mouseExited(theEvent: NSEvent) {
if (NSCursor.currentCursor() == NSCursor.pointingHandCursor() && active) {
NSCursor.arrowCursor().set()
}
}
//Informs the receiver that the mouse cursor has moved into a cursor rectangle.
override func cursorUpdate(event: NSEvent) {
if (active) {
NSCursor.pointingHandCursor().set()
}
}
//MARK: - Util
func createTrackingArea() {
var focusTrackingAreaOptions:NSTrackingAreaOptions = NSTrackingAreaOptions.ActiveInActiveApp
focusTrackingAreaOptions |= NSTrackingAreaOptions.MouseEnteredAndExited
focusTrackingAreaOptions |= NSTrackingAreaOptions.AssumeInside
focusTrackingAreaOptions |= NSTrackingAreaOptions.InVisibleRect
var focusTrackingArea:NSTrackingArea = NSTrackingArea(rect: NSZeroRect,
options: focusTrackingAreaOptions,
owner: self, userInfo: nil)
self.addTrackingArea(focusTrackingArea)
}
}
Checking first responder status when the NSTableView selection changes. This is necessary because the table's selection can be changed, even when it is not the firstResponder:
func tableViewSelectionDidChange(aNotification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
func changeSelectedRowTextColorTo(selectedColor: NSColor, unselectedColor: NSColor) {
let selectedRows = filesToTransferTable.selectedRowIndexes
for (index, tableEntry) in enumerate (tableData) {
if tableData[index]["FileName"] is NSMutableAttributedString {
var name = tableData[index]["FileName"] as! NSMutableAttributedString
var range = NSMakeRange(0, NSString(string:name.string).length)
name.beginEditing()
name.removeAttribute(NSForegroundColorAttributeName, range: range)
if (selectedRows.containsIndex(index)) {
name.addAttribute(NSForegroundColorAttributeName, value:selectedColor, range:range)
} else {
name.addAttribute(NSForegroundColorAttributeName, value:unselectedColor, range:range)
}
name.endEditing()
tableData[index]["FileName"] = name
}
filesToTransferTable.reloadDataForRowIndexes(NSIndexSet(index: index), columnIndexes: NSIndexSet(index:0))
}
}
Adding KVO for checking when FirstResponder changes:
//This is somewhere in your code where you initialize things
//KVO for first responder behavior regarding tableView and updating attributedStrings' colors
self.addObserver(self, forKeyPath: "firstResponder", options: NSKeyValueObservingOptions.Old | NSKeyValueObservingOptions.New, context: nil)
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if (change[NSKeyValueChangeNewKey] is NSTableView) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else if (change[NSKeyValueChangeOldKey] is NSTableView) {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
Finally, checking if the main window (the app itself) is in focus (if this is not done, then the colors won't change appropriately when the window loses focus):
//Put these in the same place as the KVO code
NSNotificationCenter.defaultCenter().addObserver(self, selector: "windowDidBecomeKey:",
name: NSWindowDidBecomeKeyNotification , object: self)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "windowDidResignKey:",
name: NSWindowDidResignKeyNotification , object: self)
func windowDidBecomeKey(notification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
func windowDidResignKey(notification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
Text fields automatically support clicking on embedded links, but only if they are at least selectable (if not editable). So, set your text field to be selectable.
I know that theoretically it's possible to create multiple instances of the same class with a property that would have a different value for each instance.
The thing is, I can't make it happen.
Each time I'm creating a new instance, it gets the property's value of the other instances, and when I'm changing one value for an instance, it changes the other's too.
So my guess is that I'm doing something wrong (obviously), like accessing the class property value instead of the instance property value... Here's the code.
class CustomUIImageView: UIImageView {
var someParameter: Bool = false // This is the property I want to be different in each version of the instance.
}
class ClassSiege: UIViewController, UIGestureRecognizerDelegate {
var myView: CustomUIImageView! //the instance declaration.
// I use this gesture recognizer to find out the value of the instance I'm tapping on.
func handleTap (sender: UITapGestureRecognizer) {
print("value of someParameter \(self.myView.someParameter)")
}
func handlePan(recognizer: UIPanGestureRecognizer) {
let iv: UIView! = recognizer.view
let translation = recognizer.translationInView(self.view)
iv.center.x += translation.x
iv.center.y += translation.y
recognizer.setTranslation(CGPointZero, inView: self.view)
var centerBoardX = BlackBoard.center.x // 'Blackboard' is a fixed image on the screen.
var centerBoardY = BlackBoard.center.y
var centerRondX = iv.center.x
var centerRondY = iv.center.y
if centerRondY - centerBoardY < 100 {
self.myView.someParameter = true // If the distance between myView and the blackboard is under 100 I want the instance's property to become true.
} else {
self.myView.someParameter = false // On the other hand, if the distance is greater than 100, I want it to be false.
}
}
// When the user pushes a button, it triggers this method that creates a new instance of myView and add it to the screen.
#IBAction func showContent(sender: AnyObject) {
// some code...
// Here I'm creating the instance of the view and I give it the gesture recognizer parameters. I don't think that relevant to the issue, so I'm not adding the code.
}
}
So clearly that's not the good way to do it, but what's wrong, and how can it be solved?
Basing my answer on your related question.
If what you want to achieve is initializing a property with a value that you provide, just add a new parameter to the initializer. If for instance you are using the initializer with a CGRect passed in, then you can implement an initializer like this:
class CustomUIImageView : UIImageView {
let someParameter : Bool
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(frame: CGRect, someParameter: Bool) {
self.someParameter = someParameter
super.init(frame: frame)
}
}
I hope that this is what you are looking for - let me know otherwise.
I've found the solution, and if you've been facing the same issu, here's how to deal with it.
The secret is to downcast the recognizer.view to take the parameter of the subclass CustomUIImageView.
here's how :
func handleTap (sender: UITapGestureRecognizer) {
println("value of someParameter \(self.myView.someParameter)") //I use this gesture recognizer to find out the value of the instance I'm tapping on.
}
func handlePan(recognizer:UIPanGestureRecognizer) {
let iv : UIView! = recognizer.view
let translation = recognizer.translationInView(self.view)
iv.center.x += translation.x
iv.center.y += translation.y
recognizer.setTranslation(CGPointZero, inView: self.view)
var centerBoardX = BlackBoard.center.x //blackboard is a fixed image on the screen.
var centerBoardY = BlackBoard.center.y
var centerRondX = iv.center.x
var centerRondY = iv.center.y
var myParameter = recognizer.view as CustomUIImageView //<- this is the key point. Downcasting let you access the custom subclass parameters of the object that is currently moved
if centerRondY - centerBoardY < 100 {
myParameter.someParameter = true //so now I'm really changing the parameter's value inside the object rather than changing a global var like I did before.
} else {
myParameter.someParameter = false
}
}
//when user pushes a button, it triggers this func that creates a new instance of myView and add it to the screen.
#IBAction func showContent(sender: AnyObject) {
some code...
//here I'm creating the instance of the view and I give it the gesture recognizer parameters. I don't think that relevant to the issue, so I'm not adding the code.
}