月: 2020年1月
Swift でアナログクロックを作ってみた
.NET/C# からすると、全体の設計が違っているので、とても戸惑いました。最初は、UIViewに直接描画するのかと思いましたが、秒、分、時それぞれごとにCAShapeLayer に時計の針を描画して、レイヤーの原点を translateしてセンタリングして、さらに時間に合わせて rotate し、で重ね合わせる方法が良さそうです。秒針をスムースに動かすのであれば、Animation を適用すればOK。1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
import UIKit class UIClockView: UIView { var secLayer: CAShapeLayer! var minLayer: CAShapeLayer! var hourLayer: CAShapeLayer! override init(frame: CGRect) { super.init(frame: frame) addHourLayer() addMinLayer() addSecLayer() } required init?(coder: NSCoder) { super.init(coder: coder) addHourLayer() addMinLayer() addSecLayer() } func drawClock() { let date = Date() let calendar = Calendar.current let hour = Int(calendar.component(.hour, from: date)) let min = Int(calendar.component(.minute, from: date)) let sec = Int(calendar.component(.second, from: date)) drawHourLayer(hour: hour, min: min) drawMinLayer(min: min, sec: sec) drawSecLayer(sec: sec) } func addHourLayer() { hourLayer = CAShapeLayer() hourLayer.isHidden = true hourLayer.strokeColor = UIColor.red.cgColor hourLayer.lineWidth = 5.0 let line = CGMutablePath() line.move(to: CGPoint(x:0,y:0)) line.addLine(to:CGPoint(x:80,y:0)) hourLayer.path = line self.layer.addSublayer(hourLayer) } func drawHourLayer(hour: Int, min: Int) { let radian = 2.0 * CGFloat.pi * (CGFloat(hour) + CGFloat(min) / 60.0) / 12.0 - 0.5 * CGFloat.pi; let translation = CGAffineTransform(translationX: self.bounds.width/2, y: self.bounds.height/2) let rotation = CGAffineTransform(rotationAngle: radian) let trans = rotation.concatenating(translation) hourLayer.setAffineTransform(trans) hourLayer.isHidden = false } func addMinLayer() { minLayer = CAShapeLayer() minLayer.isHidden = true minLayer.strokeColor = UIColor.blue.cgColor minLayer.lineWidth = 3.0 let line = CGMutablePath() line.move(to: CGPoint(x:0,y:0)) line.addLine(to:CGPoint(x:80,y:0)) minLayer.path = line self.layer.addSublayer(minLayer) } func drawMinLayer(min: Int, sec: Int) { let radian = (2.0 * CGFloat.pi * CGFloat(min) + CGFloat(sec) / 60.0) / 60.0 - 0.5 * CGFloat.pi; let translation = CGAffineTransform(translationX: self.bounds.width/2, y: self.bounds.height/2) let rotation = CGAffineTransform(rotationAngle: radian) let trans = rotation.concatenating(translation) minLayer.setAffineTransform(trans) minLayer.isHidden = false } func addSecLayer() { secLayer = CAShapeLayer() secLayer.isHidden = true secLayer.strokeColor = UIColor.black.cgColor secLayer.lineWidth = 1.0 let line = CGMutablePath() line.move(to: CGPoint(x:0,y:0)) line.addLine(to:CGPoint(x:100,y:0)) secLayer.path = line self.layer.addSublayer(secLayer) } func drawSecLayer(sec: Int) { let radian = 2.0 * CGFloat.pi * CGFloat(sec) / 60.0 - 0.5 * CGFloat.pi; let translation = CGAffineTransform(translationX: self.bounds.width/2, y: self.bounds.height/2) let rotation = CGAffineTransform(rotationAngle: radian) let trans = rotation.concatenating(translation) secLayer.setAffineTransform(trans) secLayer.isHidden = false } } |
Swift でのタイマー
Timer
objcパターン
1 2 3 4 5 6 7 8 9 |
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(ViewController.timerUpdate), userInfo: nil, repeats: true) ... @objc func timerUpdate() { print("update") } |
新しいパターン1
1 2 3 4 5 6 7 8 9 |
Timer.scheduledTimer( withTimeInterval: 1, repeats: true, block: TimerCallback) ... func TimerCallback(timer: Timer) { print("Called") } |
新しいパターン2
1 2 3 4 5 |
Timer.scheduledTimer( withTimeInterval: 1, repeats: true, block: {(timer: Timer) in print("Block Called")} ) |
インターバルの単位は秒。0を指定した場合は、0.1mSec。
タイマーの停止:同一スレッド内から、Timer.invalidate()を呼び、RunLoopから削除する。
Windows で Mac のキーボード、マウスを使うときのmemo
ダウンロード – Boot Camp サポートソフトウェア 5.1.5769 からドライバーをダウンロードする。
次のドライバーをダウンロード。
AppleKeyboardInstaller64.exe
AppleWirelessMouse64.exe
BlueTooth で接続する。この時、いったんマウス、キーボードの電源をオフにしてから、再接続する。
mouseのホイールが逆なので、次を参考に逆にする。
SKEmitterNode
File -> New -> Fileから、SpriteKit Particle File を追加する。
パラメータを調整する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// ViewController.swift import UIKit import SpriteKit class ViewController: UIViewController { let emitterNode = SKEmitterNode(fileNamed: "MyParticle.sks")! override func viewDidLoad() { super.viewDidLoad() addParticle() } private func addParticle() { let skView = SKView(frame: view.frame) let scene = SKScene(size: view.frame.size) skView.presentScene(scene) skView.isUserInteractionEnabled = false scene.anchorPoint = CGPoint(x: 0.5, y: 0.5) scene.addChild(emitterNode) emitterNode.position.y = 0 emitterNode.particlePositionRange.dx = 100 view.addSubview(skView) } } |
CMMotionManager でモーションを取得
デバイスの傾きをCMMotionManagerで取得すれば、水準器みたいなことができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// GameScene.swift import SpriteKit import GameplayKit import CoreMotion class GameScene: SKScene { let redBallCategory:UInt32 = 0b0001 let circleCategory:UInt32 = 0b0001 private let motionManager = CMMotionManager() override func didMove(to view: SKView) { let ballRadius: CGFloat = 20 let redBall = SKShapeNode(circleOfRadius: ballRadius) redBall.fillColor = .red redBall.position = CGPoint(x: 160, y: 320) redBall.physicsBody = SKPhysicsBody(circleOfRadius: ballRadius) let circle = SKShapeNode(circleOfRadius: 150.0) circle.position = CGPoint(x:160, y: 320) circle.strokeColor = .white circle.physicsBody = SKPhysicsBody(edgeChainFrom: circle.path!) circle.physicsBody?.restitution = 0.75 redBall.physicsBody?.collisionBitMask = 0b0001 circle.physicsBody?.categoryBitMask = 0b0001 self.addChild(redBall) self.addChild(circle) redBall.physicsBody?.categoryBitMask = redBallCategory circle.physicsBody?.categoryBitMask = circleCategory } override func sceneDidLoad() { super.sceneDidLoad() guard motionManager.isDeviceMotionAvailable else { return } motionManager.deviceMotionUpdateInterval = 0.1 // 0.1 Sec motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical, to: OperationQueue.current!, withHandler: { [weak self] (motion, error) in guard let motion = motion, error == nil else { return } self?.physicsWorld.gravity = CGVector(dx:motion.attitude.roll * 5.0, dy:-motion.attitude.pitch * 5.0) }) } } |
Swift のSpriteKitで Collisions, Bitmasks, Physics
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// // GameScene.swift import SpriteKit import GameplayKit class GameScene: SKScene { let redBallCategory:UInt32 = 0x001 let blueBallCategory:UInt32 = 0x001 let groundCategory:UInt32 = 0x001 override func didMove(to view: SKView) { let ballRadius: CGFloat = 20 let redBall = SKShapeNode(circleOfRadius: ballRadius) redBall.fillColor = .red redBall.position = CGPoint(x: 280, y: 320) redBall.physicsBody = SKPhysicsBody(circleOfRadius: ballRadius) let blueBall = SKShapeNode(circleOfRadius: ballRadius) blueBall.fillColor = .blue blueBall.position = CGPoint(x: 360, y: 320) blueBall.physicsBody = SKPhysicsBody(circleOfRadius: ballRadius) var splinePoints = [CGPoint(x: 0, y: 300), CGPoint(x: 100, y: 50), CGPoint(x: 400, y: 110), CGPoint(x: 640, y: 20)] let ground = SKShapeNode(splinePoints: &splinePoints, count: splinePoints.count) ground.physicsBody = SKPhysicsBody(edgeChainFrom: ground.path!) ground.physicsBody?.restitution = 0.75 redBall.physicsBody?.collisionBitMask = 0b0001 blueBall.physicsBody?.collisionBitMask = 0b0001 ground.physicsBody?.categoryBitMask = 0b0001 self.addChild(redBall) self.addChild(blueBall) self.addChild(ground) ground.physicsBody?.categoryBitMask = groundCategory redBall.physicsBody?.categoryBitMask = redBallCategory blueBall.physicsBody?.categoryBitMask = blueBallCategory } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// GameViewController.swift import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() if let view = self.view as! SKView? { // Load the SKScene from 'GameScene.sks' if let scene = SKScene(fileNamed: "GameScene") { // Present the scene view.presentScene(scene) } view.ignoresSiblingOrder = true view.showsFPS = true view.showsNodeCount = true } } override var shouldAutorotate: Bool { return true } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { if UIDevice.current.userInterfaceIdiom == .phone { return .allButUpsideDown } else { return .all } } override var prefersStatusBarHidden: Bool { return true } } |