Resizable UIWindow on iOS

Pete Barber from C#, C++, Windows & other ramblings

I came across a Todo item in my inbox (I often email myself things todo) whilst tidying it up following Christmas, entitled "Make a resizable UIWindow". I can remember sending this to myself but not what I was reading or watching that prompted me to do it other than I thought it must be possible to play around with the default style of main UIWindow that an iOS App displays most of its content in.

Normally, when running an iOS App the main UIWindow is black and takes up the entire devices screen. It is possible to have more than one UIWindow but the usual case for this is when content is being displayed on a secondary display, e.g. AirPlay. On MacOS it is common to have many instances of UIWindow with these being displayed on the Mac's desktop. It's normal to think of an iOS device as having a desktop as when using an App (or multiple if using Multi-tasking) then the desktop/background is completely obscured.

None the less, UIWindow is a subclass of UIView. This inherently means it has a Frame, i.e. a position in it's parent space plus a width & height. I wondered if these could be altered - they can!

I wrote a very simple single page App which contains a UIView that by default has the screen dimensions and is coloured blue, on top this, roughly in the centre is a red rectangle (a child UIView).

There are a set of 3 gesture recognisers (pinch, rotate & pan) that can be applied to either the Red Rect, the View or the underlying Window. Which one they're applied too is set using the Picker control hosted by the UIView.



The next picture shows all 3 components resized, rotated and moved around. Note how the Status Bar has almost disappeared due the black text colour now being on a black background (though the green battery indicator is still present) as no UIWindow is there.



By dragging the Window over the Status Bar it's revealed again.



Next, the Red Rectangle has been completely detached from the it's parent UIView. At this point touch events do not seem to be sent to it any more.



Taking this further my moving (via the pan gesture) the View from off the Window this too stops receiving input. At this one nothing can be done with the App.



That's not completely true. The App can be rotated. This has some bizarre results probably not helped by there being no constraints.


The code for the App is available on GitHub though the code for ViewController is below



The only slightly strange things happening here are:


  • The re-assignment of the Gesture Recognizers to the selected UIView
  • For each of the methods invoked by the Gesture Recognizers how they 'reset' the main parameter modified by the gesture after each invocation.
If you run the project, as the there are multiple Gesture Recognizers but multiple simultaneous Gesture Recognition support hasn't been enabled then in particular pinch & rotate must be discreet gestures. Also, rotation can be fiddly to get working especially if UI element has been pinched to a small size.

I'm not really aware of any practical application of this. It might be interesting to try have 2 UIWindows displayed at the same time though I'm not sure this is possible and could easily be achieved with 2 UIViews. Perhaps to have 2 sub-apps each in its own UIWindow which can be swapped between.

A slight enhancement on Developing tvOS Apps with Swift

Pete Barber from C#, C++, Windows & other ramblings

Apple announced tvOS yesterday. Downloading Xcode 7.1 Beta comes with the SDK and simulator for tvOS apps. The official documentation starts to run through how to create a basic app but is doesn't mention where to place and load the JS from and the same for the TVML.

Fortunately and vert quickly Jamerson Quave put together a tutorial.

I followed the Apple docs but checked Jameson's tutorial to verify the missing declaration of

var appController: TVApplicationController?

from AppDelegate and also for the JS and then TVML loading. I don't understand and the docs don't seem to see where the JS & TVML should be loaded from. They seem to suggest it should be remote, i.e. not part of the App Bundle but I don't know why. Anyhow I thought I'd see if I could.

The following assumes you've got to the end of Jameson's tutorial.

Loading the JS file; that then loads the TVML is easy. Add main.js to your application and change the lines within application:didFinishLaunchingWithOptions in AppDelegate.swift from:

let jsFilePath = NSURL(string: "http://localhost:8000/main.js") let javascriptURL = jsFilePath! appControllerContext.javaScriptApplicationURL = javascriptURL!

To

guard let jsUrl = NSBundle.mainBundle().URLForResource("main", withExtension: "js") else
{
    return false
}

This just loads the Javascript file (main.js) from the bundle instead. It's not a great improvement but it removes one dependency on the local web server.

I then tried to add hello.tvml to the bundler and modify main.js to create the fetched (via XmlHttpRequest). Unfortunately I couldn't create the Document in the JS. It seems that the normally (I've not done JS in a long time so what do I know) available document object isn't available to more documents and/or elements can't be created.

An attempt to create one, i.e.

var otherDoc = Document()

gives

