読者です 読者をやめる 読者になる 読者になる

髪も切れるiOSエンジニアのブログ

元美容師エンジニアの成長と奮闘の記録

Git-hub、Sourcetree、Xcodeでバージョン管理

最近弊社マンハッタンコードに新入社員が入りまして、後輩と呼べる存在ができました。
演習課題としてSwiftのトレーニングをしてもらってるのですが、
そのソースをレビューするためにGit-hubを使い
ついでにバージョン管理の導入部分を覚えてしまおうという作戦です。
今回はローカルとリモートにリポジトリを作成する手順をおさらいします。

使用ツール

Git-hub
Source tree 2.3.1
Xcode 8.2.1

導入手順

リモートリポジトリ作成

Git-hubログイン後のトップページにて
f:id:Tansok:20170116011231p:plain
「New Repository」を選択
f:id:Tansok:20170116012220p:plain
リポジトリ名を入力
②Publicを選択
③Create Repositoryを押下

リモートリポジトリをクローンしてローカルリポジトリを作成する

続いてローカルリポジトリをSource treeを使って作成します。
まずはローカルリポジトリ用のディレクトリを作ります。
f:id:Tansok:20170116013337p:plain
ファインダーから新規でフォルダを作成
f:id:Tansok:20170116013526p:plain
次はSource treeの作業です。
「+新規リポジトリ」を押下 → 「URLからクローン」を選択
f:id:Tansok:20170116013841p:plain
このような画面になります。
ここで一旦Git-hubに戻ります。
f:id:Tansok:20170116012653p:plain
リモートリポジトリのパスをコピーします。
f:id:Tansok:20170116015055p:plain
①「ソースURL」にコピーしたリモートリポジトリのパスをペースト
②「保存先のパス」入力フォームを選択(パスが自動入力される)
③「...」を選択してフォルダを開く
f:id:Tansok:20170116014628p:plain
①さっき作ったローカルのディレクトリを選択
②「開く」を押下
f:id:Tansok:20170116014834p:plain
クローンを押下
f:id:Tansok:20170116015238p:plain
自動でSource treeのバージョン管理画面が立ち上がります。
これでローカルリポジトリの作成が完了

xcodeから新規プロジェクトを作成

新規プロジェクト作成方法は割愛します。
新規プロジェクトの保存先選択時に
f:id:Tansok:20170116015827p:plain
①先ほどローカルリポジトリを作成した同じディレクトリを選択する
②Createを押下
f:id:Tansok:20170116020222p:plain
プロジェクトが作成され、
Source treeに差分が反映されていれば完了です。

まとめ

自分が業界入りたての時に苦労したのがこの作業でした。
懐かしい。
今後後輩の備忘録として役立てば何より。

おやすみなさい。

実はできない!UISliderのタップ移動

f:id:Tansok:20170114010612p:plain
UISliderはタブをスワイプで移動させるUIパーツですが、
タップでタブを移動させたい場面がやたら多い印象。
参考記事を探してもObj-C版はあるけど、Swiftはなかった(多分だよ)

環境

Xcode 8.2.1
Swift 3.0

実装

たったの2stepで実現可能

  • UISliderの独自カスタムクラスを実装
  • UISliderにカスタムクラスをセット

UISliderの独自カスタムクラスを実装

まずは独自クラスでカスタムUISliderを作成します。

import UIKit

class MySlider: UISlider {
    
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        return true
    }
}

たったこれだけ

UISliderにカスタムクラスをセット

あとはStoryboardからUISliderをセットして、
Custom Classに独自UISliderクラスをセットするだけ
f:id:Tansok:20170114012404p:plain
コードから使いたい時は

var slider: MySlider!

ですね。

おしまい。

Screen Heroでペアプログラミング

2016年ももうおわり。来年からは自社サービス開発も本格的に始動するから
ますます濃い年になるのではないでしょうかい。
そんな我社マンハッタンコードに最近、画面共有でのペア開発ツールが導入されましたのでご紹介。

