App Store へのアップロードでThere was an error sending data to the iTunes Store. Scheduling restart shortly が出たとき自分はこれで解決した

f:id:motom552:20180405031818p:plain

いつもと同じ手順でXcodeのOrganizerでApp Storeへアップロードしたら次のエラーが出ました。

There was an error sending data to the iTunes Store. Scheduling restart shortly...

考えてみた所いくつか前回と異なる点がありました。

いつもと違うところ

  • Sierra から High Sierra にバージョンアップした
  • iTunes Connect や Xcode 内 Accounts がログイン期限切れしてた

おそらく一番の要因は最初のOSバージョンだと思います。その次のログインの期限切れも影響していると思います。

やったこと

  • 時間を置いた(約4時間)
  • iTunes Connect に入ったら何故か認証エラーになっていたので再ログインした

2つ目が正直謎です。 というのもアップロードする前に対象バージョンの枠を作るために一度 iTunes Connect に接続できているためです。

確実な解決方法は分かっていませんが、同様に起きている場合は一度認証が必要な箇所を一通りチェックしてみてはいかがでしょうか?

PlantUML Viewerで一瞬しか表示されないならJava8で治った

brew cask で最新版 java インストールしてダメだったけど、java8 でインストールしたら症状が治りました。 java9でもダメだった記事を見かけたので、多分java以外の所(graphviz or atom or PlantUML Viewer or PlantUML)が改修されないとダメなんだと思う。

UITableViewのセル間の境界線を変更する

セル間の境界線のスタイルと色と開始位置/終了位置を変更する方法です。

UITableViewのセル間の境界線を変更

class ViewController: UIViewController {
    @IBOutlet private weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 線の種類
        tableView.separatorStyle = .singleLine
        // 線の色
        tableView.separatorColor = UIColor.red
        // 先のインセット
        tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }
}

線の種類を変更する

UITableViewCellSeparatorStyle

意味
.none 境界線なし
.singleLine 境界線あり

線の開始位置と終了位置を調整する

separatorInsetを変更することで線の開始位置(左端)と終了位置(右端)を移動できます。 topとbottomの値は使われません。 デフォルトは UIEdgeInsetsMake(0, 3, 0, 11) です。 separatorInset - UITableView | Apple Developer Documentation

UITableViewで一番下までスクロールする方法

概要

UITableViewで一番下のセルまでスクロールする方法についてまとめました。

実装方法

UITableView#scrollToRow(at:at:animated)を使います。

第一引数のIndexPathにはrowとsectionを指定します。 この時にデータ配列から一番最後を指定することで一番下のセルまでスクロールできるようになります。

もしセクションが複数個の場合は、一番最後のセクション番号を指定しないといけません。 セクションは固定であれば数えて一番最後のセクション、動的であれば元となるデータ配列の一番最後を指定してください。

実装コード

class ViewController: UIViewController,
                      UITableViewDataSource,
                      UITableViewDelegate {
    @IBOutlet private weak var tableView: UITableView!
    private var data: [Data] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
        
        Array(0..<100).forEach {
            data.append(Data(name: "\($0)"))
        }
    }
    
    // MARK: - UITableViewDataSource and UITableViewDelegate
    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        }
        cell?.textLabel?.text = data[indexPath.row].name
        return cell!
    }
    
    func tableView(_ tableView: UITableView,
                   didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        tableView.scrollToRow(at: IndexPath(row: data.count - 1, section: 0),
                              at: UITableViewScrollPosition.bottom, animated: true)
    }
}

struct Data {
    let name: String
}

UITableViewScrollPosition.bottom はセルをスクリーンのどの位置に合わせるかのパラメータになります。 今回は一番のセルなのでbottom にしていれば問題ありません。

へそ曲げた我が子から要求分析してママと子ども間のデッドロックを解消する

概要

この記事は 子育てエンジニア・クリエイター Advent Calendar 2017の15日目の記事になります。

adventar.org

記事テーマに悩みましたが、パパエンジニアの強みを育児に活かす方法について書こうかな。 敢えて堅苦しく強気な書き方してみたらガイドラインとかベストプラクティスみたいな文章になって、楽しく書きました。

実際に自分の家で起きるケースと、自分がやってる行動をベースに書いてあります。

炎上案件の状況

ママは毎日子ども達に例えば次のようなことを頑張っています

  • 冷めない内、保育園に送れないために早く席に座らせてご飯を食べさせる
  • 体を冷やさないためにすっぽんぽんでテレビを見る子どもをお風呂に入らせる
  • 湯冷めで風邪ひかないためにすっぽんぽんで頭乾かさないで歌ってたり戦いごっこしてる子どもに服を着させて頭を乾かす

