フォルダ以下の全プロジェクトでgit pullする
下のように、フォルダ以下に複数プロジェクトがありそれらでgit pull
したい時のtipsです。
コマンドは下の通りです。
ls | xargs -I{} git -C {} pull origin master
xargs -I{}
でls
の結果を{}
と書いた箇所で使えます。
git -C
はフォルダ指定でgitコマンドを実行するので、フォルダ名({}
)を渡す事で各プロジェクトのgit pull
を実行しています。
iPhoneアプリにストリートビューを表示する
GoogleMapsSDKを使ってアプリ上にストリートビューを表示してみました。
準備
最初にGoogleAPIManagerでAPIキーを作成します。
まずは下サイトでGoogle Maps SDK for iOSを有効化します。
次は下ページでAPIキーを作成します。
「認証情報を作成」ボタンでAPIキーを選択します。
実装
次は今作ったキーを元にストリートビューを表示します。
最初にCocoaPodsでGoogleMapsライブラリをインストールします。
target 'MyApp' do use_frameworks! pod 'GoogleMaps' end
ライブラリをインストールしたらAPIキーのセットを行います。
AppDelegateを以下のように修正します。
import UIKit import GoogleMaps @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { GMSServices.provideAPIKey("API_KEY") return true } }
最後にストリートビューの貼り付けを行います。
ViewControllerを以下のように修正します。
import UIKit import GoogleMaps class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let panoView = GMSPanoramaView(frame: view.bounds) view.addSubview(panoView) panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude:150.312)) } }
これで画面にストリートビューを表示する事ができました。
ビューには様々なオプションがあります。
let panoView = GMSPanoramaView(frame: view.bounds) view.addSubview(panoView) panoView.orientationGestures = false // スワイプで方向転換 panoView.zoomGestures = false // ピンチで拡大・縮小 panoView.navigationGestures = false // 矢印タップで移動 panoView.navigationLinksHidden = true // 矢印を非表示 panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude:150.312))
ビューにはDelegateも設定する事ができます。
extension ViewController: GMSPanoramaViewDelegate { func panoramaView(_ view: GMSPanoramaView, willMoveToPanoramaID panoramaID: String) {} func panoramaView(_ view: GMSPanoramaView, didMoveTo panorama: GMSPanorama?) {} func panoramaView(_ view: GMSPanoramaView, didMoveTo panorama: GMSPanorama, nearCoordinate coordinate: CLLocationCoordinate2D) {} func panoramaView(_ view: GMSPanoramaView, error: Error, onMoveNearCoordinate coordinate: CLLocationCoordinate2D) {} func panoramaView(_ view: GMSPanoramaView, error: Error, onMoveToPanoramaID panoramaID: String) {} func panoramaView(_ panoramaView: GMSPanoramaView, didMove camera: GMSPanoramaCamera) {} func panoramaView(_ panoramaView: GMSPanoramaView, didTap point: CGPoint) {} func panoramaView(_ panoramaView: GMSPanoramaView, didTap marker: GMSMarker) -> Bool { return true } func panoramaViewDidStartRendering(_ panoramaView: GMSPanoramaView) {} func panoramaViewDidFinishRendering(_ panoramaView: GMSPanoramaView) {} }
Processingで電子アート入門
Processingという電子アート用の開発環境を試してみました。
インストール
下からアプリをダウンロードします。
実装
円を描画
Processingを起動すると以下のような画面になっています。
ここにコードを書いていきます。
Processingでは主に下の2メソッドに処理を書いていきます。
setup
は最初に一度だけ呼ばれるメソッド、draw
は毎秒呼ばれるメソッドです。
void setup() { } void draw() { }
円の描画はellipseメソッドを使います。
第一引数、第二引数は中心のxyで第三引数と第四引数は横幅と縦幅になります。
void setup() { ellipse(50, 50, 40, 40); }
実行すると以下のように円が表示されます。
背景色を変える場合はfillメソッドを使います。
void setup() { fill(51, 51, 204); ellipse(50, 50, 40, 40); }
線を引く
線を引くにはLineメソッドを使います。
最初の2つの引数が始点の座標、次の2つが終点の座標です。
void setup() { line(10, 30, 90, 70); }
線の色はstrokeメソッドで変更します。
void setup() { stroke(51, 51, 204); line(10, 30, 90, 70); }
文字を表示する
文字の表示はtextメソッドを使います。
void setup() { text("Hello!!", 50, 50); }
クリックイベントを取得する
クリックしているかどうかはmousePressedを使えば取得できます。
drawは毎秒呼ばれるので、ここでmousePressedを使った判定式を書けばクリック時に処理させる事ができます。
void draw() { if (mousePressed) ellipse(50, 50, 40, 40); }
参考URL
【ios】FirebaseAnalyticsのイベント送信を試してみる
Firebaseでイベントを送る方法を調べてみました。
GoogleAnalyticsの下処理に相当するものです。
let tracker = GAI.sharedInstance().defaultTracker tracker?.send(GAIDictionaryBuilder.createEvent(withCategory: "category", action: "action", label: "label", value: NSNumber(value: 1)).build() as? [AnyHashable: Any])
まずは下URLを参考にFirebaseAnalyticsの設定を行います。
イベント送信は下のようにlogEventで行います。
Analytics.logEvent("ログ1", parameters: [AnalyticsEventTutorialBegin: "Value" as NSObject])
FirebaseAnalyticsの画面には以下のように表示されます。
ユーザープロパティーという、GoogleAnalyticsのカスタムディメンションのような機能もあります。
ユーザープロパティーのセットは下のように行います。
Analytics.setUserProperty("テスト1", forName: "testProperty") Analytics.logEvent("ログ1", parameters: [AnalyticsEventTutorialBegin: "Value" as NSObject])
これを使えばイベントをフィルタリングする事ができます。
【UIRefreshControl】UITableViewを下に引っ張ってリロード
UITableViewに下に引っ張るどリロードする実装を試してみました。
こちらはUIRefreshControlというクラスを使います。
UITableViewへのリロード処理の追加
実装は非常に簡単で、UIRefreshControlをUITableViewにaddSubviewするだけです。
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() let refreshControl = UIRefreshControl() tableView.addSubview(refreshControl) } }
これを実装して下に引っ張ると下のようにローディング表示されます。
下に引っ張った時のコールバックメソッドはvalueChangedイベントを使って設定します。
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() let refreshControl = UIRefreshControl() refreshControl.addTarget(self, action: #selector(ViewController.refreshControlValueChanged(sender:)), for: .valueChanged) tableView.addSubview(refreshControl) } func refreshControlValueChanged(sender: UIRefreshControl) { print("テーブルを下に引っ張った時に呼ばれる") } }
処理が終わったら、UIRefreshControlのendRefreshingメソッドを呼んでインジケータを非表示にします。
func refreshControlValueChanged(sender: UIRefreshControl) { DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { sender.endRefreshing() }) }
UIRefreshControlのカスタマイズ
UIRefreshControlはインジケータの色変更とテキスト追加ができます。
let refreshControl = UIRefreshControl() refreshControl.tintColor = UIColor.blue refreshControl.attributedTitle = NSAttributedString(string: "てきすと")
表示は下の通りです。
User Notifications frameworkでローカル通知を送ってみる
iOS10から使えるようになったUser Notifications frameworkを試してみました。
通知の許可を得る
まずはXcodeにUser Notifications frameworkを追加します。
許可を得る処理は以下の通りです。
引数で使いたいオプションを指定します。
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound], completionHandler: { result, error in })
これはiOS9以前での以下の書き方に相当します。
let settings = UIUserNotificationSettings(types: [.badge, .alert, .sound], categories: nil) UIApplication.shared.registerUserNotificationSettings(settings)
通知を作成する
通知の作成は以下の通りです。
今回は10秒後に起動する通知を作成しました。
let content = UNMutableNotificationContent() content.title = "たいとる" content.subtitle = "さぶたいとる" content.body = "ほんぶん" content.badge = NSNumber(value: 1) content.sound = UNNotificationSound.default() let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false) let request = UNNotificationRequest(identifier: "Identifier", content: content, trigger: trigger) let center = UNUserNotificationCenter.current() center.add(request)
上ではTimeIntervalを使ってTriggerを作りましたが、DateComponentを使ってTriggerを作る事もできます。
let component = DateComponents(calendar: Calendar.current, year: 2017, month: 3, day: 27, hour: 23, minute: 30) let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false)
UNUserNotificationCenterにはdelegate
import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { let content = UNMutableNotificationContent() content.title = "たいとる" let component = DateComponents(calendar: Calendar.current, year: 2017, month: 3, day: 27, hour: 23, minute: 30) let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false) let request = UNNotificationRequest(identifier: "Identifier", content: content, trigger: trigger) let center = UNUserNotificationCenter.current() center.delegate = self center.add(request) return true } } extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // バックグラウンドで来た通知をタップしてアプリ起動したら呼ばれる } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // アプリがフォアグラウンドの時に通知が来たら呼ばれる } }
フォアグラウンドで通知が来た時、下のようにcompletionHandlerを実行すれば通知を表示する事ができます。
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .badge, .sound]) }
通知のキャンセルはremoveAllPendingNotificationRequestsメソッドを使います。
removePendingNotificationRequestsメソッドでIDを指定したキャンセルも可能です。
let center = UNUserNotificationCenter.current() center.removeAllPendingNotificationRequests() center.removePendingNotificationRequests(withIdentifiers: ["Identifier"])
GameplayKitの「Entities and Components」を試してみる
GameplayKitの「Entities and Components」を試してみました。
Entities and Componentsとは
Entities and Componentsとは、コンポーネント指向プログラミングをサポートするものです。
コンポーネント指向プログラミングとは、そのオブジェクトの性質を継承などではcomponentの追加を使って表すものです。
例えばタップ時にジャンプするオブジェクトにはジャンプコンポーネントを追加を、ランダムに移動するオブジェクトにはランダム移動コンポーネントの追加をすると言った具合です。
そうすることで、簡単に1つのオブジェクトが複数の性質を持たせる事ができます。
Entities and Componentsの実装
早速実装してみます。
今回はNodeを2つ作って、片方は横移動+タップで回転、もう片方は縦移動+タップで回転するようにします。
まずは画面にNodeを2つ表示します。
import SpriteKit import GameplayKit class GameScene: SKScene { let node1 = SKSpriteNode(color: UIColor(red: 0.1, green: 0.8, blue: 0.8, alpha: 1), size: CGSize(width: 100, height: 100)) let node2 = SKSpriteNode(color: UIColor(red: 0.8, green: 0.8, blue: 0.1, alpha: 1), size: CGSize(width: 100, height: 100)) override func didMove(to view: SKView) { node1.position.x = -100 node2.position.x = 100 addChild(node1) addChild(node2) } }
次にnode1は右方向、node2は上方向に動くようにします。
まずは上方向に移動する性質を表すHorizontalComponentクラスと横方向に移動する性質を表すVerticalComponentクラスを作ります。
class HorizontalComponent: GKComponent { let node: SKNode init(node: SKNode) { self.node = node super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func update(deltaTime seconds: TimeInterval) { node.position.x += 1 } } class VerticalComponent: GKComponent { let node: SKNode init(node: SKNode) { self.node = node super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func update(deltaTime seconds: TimeInterval) { node.position.y += 1 } }
続けて今作ったクラスをnode1、node2に適用します。
import SpriteKit import GameplayKit class GameScene: SKScene { let horizontalComponentSystem = GKComponentSystem(componentClass: HorizontalComponent.self) let verticalComponentSystem = GKComponentSystem(componentClass: VerticalComponent.self) var lastTime: TimeInterval = 0 let node1 = SKSpriteNode(color: UIColor(red: 0.1, green: 0.8, blue: 0.8, alpha: 1), size: CGSize(width: 100, height: 100)) let node2 = SKSpriteNode(color: UIColor(red: 0.8, green: 0.8, blue: 0.1, alpha: 1), size: CGSize(width: 100, height: 100)) let entity1 = GKEntity() let entity2 = GKEntity() override func didMove(to view: SKView) { node1.position.x = -100 node2.position.x = 100 addChild(node1) addChild(node2) let horizontalComponent = HorizontalComponent(node: node1) entity1.addComponent(horizontalComponent) horizontalComponentSystem.addComponent(horizontalComponent) let verticalComponent = VerticalComponent(node: node2) entity2.addComponent(verticalComponent) verticalComponentSystem.addComponent(verticalComponent) } override func update(_ currentTime: TimeInterval) { if lastTime == 0 { lastTime = currentTime } let deltaTime = currentTime - lastTime horizontalComponentSystem.update(deltaTime: deltaTime) verticalComponentSystem.update(deltaTime: deltaTime) lastTime = currentTime } }
これでnode1が右方向、node2が上方向に移動するようになりました。
entity1とentity2は下のupdateメソッドのように、片方のコンポーネントから別のコンポーネントのメソッドを呼び出す時に便利です。
class HorizontalComponent: GKComponent { let node: SKNode init(node: SKNode) { self.node = node super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func update(deltaTime seconds: TimeInterval) { node.position.x += 1 entity?.components.flatMap { $0 as? VerticalComponent }.forEach { $0.update(deltaTime: seconds) } } }
最後に、それぞれのnodeに「画面タップで回転」という性質を追加します。
「画面タップで回転」という性質を表すRotateComponentクラスを下のように作成します。
class RotateComponent: GKComponent { let node: SKNode init(node: SKNode) { self.node = node super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func rotate() { node.run(SKAction.rotate(byAngle: 2, duration: 1)) } }
今作ったcomponentをnode1とnode2に反映します。
import SpriteKit import GameplayKit class GameScene: SKScene { let horizontalComponentSystem = GKComponentSystem(componentClass: HorizontalComponent.self) let verticalComponentSystem = GKComponentSystem(componentClass: VerticalComponent.self) let rotateComponentSystem = GKComponentSystem(componentClass: RotateComponent.self) var lastTime: TimeInterval = 0 let node1 = SKSpriteNode(color: UIColor(red: 0.1, green: 0.8, blue: 0.8, alpha: 1), size: CGSize(width: 100, height: 100)) let node2 = SKSpriteNode(color: UIColor(red: 0.8, green: 0.8, blue: 0.1, alpha: 1), size: CGSize(width: 100, height: 100)) let entity1 = GKEntity() let entity2 = GKEntity() override func didMove(to view: SKView) { node1.position.x = -100 node2.position.x = 100 addChild(node1) addChild(node2) let horizontalComponent = HorizontalComponent(node: node1) entity1.addComponent(horizontalComponent) horizontalComponentSystem.addComponent(horizontalComponent) let verticalComponent = VerticalComponent(node: node2) entity2.addComponent(verticalComponent) verticalComponentSystem.addComponent(verticalComponent) // ここから今回追加 let rotateComponent1 = RotateComponent(node: node1) let rotateComponent2 = RotateComponent(node: node2) entity1.addComponent(rotateComponent1) entity2.addComponent(rotateComponent2) rotateComponentSystem.addComponent(rotateComponent1) rotateComponentSystem.addComponent(rotateComponent2) } override func update(_ currentTime: TimeInterval) { if lastTime == 0 { lastTime = currentTime } let deltaTime = currentTime - lastTime horizontalComponentSystem.update(deltaTime: deltaTime) verticalComponentSystem.update(deltaTime: deltaTime) lastTime = currentTime } // ここから今回追加 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { rotateComponentSystem.components.flatMap { $0 as? RotateComponent }.forEach { $0.rotate() } } }
これで画面タップでnodeが回転するようになりました。