Changing font / label size with animation in swift - animation

Im after come code to increase the size of my label when the game over code is run.... here is what i have at the moment
if gameOver == 0 {
movingObjects.speed = 0
gameOver = 1
movingObjects.removeAllChildren()// Remove all enemies
gameOverLabel.fontSize = 43
gameOverLabel.text = "Tap to retry!"
gameOverLabel.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*1.5)
labelHolder.addChild(gameOverLabel)
gameOverLabel.zPosition = 9
var pauseScore = SKAction.waitForDuration(0.4)
var moveScore = SKAction.moveToY(CGRectGetMidY(self.frame)*1.2, duration: 0.8)
var resizeScore = SKAction.runBlock({
self.scoreLabel.fontSize = 150
})
var scoreEndSequence = SKAction.sequence([pauseScore, moveScore, resizeScore])
scoreLabel.runAction(scoreEndSequence)
if (UInt(score) > UInt(hScore as NSNumber)) {
NSUserDefaults.standardUserDefaults().setObject(score, forKey: "highscore")
println("Highscore Saved")
} else {
println("Highscore Not Saved")
}
I would like the scoreLabel to also get bigger... now what i have now does do it but it just jumps up to 150 i would like it to animate up

It seems the property fontSize is not animatable.
To fix it, change your holder into a view object

You can animate the node size like this:
var grow = SKAction.scaleTo(10, duration: 0.5)
But if you do this with a SKLabelNode the text will look bad if it gets too big. The text with not rendered again when the node size changes.

Related

Cropping NSImage with onscreen coordinates incorrect on different screen sizes