Screen Hero

screenhero.com
Slackアカウント一つで普段使っているSlackチーム間でのペアプログラミングが可能になる優れもの!

導入手順

まずはブラウザ版Slackチームにサインインします。
f:id:Tansok:20161223202456p:plain
左上のプロフィールの編集等ができるプルダウンを開き
「Apps&integrations」を選択します。

f:id:Tansok:20161223192035p:plain
検索フォームで「screen」と検索すれば「Screenhero」が候補にでてくるので選択します。

f:id:Tansok:20161223192354p:plain
使用ガイド画面に来ました。ここでは一旦「expand」を選択して、出現したテキスト内のリンク「Create your free Screenhero account」を選択します。

f:id:Tansok:20161223192954p:plain
緑のボタン「Try Screenhero with...」を選択します。

f:id:Tansok:20161223193310p:plain
「Download Screenhero」を選択します。
zipファイルがダウンロードされますので展開してScreenheroを起動します。

f:id:Tansok:20161223193718p:plain
すると右上のメニューバーの部分にScreenheroのロゴが追加されます。
ここからいつでも起動可能になります。
起動したらサインインを求められます。

f:id:Tansok:20161223200943p:plain
スクリーン共有したいユーザを選んで、
「Share your screen」を選択します。

f:id:Tansok:20161223201533p:plain
シェアを求められたユーザには上のダイアログが表示されるので
「Accept Share」を選択します。
これでスクリーンの共有が可能になります。
当然ペアプログラミングツールなので、相手の画面に書き込みことが可能です!
リモートの作業が多くなるプロジェクトにはもってこいのツールの一つになること間違えなしですね!

Screenhero作業中にお腹が痛くなってトイレに中座すると
意地悪な先輩にエロサイトをたくさん開かれる被害にあいますのでご注意ください。

自作家計簿アプリ製作の軌跡④ 〜UIパーツにシャドウを〜

これからアプリ内のUIパーツにシャドウを設定していくことが多くなりそうだ。
一発メソッド呼び出しでシャドウをつけたい!かつ細かい設定ができるようにしたいと!
ということで拡張してシャドウ用メソッドを作りました。

環境

xcode 8.1
swift 3.0

実装

UIViewクラスを拡張します。

import UIKit

// UIViewを拡張して
extension UIView {
    /// UIViewを継承するUIパーツにシャドウを設定するメソッド
    /// - parameter opacity: 影の濃さ
    /// - parameter radius: 影のぼかし具合
    /// - parameter x: 水平方向の影の出し具合
    /// - parameter y: 垂直方向の影の出し具合
    ///
    func setShadow(opacity: CGFloat, radius: CGFloat, x: CGFloat, y: CGFloat) {
        self.layer.shadowOpacity = opacity
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowRadius = radius
        self.layer.shadowOffset = CGSize(width: x, height: y)
    }

呼び出しは

setLabel.setShadow(opacity:1.0 radius: 2.0, x: 0, y: 3.0)

UIViewを拡張してるから
UIViewを継承するUIパーツならなんでも使える!
もっと便利な拡張があったら教えてください!

つづく

自作家計簿アプリ製作の軌跡③ 〜NavigationControllerの実装〜

今回はNavigationControllerの実装です。
アプリの仕様的に階層の深い作りにする気はないので
NavigationBarをStoryBoardで取り入れればいいかとも思いましたが、
将来的なバージョンアップで画面数の増加も考えてNavigationControllerを取り入れました。
でも、実装している中で大きな誤解に気づいたので記事にします。

環境

xcode 8.1
swift 3

実装

前提として、自分の家計簿アプリはトップ画面にTabBarを置いた作りです。
なんとなくNavigationControllerってStoryBoardで言うところの
遷移の一番初めの親のViewに置くイメージが強かったので
f:id:Tansok:20161126235620p:plain
迷わずこうした訳です。
NavigationControllerのrootViewControllerにTabBarControllerを設定してます。
実機でデバッグしてみても全画面にNavigationBarが設置されており
「よしっ」なんて思いましたが、落とし穴がありました。
それぞれのViewControllerの「title」を設定してみてもNavigationBarに
表示されません!なんでだ?!
そこで素直に先人の知恵を求めてみました。
あったあった。
TabBarControllerとNavigationControllerを同時に使う(Storyboard) - Qiita
tabBarControllerとUINavigationControllerを同時に使いたい! - Takahiro Octopress Blog
要するに、階層構造にしたい親のViewに設定しなければいけないってこと。
NavigationControllerのrootViewController以下の遷移は「push」か「show」結ばれていなければならないってこと。
自分のアプリのトップ画面はTabBarControllerで、その子は「viewControllers」で3画面持っている状態です。
なんか理科の電流の授業を思い出しましたけど、並列繋ぎではNavigationControllerは機能しないんですね。
f:id:Tansok:20161127001336p:plain
結論はこう。
TabBarから分岐した遷移の配下からNavigationControllerを置きましょう。

おまけ

ちなみにNavigationControllerが複数になったとしても
AppDelegateクラスのdidFinishLaunchingWithOptions内で

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // ナビゲーションバーのカラー設定
        UINavigationBar.appearance().barTintColor = UIColor.orange()
        // ナビゲーションバーのフォント設定(フォント名、サイズ)
        let attributes =
            [NSForegroundColorAttributeName:UIColor.white,
             NSFontAttributeName: UIFont(name:"Futura", size:25)!]
        UINavigationBar.appearance().titleTextAttributes = attributes
        // タブバーのカラー設定
        UITabBar.appearance().barTintColor = UIColor.orange()
        
