昨日はルールの変更に合わせてボードの修正をした。
今日は実際にルールを表示する画面を作る。
ルール画面
さっそくコードを。
//============================== // YWF //------------------------------ // RuleScene.swift //============================== import SpriteKit import WebKit public class RuleScene: SKScene { public static var scale: CGFloat = 1.0 private static let margin: CGFloat = 20.0 private static let headerHeight: CGFloat = 80.0 private static let headerOffset: CGFloat = 10.0 private static let logoHeight: CGFloat = 60.0 private static let buttonHeight: CGFloat = 60.0 private static let infoFileName: String = "RuleInfo" private static let infoFileExtension: String = "txt" private static let infoFile: String = RuleScene.infoFileName + "." + RuleScene.infoFileExtension private static let indexHTML: String = "index.html" private static let files: [String] = [ "Board.png", "Piece.png", "Yoiko.png", "Waruiko.png", "Futsunoko.png", "Token.png", "InitialBoard.png", "8directions.png", "Change.png", ] private class func copyFilesIfNeed() -> NSURL { let fileManager = NSFileManager.defaultManager() let originalInfoURL = NSBundle.mainBundle().URLForResource(RuleScene.infoFileName, withExtension: RuleScene.infoFileExtension)! let sourceURL = originalInfoURL.URLByDeletingLastPathComponent! let temporaryDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory())! let destURL = temporaryDirectoryURL.URLByAppendingPathComponent("www") let copiedInfoURL = destURL.URLByAppendingPathComponent(RuleScene.infoFile) fileManager.createDirectoryAtURL(destURL, withIntermediateDirectories: true, attributes: nil, error: nil) let copiedInfo: NSString! = NSString(contentsOfURL: copiedInfoURL, encoding: NSUTF8StringEncoding, error: nil) if copiedInfo != nil { let originalInfo = NSString(contentsOfURL: originalInfoURL, encoding: NSUTF8StringEncoding, error: nil)! if copiedInfo == originalInfo { // file is latest. return destURL.URLByAppendingPathComponent(RuleScene.indexHTML) } else { fileManager.removeItemAtURL(destURL, error: nil) } } fileManager.copyItemAtURL(sourceURL.URLByAppendingPathComponent(RuleScene.infoFile), toURL: destURL.URLByAppendingPathComponent(RuleScene.infoFile), error: nil) fileManager.copyItemAtURL(sourceURL.URLByAppendingPathComponent(RuleScene.indexHTML), toURL: destURL.URLByAppendingPathComponent(RuleScene.indexHTML), error: nil) for file in RuleScene.files { fileManager.copyItemAtURL(sourceURL.URLByAppendingPathComponent(file), toURL: destURL.URLByAppendingPathComponent(file), error: nil) } return destURL.URLByAppendingPathComponent(RuleScene.indexHTML) } private var exitButton: ButtonNode! private var webView: WKWebView! public override init(size: CGSize) { self.exitButton = nil self.webView = nil super.init(size: size) self.scaleMode = .AspectFill let backgroundTexture = SKTexture(imageNamed: "Background") let background = SKSpriteNode(texture: backgroundTexture) background.position = CGPoint(x: size.width/2.0, y: size.height/2.0) self.addChild(background) let headerTexture = SKTexture(imageNamed: "Header") let header = SKSpriteNode(texture: headerTexture) header.position = CGPoint(x: 0.0, y: self.size.height/2.0 - RuleScene.headerOffset) background.addChild(header) let logoTexture = SKTexture(imageNamed: "Logo") let logo = SKSpriteNode(texture: logoTexture) logo.position = CGPoint(x: (-self.size.width/2.0 + RuleScene.margin + logo.size.width/2.0), y: -RuleScene.logoHeight/2.0) header.addChild(logo) self.exitButton = ButtonNode(type: .Exit) self.exitButton.position = CGPoint(x: (self.size.width/2.0 - RuleScene.margin - self.exitButton.size.width/2.0), y: -RuleScene.buttonHeight/2.0) //self.exitButton.addObserver(self) header.addChild(self.exitButton) let webBackground = SKShapeNode(rectOfSize: CGSize(width: (self.size.width - RuleScene.margin*2.0), height: (self.size.height - RuleScene.headerHeight - RuleScene.margin*2.0))) webBackground.lineWidth = 0.0 webBackground.fillColor = SKColor.whiteColor().colorWithAlphaComponent(0.5) webBackground.position = CGPoint(x: 0.0, y: -RuleScene.headerHeight / 2.0) background.addChild(webBackground) } public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public override func didMoveToView(view: SKView) { // workaround for WKWebView bug. let indexURL = RuleScene.copyFilesIfNeed() self.webView = WKWebView(frame: CGRectZero) self.webView.setTranslatesAutoresizingMaskIntoConstraints(false) self.webView.backgroundColor = UIColor.clearColor() self.webView.opaque = false view.addSubview(self.webView) view.addConstraint(NSLayoutConstraint(item: self.webView, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1.0, constant: RuleScene.margin / RuleScene.scale)) view.addConstraint(NSLayoutConstraint(item: self.webView, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -RuleScene.margin / RuleScene.scale)) view.addConstraint(NSLayoutConstraint(item: self.webView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: (RuleScene.headerHeight + RuleScene.margin) / RuleScene.scale)) view.addConstraint(NSLayoutConstraint(item: self.webView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: -RuleScene.margin / RuleScene.scale)) self.webView.loadRequest(NSURLRequest(URL: indexURL)) } public override func willMoveFromView(view: SKView) { self.exitButton.removeAllObservers() self.webView.removeFromSuperview() self.webView = nil } }
SKSceneを継承したRuleSceneというクラスを作って、ルール画面を描画するようにしている。
ただし、ルールはHTMLで用意するので、実際に表示するのはWKWebView。
そこで、シーンがビューに追加された直後に呼ばれるRuleScene#didMoveToView(_: SKView)
の中でWKWebViewを作って、それをシーンを表示しているビューのサブビューにしている。
このとき、iOSでローカルのHTMLを表示する方法について。 - いものやま。で言及したとおり、WKWebViewにバグがあるので、それを回避するためにファイルのコピーを(必要なら)行っている。
また、WKWebViewのサイズをちょうどよくするために、制約(NSLayoutConstraint)を上下左右に追加している。
なお、この制約を追加するとき、まずはサブビューを親ビューに登録しないといけないみたい。
制約を設定してからサブビューを親ビューに登録しようとしたら、実行時エラーになってしまった。
あと、制約の長さをスケールで割り算しているのは、いろんな画面サイズに対応する方法について。 - いものやま。で言及した方法を使っているため、シーンの単位長とビューの単位長が変わっているから。
スケールで割り算することで、シーンでのサイズをビューでのサイズに変換してやっている。
この他にちょっと説明が必要なところとして、webBackgroundというSKShapeNodeをシーンに追加している点と、WKWebViewのbackgroundColorプロパティを透明色、opaqueプロパティをfalseにしている点があると思う。
これは何のためかというと、WKWebViewの背景色を半透明にするため。
まず、何もしない状態だと、WKWebViewの背景は不透明で、せっかく木目の背景画像を使っているのが活きてこない。
そこで、まずWKWebViewのbackgroundColorプロパティとopaqueプロパティをセットすることで、WKWebViewの背景を透明にすることが出来る。
ただ、それだと文字が読みにくいので、完全な透明ではなく、半透明にしたいところ。
そこで、webBackgroundという半透明のSKShapeNodeをシーンに追加することで、まるでWKWebViewの背景が半透明になったかのように見せることが出来る、というわけ。
動作確認
ビューコントローラをちょっと書き換えて動かしてみると、次のような感じ。
iPhone 4S (ja)
iPad (en)
デバイスによって、WKWebViewのサイズがちょうどよくなっていることとか、言語設定にしたがったHTMLが表示されていることとかが分かると思う。
それと、スクロールやリンクによるジャンプが出来ていることも分かると思う。
今日はここまで!