I'm trying to replicate macOS's screenshot functionality, dragging a selection onscreen to provide coordinates for cropping an image. I have it working fine on my desktop Mac (2560x1600), but testing on my laptop (2016 rMBP 15", 2880x1800), the cropped image is completely wrong. I don't understand why I'd get the right results on my desktop, but not on my laptop. I think it has something to do with the Quarts coordinates being different from Cocoa coordinates, seeing as how on the laptop, the resulting image seems like the coordinates are flipped on the Y-axis.
Here is the code I am using to generate the cropping CGRect:
# Segment used to draw the CAShapeLayer:
private func handleDragging(_ event: NSEvent) {
let mouseLoc = event.locationInWindow
if let point = self.startPoint,
let layer = self.shapeLayer {
let path = CGMutablePath()
path.move(to: point)
path.addLine(to: NSPoint(x: self.startPoint.x, y: mouseLoc.y))
path.addLine(to: mouseLoc)
path.addLine(to: NSPoint(x: mouseLoc.x, y: self.startPoint.y))
path.closeSubpath()
layer.path = path
self.selectionRect = path.boundingBox
}
}
private func startDragging(_ event: NSEvent) {
if let window = self.window,
let contentView = window.contentView,
let layer = contentView.layer,
!self.isDragging {
self.isDragging = true
self.startPoint = window.mouseLocationOutsideOfEventStream
shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 1.0
shapeLayer.fillColor = NSColor.white.withAlphaComponent(0.5).cgColor
shapeLayer.strokeColor = NSColor.systemGray.cgColor
layer.addSublayer(shapeLayer)
}
}
Then this is the code where I actually generate the screenshot and crop using the CGRect:
public func processResults(_ rect: CGRect) {
if let windowID = self.globalWindow?.windowNumber,
let screen = self.getScreenWithMouse(), rect.width > 5 && rect.height > 5 {
self.delegate?.processingResults()
let cgScreenshot = CGWindowListCreateImage(screen.frame, .optionOnScreenBelowWindow, CGWindowID(windowID), .bestResolution)
var rect2 = rect
rect2.origin.y = NSMaxY(self.getScreenWithMouse()!.frame) - NSMaxY(rect);
if let croppedCGScreenshot = cgScreenshot?.cropping(to: rect2) {
let rep = NSBitmapImageRep(cgImage: croppedCGScreenshot)
let image = NSImage()
image.addRepresentation(rep)
self.showPreviewWindow(image: image)
let requests = [self.getTextRecognitionRequest()]
let imageRequestHandler = VNImageRequestHandler(cgImage: croppedCGScreenshot, orientation: .up, options: [:])
DispatchQueue.global(qos: .userInitiated).async {
do {
try imageRequestHandler.perform(requests)
} catch let error {
print("Error: \(error)")
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.hidePreviewWindow()
}
}
}
self.globalWindow = nil
}
Not 15 minutes after I asked this question, I tried one more thing and it works!
Relevant snippet:
var correctedRect = rect
// Set the Y origin properly (counteracting the flipped Y-axis)
correctedRect.origin.y = screen.frame.height - rect.origin.y - rect.height;
// Checks if we're on another screen
if (screen.frame.origin.y < 0) {
correctedRect.origin.y = correctedRect.origin.y - screen.frame.origin.y
}
// Finally, correct the x origin (if we're on another screen, the origin will be larger than zero)
correctedRect.origin.x = correctedRect.origin.x + screen.frame.origin.x
// Generate the screenshot inside the requested rect
let cgScreenshot = CGWindowListCreateImage(correctedRect, .optionOnScreenBelowWindow, CGWindowID(windowID), .bestResolution)

NSWindow view capture to image

Update: Nov.6
Thanks to pointum I revised my question.
On 10.13, I'm trying to write a view snapshot function as general purpose NSView or window extension. Here's my take as a window delegate:
var snapshot : NSImage? {
get {
guard let window = self.window, let view = self.window!.contentView else { return nil }
var rect = view.bounds
rect = view.convert(rect, to: nil)
rect = window.convertToScreen(rect)
// Adjust for titlebar; kTitleUtility = 16, kTitleNormal = 22
let delta : CGFloat = CGFloat((window.styleMask.contains(.utilityWindow) ? kTitleUtility : kTitleNormal))
rect.origin.y += delta
rect.size.height += delta*2
Swift.print("rect: \(rect)")
let cgImage = CGWindowListCreateImage(rect, .optionIncludingWindow,
CGWindowID(window.windowNumber), .bestResolution)
let image = NSImage(cgImage: cgImage!, size: rect.size)
return image
}
}
to derive a 'flattened' snapshot of the window is what I'm after. Initially I'm using this image in a document icon drag.
It acts bizarrely. It seems to work initially - window in center, but subsequently the resulting image is different - smaller, especially when window is moved up or down in screen.
I think the rect capture is wrong ?
Adding to pointum's answer I came up with this:
var snapshot : NSImage? {
get {
guard let window = self.window, let view = self.window!.contentView else { return nil }
let inf = CGFloat(FP_INFINITE)
let null = CGRect(x: inf, y: inf, width: 0, height: 0)
let cgImage = CGWindowListCreateImage(null, .optionIncludingWindow,
CGWindowID(window.windowNumber), .bestResolution)
let image = NSImage(cgImage: cgImage!, size: view.bounds.size)
return image
}
}
As I only want / need a single window, specifying 'null' does the trick. Well all else fails, the docs, if you know where to look :o.
Use CGWindowListCreateImage:
let rect = /* view bounds converted to screen coordinates */
let image = CGWindowListCreateImage(rect, .optionIncludingWindow,
CGWindowID(window.windowNumber), .bestResolution)
To save the image use something like this:
let dest = CGImageDestinationCreateWithURL(url, "public.jpeg", 1, nil)
CGImageDestinationAddImage(destination, image, nil)
CGImageDestinationFinalize(destination)
Note that screen coordinates are flipped. From the docs:
The coordinates of the rectangle must be specified in screen coordinates, where the screen origin is in the upper-left corner of the main display and y-axis values increase downward

How to add UIImageViews to a UIStackView that is constrained to the centerXAnchor of a view?

I'm trying to add profile icons via UIImageViews to a UIStackView in order to keep the icons centered in a view. How would I go about adding UIImageViews of a fixed frame to a UIStackView and keep the UIStackView centered in the main view according to varying numbers of UIImageViews in the UIStackView?
let memberIcons: UIStackView = {
let iconView = UIStackView()
iconView.translatesAutoresizingMaskIntoConstraints = false
iconView.axis = .horizontal
iconView.spacing = 5
iconView.distribution = .equalSpacing
iconView.alignment = .center
return iconView
}()
for member in story!.members {
let circle = UIImageView()
circle.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
circle.translatesAutoresizingMaskIntoConstraints = false
circle.layer.cornerRadius = CGFloat(circle.frame.width / 2)
circle.image = member.profilePicture
circle.contentMode = .scaleAspectFill
circle.clipsToBounds = true
memberIcons.addArrangedSubview(circle)
}
Because you set memberIcons.distribution = .equalSpace, the stack view will ask its subviews for their intrinsic sizes. When asked, the UIImage (i.e. circle) will calculate its intrinsic size as "image pixel size / scale", which is not what you want -- you want the image to be of fixed size (36 x 36).
Use Auto Layout on circle:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(memberIcons)
memberIcons.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
memberIcons.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
// Limit the stack view's width to no more than 75% of the superview's width
// Adjust as needed
memberIcons.widthAnchor.constraint(lessThanOrEqualTo: view.widthAnchor, multiplier: 0.75).isActive = true
let width: CGFloat = 36.0
for member in story!.members {
// We don't care about the frame here as we're gonna use auto layout
let circle = UIImageView(frame: .zero)
circle.translatesAutoresizingMaskIntoConstraints = false
circle.layer.cornerRadius = width / 2
circle.image = member.profilePicture
circle.contentMode = .scaleAspectFill
circle.clipsToBounds = true
circle.layer.borderWidth = 1
circle.layer.borderColor = UIColor.lightGray.cgColor
memberIcons.addArrangedSubview(circle)
circle.widthAnchor.constraint(equalToConstant: width).isActive = true
circle.heightAnchor.constraint(equalToConstant: width).isActive = true
}
}
Result:
Because we limit the width of the UIStackView, there a maximum number of profile images you can add (7 in this case) before you get a bunch of auto layout error on the console. You can enclose the Stack View inside a Scroll View or use a Collection View for a matrix-like display.

Swift 2 how to expand and collapse a view?

I have scrollView in which if there is no data, I need to hide (collapse the space occupied by it) so that the lower view can come upwards.
I tried this code with no luck. Please help me.
if(dataForScroll==0){
var newFrame:CGRect = self.myScroll.frame;
newFrame.size.height = 0;
self.myScroll.frame = newFrame
}
A simple example with Swift 3 version, i think it might be solves your problem. Let me know if have any queries
#IBOutlet var mainScrollView: UIScrollView!
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
#IBOutlet var view3: UIView!
var data1 = NSArray()
var data2 = NSArray()
var data3 = NSArray()
func setFrame()
{
// data = Something you need to get
if data1.count == 0
{
self.view1.frame.size.height = 0
}
else
{
// return its actual height you need
}
if data2.count == 0
{
self.view2.frame.size.height = 0
}
else
{
// return its actual height you need
}
if data3.count == 0
{
self.view3.frame.size.height = 0
}
else
{
// return its actual height you need
}
var frame = self.view1.frame
frame.origin.y = 5
self.view1.frame = frame
frame = self.view2.frame
frame.origin.y = self.view1.frame.origin.y + self.view1.frame.size.height + 5
self.view2.frame = frame
frame = self.view3.frame
frame.origin.y = self.view2.frame.origin.y + self.view2.frame.size.height + 5
self.view3.frame = frame
var scrollHeight:CGFloat = 0.0
var contentRect:CGRect = CGRect.zero
for views in self.mainScrollView.subviews
{
let subView:UIView = views
contentRect = contentRect.union(subView.frame)
print(subView.frame.size.height)
scrollHeight = scrollHeight + subView.frame.size.height
}
self.mainScrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: scrollHeight + 20) // the "+ 20" reamains the spaces between the views, you can change as per your need
}