        return true
    }

appearance()メソッドを使えば全画面NavigationBarを一括で変更、統一できます。

つづく

自作家計簿アプリ製作の軌跡②-2 〜Calendarの実装〜

swift

前回からの続きです。
自作家計簿アプリ製作の軌跡②-1 〜Calendarの実装〜 - 髪も切れるiOSエンジニアのブログ
今回はその月のカレンダーに表示する日付を取得できるようにしていきます。

環境

xcode 8
swift 3.0

作業切り分け

1、CalendarCellクラスを追加 ⇨ 完了
2、一旦CollectionViewを最低限実装する ⇨ 完了
3、DateManagerクラスの追加 ⇨ ここから
3-1、その月のカレンダーの最初の週の日曜日が何日かを取得する
3-2、その月のカレンダーの最後の週の土曜日が何日かを取得する
3-3、-1から-2までが何日あるかを取得する

実装

3、DateManagerクラスの追加

まずはカレンダーの日付取得専用のクラス、DateManager.swiftクラスファイルを追加しましょう。
後に必要になる変数定数の宣言もしておきます。

import Foundation

class DateManager: NSObject {

    var selectedDay = Date()
    var beginningDate = Date()
    var lastDate = Date()
    let calendar = Calendar.current

}

このクラス内で、
・その月のカレンダーの最初の週の日曜日が何日か
・その月のカレンダーの最後の週の土曜日が何日か
・その月のカレンダーの表示に必要なセルの数
を一括で管理できるようにします。

3-1、その月のカレンダーの最初の週の日曜日が何日かを取得する

    /// カレンダーの起点の日付を取得
    func firstDateOfMonth() -> Date {
        /// 現在日付のコンポーネント
        var components = calendar.dateComponents([.year,.month,.day], from: selectedDay)
        // 日付の要素を1日に指定
        components.day = 1
        /// 今月1日のデータ
        let firstDate = Calendar.current.date(from: components)
        /// 今月1日の曜日
        let weekOfFirstDay = calendar.component(.weekday, from:firstDate!)
        /// 取得した1日の曜日から、同じ週の日曜日が何日になるかを取得する
        let startDateOfCalender = calendar.date(byAdding: .day, value: 1 - weekOfFirstDay, to: firstDate!)!
        return startDateOfCalender
    }