子ども達とママは毎日が同じコンディションではありません

毎日の繰り返しと日によって子どもの機嫌が悪くて言うことを聞かない日もあります。 同じようにママの体調が良くない日もあります。 それら条件をすべて満たすと、ママは鬼となります。子どもたちは恐怖で不満を吐き出さず拗らせるか、余計機嫌が悪くなっている状態です。

パパの取るべきだった行動

人のせいにする前に自分がやるべきだったことを考える

まずパパはこの状況を引き起こしたのは、自分の武器である論理的思考により家庭のルーチン効率化を図らなかったことを反省することから入ります。 状況に対して、子ども達やママのせいにしてはいけません。 今ある状況はなるべきしてなったのです。なので、必要・必然・ベストな状況だと捉えて早いとこ自分を納得させましょう。

これからパパが取るべき行動

両者の要求(言い分)を聞き出し、客観的に論理的思考で両者に線を引く作業です。 大事なのは子どもが年齢が4〜5歳だろうと15歳だろうと問わずに扱うことです。

パパエンジニアの強みを確認する

ここで念のためエンジニアとしての強みを家庭版に落とし込んでみましょう。

エンジニアの強みは、論理的思考と非エンジニアへの聞き取りです。 これらを家庭版として落とし込んだ場合どういった感じに使えそうでしょうかね。

ここでの論理的思考とは、冷静に客観的視点から状況の原因分析を行うことですね。

ここでの非エンジニアへの聞き取りは、自分の感情・思考を分析や言葉として落とし込むのが苦手な子どもへの多彩な質問で子どもでも気づいていないことを引き出すことですね。

ママには労いを子どもには粋な計らいを

ママは感情により目的に対する行動がズレてることがある

ママから話を聞き出すまえに労いと感謝の言葉を伝えましょう。ママは大人なので何を考えているのかは容易に聞き出せると思います。 注意としては、聞き出した内容(考え)が本質を捉えているかどうかは別件ということでしょうか。

子どもへの聞き出しは多種多様多彩に質問と確認を繰り返す

次に子どもへの聞き出しは、

  • Y軸の視線を合わせましょう
  • パパは君の味方であるオーラを全開で接しましょう
  • 子どもの要求/要望は採択はせずとも受け入れて肯定・共感しましょう。
  • 善悪判断は大人が勝手に決めて子どもに伝えるのではなく、子どもに外堀を囲むように状況/情報を伝えて、それから「どう思う?どうすればいいと思う?」と聞いて本人に判断させましょう
  • 子どもの言ってることが矛盾してたり、言ってることがバラバラだったりしたら言い方や質問の言葉を変えたり、状況を1個1個確認してみましょう。
  • 1個1個確認すると全部肯定する場合、1個わざと違うことを「◯◯だったんだよね?」と聞いて確認すると便利です。
  • わざと違うことを聞いても肯定する場合、子どもがまだ言葉を使ってやりとりできないので、強くハグしてダメな行動と、その行動で誰が悲しむのか伝えてあげましょう。

両者から引き出した要求を元に両者に伝える

ママから聞き出した要求をそのまま子どもに伝えてはいけません。それが伝わらないからこの状況になっているからです。

嫌いな人から要望を聞いても中々腹落ちはしにくいもの

まずはママ好きという言葉を子どもの口から言わせましょう。

  1. 好きな食べ物何?
  2. パパ好き?
  3. じゃぁママ好き?
  4. (↑で嫌いと言った場合)嫌い?じゃぁママいなくなって、ずっと帰ってこなくてもう一緒にお風呂入ったり、寝れなくなってもいい?笑ったママや美味しいご飯作るママいなくなってもいい?

ダメな行動で何が起きるのかを本人の口から言わせる

濡れた頭を乾かさない場合であれば、「乾かさなかったらどうなる?」と聞いてみます。 ここで風邪をひくとか、地震や火事があったら逃げれないって言ったら、次にうつります。

ママがして欲しいことではなくママが心配していることを伝える

子どもは何でママがあれやりなさい、これやりなさい、早くしなさいと言ってるのか、知らないです。 なのでママが好きを言わせたら、そんなママは、君を心配していることを教えます。 先ほど風邪をひくと子どもが言ったのであれば、「風邪ひいて苦しい君を見るとママは泣きたいくらい悲しくて心配らしいよ。。知ってた?」と