Do I have to delete a sprite node once I end up using it?

I'm creating a series of moving pipes in my scene. But it always crashes after ~30 pipes are generated. Is it because of too many nodes in the scene and no memories for new ones? The code is like this:
import SpriteKit
class GameScene: SKScene {
var mainPipe: SKSpriteNode = SKSpriteNode()
var space:Float = 1000
var pipeCount:Int = 0
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.size.width = 640
self.size.height = 1136
}
func randomOffset() -> Float{
var rNum:Float = Float(arc4random()%181) // 0-180
return rNum
}
var durations: CFloat = 5.0
var colorPipes:UIColor = UIColor.grayColor()
func spawnPipeRow(offs:Float){
self.pipeCount = self.pipeCount + 1
println("\(self.pipeCount)")
//offs is the random number
//let offset = offs + (space/2) - 105
let offset = offs + Float(self.size.height/100) - 180
// mainPipe = SKSpriteNode(color:colorPipes, size:CGSize(width: view.bounds.size.width/3, height:700))
mainPipe = SKSpriteNode(color:colorPipes, size:CGSize(width: self.size.width/5, height:self.size.height/1.5))
let pipeBottom = (mainPipe as SKSpriteNode).copy() as SKSpriteNode
let pipeTop = (mainPipe as SKSpriteNode).copy() as SKSpriteNode
let xx = self.size.width * 2.0
self.setPositionRelativeBot(pipeBottom, x:Float(xx), y: offset )
self.setPositionRelativeTop(pipeTop, x:Float(xx), y: offset + space)
pipeBottom.physicsBody = SKPhysicsBody(rectangleOfSize: pipeBottom.size)
pipeTop.physicsBody = SKPhysicsBody(rectangleOfSize: pipeTop.size)
pipeBottom.physicsBody?.dynamic = false
pipeTop.physicsBody?.dynamic = false
//pipeTop.physicsBody?.contactTestBitMask = birdCategory
//pipeBottom.physicsBody?.contactTestBitMask = birdCategory
self.addChild(pipeBottom)
self.addChild(pipeTop)
var actionArray1:NSMutableArray = NSMutableArray()
actionArray1.addObject(SKAction.moveTo(CGPointMake(-1000, pipeBottom.size.height - 200), duration: NSTimeInterval(durations)))
var actionArray2:NSMutableArray = NSMutableArray()
actionArray2.addObject(SKAction.moveTo(CGPointMake(-1000, pipeTop.size.height - 200), duration: NSTimeInterval(durations)))
actionArray1.addObject(SKAction.removeFromParent())
actionArray2.addObject(SKAction.removeFromParent())
pipeBottom.runAction(SKAction.sequence(actionArray1))
pipeTop.runAction(SKAction.sequence(actionArray2))
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
}
}
override func update(currentTime: CFTimeInterval) {
var timeSinceLastUpdate = currentTime - lastUpdateTimerInterval
lastUpdateTimerInterval = currentTime
if(timeSinceLastUpdate > 1){
timeSinceLastUpdate = 1/60
lastUpdateTimerInterval=currentTime
}
updateWithTimeSinceLastUpdate(timeSinceLastUpdate)
/* Called before each frame is rendered */
}
func setPositionRelativeBot(node:SKSpriteNode, x: Float, y: Float){
let xx = (Float(node.size.width)/2) + x
let yy = (Float(self.size.height)/2) - (Float(node.size.height)/2) + y
node.position.x = CGFloat(xx)
node.position.y = CGFloat(yy)
}
func setPositionRelativeTop(node:SKSpriteNode, x:Float, y:Float){
let xx = (Float(node.size.width)/2) + x
let yy = (Float(self.size.height)/2) + (Float(node.size.height)/2) + y
node.position.x = CGFloat(xx)
node.position.y = CGFloat(yy)
}
var lastUpdateTimerInterval:NSTimeInterval = NSTimeInterval()
var lastYieldTimeInterval:NSTimeInterval = NSTimeInterval()
var speedOfBird: CDouble = 1.8
func updateWithTimeSinceLastUpdate(timeSinceLastUpdate:CFTimeInterval){
lastYieldTimeInterval += timeSinceLastUpdate
if(lastYieldTimeInterval > speedOfBird ){
lastYieldTimeInterval=0
self.spawnPipeRow(self.randomOffset())
if speedOfBird > 0.8{
speedOfBird -= 0.1}
}
}
}
You should remove your sprites from the scene when you no longer need them. However your problem is probably not related to the memory occupied by your textures:
SpriteKit Programming Guide
An SKTexture object is created and attached to the sprite. This
texture object automatically loads the texture data whenever the
sprite node is in the scene, is visible, and is necessary for
rendering the scene. Later, if the sprite is removed from the scene or
is no longer visible, Sprite Kit can delete the texture data if it
needs that memory for other purposes. This automatic memory management
simplifies but does not eliminate the work you need to do to manage
art assets in your game.
The texture object itself is just a placeholder for the actual texture
data. The texture data is more resource intensive, so Sprite Kit loads
it into memory only when needed.
If you already have an SKTexture object, you can create new textures
that reference a portion of it. This approach is efficient because the
new texture objects reference the same texture data in memory.
Try to delete them with this code :
override func update(currentTime: CFTimeInterval) {
self.enumerateChildNodesWithName("nodeName") {
node, stop in
if (node is SKSpriteNode) {
let sprite = node as SKSpriteNode
// Check if the node is not in the scene
if (sprite.position.x < -sprite.size.width/2.0 || sprite.position.x > self.size.width+sprite.size.width/2.0
|| sprite.position.y < -sprite.size.height/2.0 || sprite.position.y > self.size.height+sprite.size.height/2.0) {
sprite.removeFromParent()
println("outside")
}
}
}
}
Don't forget to named your node :
node.name = "nodeName"
Hope your crash will stop

Resources