ここでポイントとなるのは、「今月1日が何曜日なのか?」です。
let weekOfFirstDay = calendar.component(.weekday,from:firstDate!)
で曜日を取得すると、曜日のindex番号が返却されます。
日曜日 ⇨ 1
月曜日 ⇨ 2
・・・
土曜日 ⇨ 7
(取得した曜日のindex番号 - 1)日分日付を戻せば前の月の最後の日曜日の日付(=今月のカレンダーの最初の日曜日の日付)を求めることができます。
let startDateOfCalender = calendar.date(byAdding: .day, value: 1 - weekOfFirstDay, to: firstDate!)!

3-2、その月のカレンダーの最後の週の土曜日が何日かを取得する

    func endDateOfMonth() -> Date {
        /// 今月の最後の日のデータ
        let lastDate = calendar.nextDate(after: selectedDay, matching: DateComponents(day:1), matchingPolicy: Calendar.MatchingPolicy.nextTime)
        /// 今月の最後の日付の曜日
        let weekOfLastDate = calendar.component(.weekday,from: lastDate!)
        // 今月の最後の日付の曜日を調べて、その要素数だけ進んだものが右下(次の月の初めで計算している事に注意)
        let lastDateOfCalender = calendar.date(byAdding: .day, value: 7 - weekOfLastDate, to: lastDate!)!
        return lastDateOfCalender
   }

3-1と同様に今度は最終週の土曜日の日付を取得しています。

3-3、3-1から3-2までが何日あるかを取得する

まずは現在の月のカレンダー表示に必要なセルの個数を決定します。

    /// 現在の月のセルの数を返すメソッド
    func dateCountInCurrentMonth() -> Int {
        // 始まりの日と終わりの日を取得
        beginningDate = firstDateOfMonth()
        lastDate = endDateOfMonth()
        // 始点から終点の日数
        return calendar.dateComponents([.day], from:beginningDate ,to:lastDate).day! + 1
    }

return calendar.dateComponents([.day], from:beginningDate ,to:lastDate).day! + 1
最後に"+ 1"していることに注意です。
カレンダーの始点から終点までの日数をそのまま返却してしまうとセルの数としては一つ足りません。

次にコレクションViewのindex番号から各日付のセルに表示していく日にちの文字列を返すメソッドを追加します

    /// カレンダーの始点から引数で渡ってきたカレンダーセルのインデックスを加算した日付の文字列を返す
    func convertDateFormat(index: Int) -> String {
        /// カレンダーの起点の日付にカレンダーセルのインデックス番号を足した日にち
        let date = calendar.date(byAdding: .day, value: index, to: beginningDate)
        return calendar.component(.day, from: date!).description
    }

これでDateManagerクラスの実装は完了。

カレンダークラスの修正

あとはカレンダーを表示しているクラスからDateManagerクラスを呼び、必要なメソッドを利用していきます。

//// CalendarViewController.swift
    /// カレンダーのセルの個数を返す
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // return 30
        return dateManager.dateCountInCurrentMonth()
    }
//// CalendarViewController.swift
    /// 各セルに日付を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CalendarCell", for: indexPath) as! CalendarCell
        //// ここ追加
        cell.dateNumber.text = dateManager.convertDateFormat(index: indexPath.row)
        return cell
    }

セルのindex番号を引数にconvertDateFormatメソッドを呼ぶだけです。
実行してみましょう。
f:id:Tansok:20161119134204p:plain

完成!

最後に

この記事を書いてて気付きましたが、
現在の日付データが一貫してグリニッジ標準時で作ってます。
日本の標準時にしないと月末月初で影響でちゃう?なんて懸念点があります。
対応しないとね。

自作家計簿アプリ製作の軌跡②-1 〜Calendarの実装〜

swift

今回はアプリの顔となるカレンダー画面を実装していきます。
ベースはUICollectionViewを使います。
複雑な印象があったけど、作業を切り分ければ簡単。

環境

xcode 8.0
Swift 3.0

参考記事