ママには子どもの本質的な要求を伝える

子どもにあの手この手色んな角度から質問したり話を引き出していくと、実はあまり繋がりのない所で寂しいとかもっと遊びたいとか、以前ママに話を聞いてもらえなかったとか、以前ママが約束破って思い出したとか、出てきます。

つまり子どもダメな行動ってアラートみたいなもので、アラート自体が要求じゃなかったりします。

本質的な要求や不満を聞き出せたら、ママはそれを聞いて謝るなり、週末一緒に遊ぶ約束したりして Done です。

今後パパが取るべき行動

KPTで言うTry, 再発防止策、テストケース追加といったところでしょうか。 最初に書いたそもそもこの状況を引き起こした要因は自分にあると考えて、取り組むと自分でもやれることは意外と見つかりやすいです。

if 子どもがダメな行動をとった本質 == ママと遊ぶ時間が少なくて寂しい {
 var パパがやるべき行動 = パパ行動サービス.get( ママ.get時間のかかる家事一覧(), パパ.get家事スキル() )
 if パパがやるべき行動.isEmpty {
  パパがやるべき行動 = 家事スキルを上げる
 }
 パパ.action(パパがやるべき行動)
}

育児に参加しにくい環境/状況のエンジニアもいると思います。 でもそこで諦め/丸投げせず、自分の武器を使えないか考えるのも頭の体操みたいなものでいいと思います。 実際やると分かりますが、子どもに対して要求を聞き出すって難しいし、慣れると仕事で非エンジニアから聞き出すのが楽に感じます。

UITableViewのセルの高さを動的に変更する

f:id:motom552:20171210212150p:plain
UITableViewのセル高さを動的に変更

概要

iOSで使われるUIベスト3に入るUITableView

そのUITableViewのセルの高さを内容に応じて動的に変更する方法についてまとめました。

実装方法

  • UITableView.rowHeightUITableViewAutomaticDimensionを設定する
  • セルのAuto Layoutの縦軸が全て繋がるように設定する

サンプルコード

サンプルコードは少し長いですが、実際はカスタムセルとセル内に表示するでかい文字列を生成するコードが大半です。

ViewController.swift

class ViewController: UIViewController {
    @IBOutlet private weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.register(UINib(nibName: "TableViewCell", bundle: nil),
                           forCellReuseIdentifier: "TableViewCell")
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 44.0
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
        cell.configure(with: TableViewCell.Model(title: cellTitle, desc: text, date: Date()))
        return cell
    }
    
    var cellTitle: String {
        return [title1, title2, title3][Int(arc4random()) % 3]
    }
    var text: String {
        return [text1, text2, text3][Int(arc4random()) % 3]
    }
    
    var title1: String { return "HogeHoge" }
    var title2: String { return "ダミー太郎" }
    var title3: String { return "本日晴天なり🇯🇵" }
    var text1: String {
        return """
        この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。
        """
    }
    var text2: String {
        return "ダミーテキスト。"
    }
    var text3: String {
        return """
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
        """
    }
}

TableViewCell.swift

class TableViewCell: UITableViewCell {
    struct Model {
        let title: String
        let desc: String
        let date: Date
        
        var dateString: String {
            let fmt = DateFormatter()
            fmt.dateFormat = "yyyy/MM/dd"
            return fmt.string(from: date)
        }
    }
    
    @IBOutlet private weak var titleLabel: UILabel!
    @IBOutlet private weak var descLabel: UILabel!
    @IBOutlet private weak var dateLabel: UILabel!
    
    private(set) var model: Model? {
        didSet {
            titleLabel.text = model?.title
            descLabel.text = model?.desc
            dateLabel.text = model?.dateString
        }
    }
    
    func configure(with model: Model?) {
        self.model = model
    }
}

レイアウト

f:id:motom552:20171210212157p:plain
UITableViewのセル高さを動的に変更するためのレイアウト

UITableViewの空セルの線(separator)を消す実装

f:id:motom552:20171210180222p:plain
UITableViewの空セルの線(separator)を消す

概要

iOSで使われるUIベスト3に入るUITableView

そんなUITableViewですがデザイナーからよく指摘される空セルには線(separator)を引かないで消す実装についてまとめました。

実装方法

UITableView.tableFooterView を空にするだけです。

サンプルコード

class ViewController: UIViewController {
    @IBOutlet private weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.tableFooterView = UIView()
    }
}

たったこれだけで線(separator)を消すことができます。