015-09-10 21:53:50.213 tv1[55699:1483712] ITML <Error>: Document is not a function. (In 'Document()', 'Document' is an instance of IKDOMDocumentConstructor) - file:///Users/pete/Library/Developer/CoreSimulator/Devices/C2E7E5BD-1823-48BF-89E9-D3A499EE778A/data/Containers/Bundle/Application/F9C514E1-1A95-46A8-83D1-1BC96BC9A220/tv1.app/main.js - line:18:25

The objects mentioned in the TVJS documentation don't seem to be able to create one either.

Anyway, hopefully another small step. Full source on github.

A good be being dumb here and another look at the docs & samples suggests that writing apps. via JS is just one way and that a more iOS like app. can be written. Perhaps this is similar to Windows Metro that had both a JS and .Net (C#) version of WinRT; & C++ for completion.

Drawing into bitmaps and saving as a PNG in Swift on OS X

Pete Barber from C#, C++, Windows &amp; other ramblings

Not an in depth post today. For a small iOS Swift/SpriteKit game I'm writing for fun I wanted a very basic grass sprite that could be scrolled; to create a parallax effect. This amounts to a 800x400 bitmap which contains sequential isosceles triangles of 40 pixels with random heights (of up to 400 pixels) and coloured using a lawn green colour.

Initially I was creating an SKShapeNode and creating the triangles above but when scrolling the redrawing of these hurt performance, especially when running on the iOS Simulator hence the desire to use a sprite.

I had a go at creating these with Photoshop. Whilst switching to a sprite improved performance the look of the triangles drawn by hand wasn't as good as the randomly generated ones. Therefore I thought I'd generate the sprite.

It wasn't really practical to do this on iOS as the file was needed in Xcode so I thought I'd try experimenting with a command line OS X (Cocoa) program in Swift. A GUI would possibly be nice to preview the results (and re-generate if needed) and to select the save-to file location but this solution sufficed.

I'd not done any non-iOS Swift development and never generated PNGs so various amounts of Googling and StackOverflow-ing was needed. Whilst the results of these searches were very helpful I didn't come across anything showing a complete program to create a bitmap, draw into it and then save so the finished program is presented below. It's also available as a gist.


1:  import Cocoa  
2:
3: private func saveAsPNGWithName(fileName: String, bitMap: NSBitmapImageRep) -> Bool
4: {
5: let props: [NSObject:AnyObject] = [:]
6: let imageData = bitMap.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: props)
7:
8: return imageData!.writeToFile(fileName, atomically: false)
9: }
10:
11: private func drawGrassIntoBitmap(bitmap: NSBitmapImageRep)
12: {
13: var ctx = NSGraphicsContext(bitmapImageRep: bitmap)
14:
15: NSGraphicsContext.setCurrentContext(ctx)
16:
17: NSColor(red: 124 / 255, green: 252 / 255, blue: 0, alpha: 1.0).set()
18:
19: let path = NSBezierPath()
20:
21: path.moveToPoint(NSPoint(x: 0, y: 0))
22:
23: for i in stride(from: 0, through: SIZE.width, by: 40)
24: {
25: path.lineToPoint(NSPoint(x: CGFloat(i + 20), y: CGFloat(arc4random_uniform(400))))
26: path.lineToPoint(NSPoint(x: i + 40, y: 0))
27: }
28:
29: path.stroke()
30: path.fill()
31:
32: }
33:
34: let SIZE = CGSize(width: 800, height: 400)
35:
36: if Process.arguments.count != 2
37: {
38: println("usage: grass <file>")
39: exit(1)
40: }
41:
42: let grass = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(SIZE.width), pixelsHigh: Int(SIZE.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)
43:
44: drawGrassIntoBitmap(grass!)
45: saveAsPNGWithName(Process.arguments[1], grass!)
46:

Resolving strong references between Swift and Objective-C classes – Using unowned and weak references from Swift to Objective-C classes

Pete Barber from C#, C++, Windows &amp; other ramblings

My Swift and SpriteKit exploration continues. At the moment I'm writing the collision handling code.

Rather than derive game objects from SKSpriteNode with each derived class containing the code for handling collisions with the other types of game objects I'm following something akin to a Component-Entity model.

I have per-game-object handler classes of which an instance of each is stored in the actual SKSpriteNode's userData dictionary. In turn each handler instance has a reference to the SKSpriteNode that references it. Given ARC is used this is a classical strong-reference chain which will prevent memory from being freed. The usual solution to this in Objective-C is to have one of the references be weak. In Swift there are two types of weak references: weak which is the same as in Objective-C and unowned (which I think is new). The difference is that an unowned reference can never be nil, i.e. it's optional whether a weak pointer reference an object but an unowned pointer must always reference something. As such the member variable is always defined using let and must be initialized, i.e. an init method is required.