swift3にも対応済みのこの記事にはお世話になりました。
http://qiita.com/robokunx/items/ed3bbb4edb94e226b8db
http://qiita.com/robokunx/items/60acfaa6e6834e6726f1

実装

切り分けます。
1、CalendarCellクラスを追加
2、一旦CollectionViewを最低限実装する
3、DateManagerクラスの追加
3-1、その月のカレンダーの最初の週の日曜日が何日かを取得する
3-2、その月のカレンダーの最後の週の土曜日が何日かを取得する
3-3、-1から-2までが何日あるかを取得する

1、CalendarCellクラスを追加

Cocoa Touch Class から
UICollectionViewCellをxib付きで作成します。
f:id:Tansok:20161117233825p:plain
自分のアプリのカレンダーのデザインはセルのど真ん中に日付が一つのシンプルなものなので
UILabelをど真ん中に一個。このLabelは実装クラスにも紐付けておきます。
f:id:Tansok:20161117234111p:plain

2、一旦CollectionViewを最低限実装する

1、で作ったカスタムセルでCollectionViewを実装します。
「最低限」としたのはこの段階では「一旦カレンダーっぽい画面を表示させる」だけで、日付に関しては考えません。
切り分け切り分けw
StoryBoardでUICollectionViewを設置します。
この時もとから設置されてるCollectionViewCellのCustomClassの設定を忘れずに
f:id:Tansok:20161118001316p:plain
このCalendarCellは1、で作ったカスタムセルのこと。
実装クラスにOutlet接続します。
実装クラスに必要なデリゲートを継承させます。
後に必要になる週の数の変数も宣言しておきます

class CalenderViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
     @IBOutlet weak var calenderView: UICollectionView!
    let weekDayCount: Int = 7

viewDidLoadで
・CollectionViewのデリゲートに自分自信のクラスをセット
・1、のxibで作ったカスタムセルの登録
を行います。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        calenderView.delegate = self
        calenderView.dataSource = self
        calenderView.backgroundColor = UIColor.white
        
        self.calenderView.register(UINib(nibName: "CalenderCell", bundle: nil), forCellWithReuseIdentifier: "CalenderCell")   
    }

ここからCollectionViewの必要なメソッドを実装していきます。
まずはセルの個数を指定するメソッド

    /// カレンダーのセルの個数を返す
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 一旦30日として、セルの数を30個にしておきます
        return 30
    }

次はセルの内容を設定するメソッドです

    /// セルの内容を設定するメソッド
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // viewDidLoadで登録したカスタムセルのcollectionViewのセルに設定
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CalenderCell", for: indexPath) as! CalenderCell
        // 一旦何も設定せずにreturn
        return cell
    }

最後にセルのサイズとマージン(余白)設定です。

    /// セルのサイズを設定
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // 一つのセルの幅 = コレクションViewの横幅 ➗ 7(週の日数)としている
        let width: CGFloat = collectionView.frame.size.width / CGFloat(weekDayCount)
        // 高さはコレクションViewを6等分します。
        let height: CGFloat = collectionView.frame.size.height / 6
        return CGSize(width: width, height: height)
    }
    
    /// セルの垂直方向のマージンを設定
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    /// セルの水平方向のマージンを設定
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

高さを6等分しているのは、カレンダーの段って最大6段ですよね。それだけw
自分のアプリの仕様上、セルとセルの間の余白は取りません。コレクションViewはデフォルトでマージンが入っているため、
こちらで明示的にマージンを0に指定しないといけません。
では一旦実行
f:id:Tansok:20161118003811p:plain
自分は週を表示する行を、UICollectionViewとは別にViewと、その子のLabelを7つ設置して表示させています。
カレンダーのベースとなるコレクションViewを設置することができました。
次回はカレンダーの日付関連
DateManagerクラスを実装していきます。

つづく
自作家計簿アプリ製作の軌跡②-2 〜Calendarの実装〜 - 髪も切れるiOSエンジニアのブログ