The following code shows how I was intending to implement this. There is the strong reference from node.UserData to PhysicsActions and then the unowned reference back again from PhysicsActions.

class PhysicsActions
{
  unowned let node : SKSpriteNode

  init(associatedNode : SKSpriteNode)
  {
    // Store really weak (unowned) reference
    self.node = associatedNode
  }

  func onContact(other : PhysicsActions)
  {
     // Do stuff with node

  }
}

class func makeNode(imageNamed name: String) -> SKSpriteNode
{
  let node = SKSpriteNode(imageNamed: name)
  node.userData = NSMutableDictionary()
  // Store strong reference
  node.userData["action"] = makeActionFn(node)
  return node

}

However, when I went to use this code it crashed within the onContact method when it attempted to use the node. Changing this the reference type from unowned to weak fixed this, e.g.

weak let node : SKSpriteNode?

This verified that the rest of the code was ok so this seemed to look like another Swift/Objective-C interoperability issue. Firstly, I made a pure Swift example which is a simplified version from the The Swift Programming Language book.

class Foo
{
  var bar : Bar?

  func addBar(bar: Bar)
  {
    self.bar = bar
  }
}

class Bar
{
  unowned let foo : Foo
  init(foo : Foo)
  {
    self.foo = foo
  }

  func f()
  {
    println("foo:\(foo)")
  }
}

var foo : Foo? = Foo()
var bar = Bar(foo: foo!)
foo!.รง(bar)

bar.f()

Which works and results in:

foo:C14XXXUnownedTest3Foo (has 1 child)

Ok, not a fundamental problem but let's try having an unowned reference to an Objective-C class which is just like the real case as that's what SKSpriteNode is.

Foo2.h

@interface Foo2 : NSObject
@end

Foo2.m

@implementation Foo2

-(id)init
{
  return [super init];
}

@end

main.swift

class Bar2
{
  unowned let foo2 : Foo2
  init(foo2 : Foo2)
  {
    self.foo2 = foo2
  }

  func f()
  {
    println("foo2:\(foo2)")
  }
}

var foo2 = Foo2()
var bar2 = Bar2(foo2: foo2)
bar2.f()

Which when foo2.f() is invoked results in:

libswift_stdlib_core.dylib`_swift_abortRetainUnowned:
0x100142420:  pushq  %rbp
0x100142421:  movq   %rsp, %rbp
0x100142424:  leaq   0x17597(%rip), %rax       ; "attempted to retain deallocated object"
0x10014242b:  movq   %rax, 0x348be(%rip)       ; gCRAnnotations + 8
0x100142432:  int3   
0x100142433:  nopw   %cs:(%rax,%rax)

Again, changing unowned let foo2 : Foo2 to weak var foo2 : Foo2? works. 

I can't explain what the actual problem is but it looks like the enhanced weak reference support (unowned) only works with pure Swift classes. If you have cyclic references to Objective-C classes from Swift then don't use unowned. In fact writing the previous sentence led me to try the following:

let orig = Foo2()
unowned let other = orig
println("other:\(other)")
println("orig:\(orig)")

No cyclic references, just an ordinary reference counted instance of Foo2 (the Objective-C class) which is then assigned to an unowned reference. The final call to println will keep the instance around until the end. This crashes as per the previous example when the other variable is accessed. Changing the type assigned to orig from Foo2 (Objective-C) to Foo (Swift) make it work.

Therefore it seems unowned should not be used to refer to Objective-C classes, just Swift classes.



Beware: Despite the docs SKNode userData is optional

Pete Barber from C#, C++, Windows &amp; other ramblings

In the Swift documentation for SKNode the userData member (a dictionary) is defined as follows:

userData

A dictionary containing arbitrary data.

Declaration

SWIFT
var userData: NSMutableDictionary!

OBJECTIVE-C

@property(nonatomic, retain) NSMutableDictionary *userData

However, in Objective-C the userData member is initially nil. Given that this is the same class then it should also be in Swift and using it, e.g.

let node = SKSpriteNode(imageNamed: name)
node.userData["action"] = Action() // custom class

causes a crash:

fatal error: Can't unwrap Optional.None

This is because the it is in fact nil despite the '!' following the Swift definition. This must be a documentation bug. The correct code is:

let node = SKSpriteNode(imageNamed: name)
node.userData = NSMutableDictionary()
node.userData["action"] = Action() // custom class