ListVIewにitemを表示する

Android開発に入門しました。今回はListViewを使ってみました。

ListViewを追加します。 制約を適当につけidをつけます。今回はItemListViewにしました。

f:id:churabou:20180914172741p:plainf:id:churabou:20180914172747p:plain

実際のコード

・MainActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setUpListView()
    }

    fun setUpListView() {
        val items = Array(20, { i -> "item $i" })
        val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items)
        ItemListView.adapter = adapter
        ItemListView.setOnItemClickListener { _, view, _, _ ->
            val textView = view.findViewById<TextView>(android.R.id.text1)
            Toast.makeText(this, "Clicked: ${textView.text}", Toast.LENGTH_SHORT).show()
        }
    }
}

プレビュー

f:id:churabou:20180914172743p:plain

所感

実機を持ってなくmac book airエミュレーターを動かすのがストレスですがいい感じです。 多分iOSで同じことやろうと、IBでやるにしろコードでやるにしろdelegateやdataSourceなど少し初心者にはとっつきにくかったと記憶します。

UILabelのフォントサイズを2倍にするとき、必要なboundsも2倍になるのか

ただのメモです。

結論から言うとぴったし2倍にはならないがだいたい二倍になる。

        let label = UILabel()
        label.textColor = .white
        label.backgroundColor = .red
        label.text = "test test test"
        label.center = view.center
        label.font = .boldSystemFont(ofSize: 20)
        view.addSubview(label)
        
        
        print(label.bounds) //(0.0, 0.0, 0.0, 0.0)
        label.sizeToFit()
        print(label.bounds) //(0.0, 0.0, 115.0, 24.0)
        
        //フォントサイズを二倍にする。
        label.font = .boldSystemFont(ofSize: 40)
        label.sizeToFit()
        print(label.bounds) //(0.0, 0.0, 225.333333333333, 48.0)
        let requiredSize: ((CGFloat) -> CGRect) = { (size) in
            
            let text = "test test test"
            let font: UIFont = .boldSystemFont(ofSize: size)
            let attributedText = NSAttributedString(string: text,
                                                    attributes: [.font: font])
            
            let greatest = CGSize(width: CGFloat.greatestFiniteMagnitude,
                                  height: CGFloat.greatestFiniteMagnitude)
            return attributedText.boundingRect(with: greatest,
                                                   options: .usesLineFragmentOrigin,
                                                   context: nil)
        }
        
        print(requiredSize(20)) //(0.0, 0.0, 114.990234375, 23.8671875)
        print(requiredSize(40)) //(0.0, 0.0, 225.05859375, 47.734375)

6月を振り返って+7月の目標

6月が終わった。なんとか生きてた。 望ましい形ではないが、少し前に進めた気がする。 6月はアウトプットの記事を書いたり、インターンの面接とかしてたら終わった。

アウトプット

今月はアウトプットを頑張ってみた気がする。 目標通り画像エディターの記事を3つ書けたしqiitaにも適当ながら記事を2つ投稿した。 RxSwiftの勉強した記事や、自分のホームページを作成しGitHub-Pagesで公開した。

インターンの面接やイベントで人事の方と話している中でアウトプットを頑張ろうと思った結果だ。 アウトプットするには時間がかかるのに自分の能力変わらない。心のどこかでそう思っていてあんまりアウトプットはしてこなかった。 6月にたくさん記事を公開して良かったと思う。

もう少しでストアにアプリをリリースできそうだ。

インターン

サマーインターンの選考に複数参加した。結果全滅だった。自分の実力不足もあるが、多分、他の学生たちと決定的な何かが違うのだと思う。自分も他の学生の面接に立ち会ってみたい。どんなモチベーションで何を考えているのだろうか。

だた17日の逆求人イベントと22日のIT*医療の企業での面談を経てはっきりしたことがある。

自分はビジネスに興味があること。 心のどこかで起業してみたいと思っていること。

その他

アプリ開発に関して自分の中で気づいたことがある。

  • UI/UXが好き。デザインも好きだし、機能を考えるのが好き。人に使ってもらって意見を聞くのも楽しい。
  • 設計が好き。 半年前初めてMVVMを学習した時感動したのを思い出した。どうやったら効率がよくなるのか、わかりやすいのか、かっこいいのか考えるのが楽しいと思った。ただ今の自分は若干スキルが足りないみたい。

あとはエンジニアになるなら自分は技術力高い系よりはサービス開発系とかマーケティング系だと思った。

CA.swiftに参加した。今後勉強に参加したら記事を書くことにする。 あとReactorKitの勉強会に行かなかったのは後悔している。 画像ライブラリの設計について聞いてみたかった。

7月の目標

ビジネスに興味があるのがわかった。SPIを受けて自分がどれほどバカになっていたか実感した。 英語も全然わからなくなっていた。今月はエンジニア以外の普通の勉強をしようと思った。 後一応ちゃんとテスト勉強して大学の単位もある程度取ろう。


  • 画像編集ライブラリを設計とUIこだわって公開したい。
  • Swiftの些細なことでも得た知見を別記事で公開する。
  • 既存のサービスを分析する。
  • 日経新聞を読む
  • アプリを公開する
  • 英語とか漢字とか衰えた脳を修正する。
  • 12単位くらいは取る。

【Swift】PhotoShopのacvファイルを使って画像にフィルターをかける

iOSには画像系のライブラリで有名なCPUImageあり、これを使えば.acvファイルを使用した画像フィルターが簡単に利用できます。 しかしこの機能を外部のライブラリに頼りたくないので自分で実装してみようと思います。

完成品

f:id:churabou:20180630120306g:plain

トーンカーブを使うには

実装

photoshopのacvファイルについてはこちら このカーブはスプライン曲線と呼ばれるもので、GPUImageはスプライン補間を使って再現している。らしいですが正直理解する気もないので、CPUImageから丸パクリします。

ここら辺はcreateLutとかtoneCurveTexureとかgithubで調べれば似たようなのが複数落ちてました。

①普通にやる(1pxごとに書き換える)

オリジナルの画像からピクセルデータの配列を作成して1pxごとに画素値を変更 してそれを使って新しい画像を作成する

まったく問題なく実装できた。がパーフォマンスに自信がない。

②CIKernel

kernel vec4 filterKernel(sampler inputImage, sampler toneCurveTexture) {
    vec4 textureColor = sample(inputImage,samplerCoord(inputImage));
    float redCurveValue = sample(toneCurveTexture, vec2(textureColor.r, 0.5)).r;
    float greenCurveValue = sample(toneCurveTexture, vec2(textureColor.g, 0.5)).g;
    float blueCurveValue = sample(toneCurveTexture, vec2(textureColor.b, 0.5)).b;
    return vec4(vec3(redCurveValue, greenCurveValue, blueCurveValue),textureColor.a);
}

0penGLのSLぽくかけるので使ってみたが、若干画像にムラができてしまった。色空間がなんとからしいのだが ③で実装できたので深追いはしていない。

③CIColorCube

CoreImageのフィルターのこれを使う。

結論

最終的に③で実装することになったのLUT画像をPhotoshopで作成し(acvファイルを解析する手間が省ける)、それを読みCIColorCubeで使いました。

ので色々調べましたがこの2つで十分でした。 - https://coherent-labs.com/converting-adobe-photoshop-acv-to-lut-for-color-grading/ - https://qiita.com/Ushio/items/b0a0cb7617ca44c3118d

一応acvファイルを読むコードを載せておきます。半年前のですが・・

github.com

感想

インターン先で個人事業主の方に、フォトショップで作成したのacvファイルとGPUImageを使ってフィルターをかけているんだけど、可能な限り内部化したいのでSwiftで実装してほしいと依頼されました。 当初はCGUImageのソースを読んで頭の中に"acvファイルを解析してOpenGLを使う"というイメージを持ちました。

しかし実際にはacvファイルも使用せず、またOpenGLの知識も一切必要ありませんでした。 昔高校の先生に理系の人間はまず前提から疑うとよく言われたのを思い出しました。

にしてもLUTという概念を発見するのに時間がかかりました。githubで類似コードを探しててcreateLutという関数を見つけたのですが、GPUImageではtoneCurveTextureという変数名がLutの配列だった。今回の話でいうと、この変数名にlutという文字があればもっと早く・・

画像エディターのテキスト入力時のラベルについて

引っ張ると回転しながらサイズも変えられるラベル。よくあるらしい。

初めて見たときは、これどうやって実装するんだ??と思ったけど、ラベルの中心が変わらないことに気づけば 適当に実装しても似た感じのができた。

デモ

ViewDidLoadに下記を貼ると

        let label = TextToolLabel()
        label.bind(to: view)
        label.text = "アイウエオ"

f:id:churabou:20180616231034g:plain

実装について

View自体はこんな感じに配置した。

f:id:churabou:20180616231820p:plain

メインの右下の丸は

中心を基準に回転と拡大縮小をする。フォントサイズが一定より小さいと、回転だけが行われる。

フォントサイズを更新 -> それにあうViewのサイズを決めるのか

Viewのサイズを変更 -> それに合うフォントのサイズを決定するのか

について悩んだ。

全体

class TextToolLabel: UIView {
    
    var text: String? {
        get {
            return label.text
        }
        
        set {
            label.text = newValue
            adjustSizeToFitFont()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .clear
        addSubview(label)
        addSubview(editView)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private lazy var label: UILabel  = {
        let l = UILabel()
        l.textColor = .black
        l.textAlignment = .center
        l.layer.borderColor = UIColor.gray.cgColor
        l.layer.borderWidth = 2
        l.isUserInteractionEnabled = true
        l.addGestureRecognizer(movePanGesture)
        return l
    }()
    
    private let circleSize: CGFloat = 30
    private lazy var editView: UIView = {
        let v = UIView()
        v.backgroundColor = .cyan
        v.layer.cornerRadius = circleSize / 2
        v.addGestureRecognizer(editPanGesture)
        //右下にぴったりくっつくように
        v.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin]
        v.frame.origin = CGPoint(x: bounds.maxX-circleSize, y: bounds.maxY-circleSize)
        v.frame.size = .init(width: circleSize, height: circleSize)
        return v
    }()

    func bind(to: UIView) {
        to.addSubview(self)
        center = to.center
    }
    
    private lazy var movePanGesture: UIPanGestureRecognizer = {
        return UIPanGestureRecognizer(target: self, action: #selector(actionMove))
    }()
    private var previousLocation: CGPoint = .zero
    private var initialCenter: CGPoint = .zero
    //Labelに追加。ラベルの位置を移動する。
    @objc private func actionMove(_ sender: UIPanGestureRecognizer) {

        let touchLocation = sender.location(in: superview)
        
        if sender.state == .began {
            previousLocation = touchLocation
            initialCenter = center
        } else {
            
            let dx = touchLocation.x - previousLocation.x
            let dy = touchLocation.y - previousLocation.y
            center = CGPoint(x: initialCenter.x + dx, y: initialCenter.y + dy)
        }
    }
    
    
    private lazy var editPanGesture: UIPanGestureRecognizer = {
        return UIPanGestureRecognizer(target: self, action: #selector(actionEdit))
    }()
    
    private let minimumFontSize: CGFloat = 10
    private var initialDistance: CGFloat = 0
    private var initialiFontSize: CGFloat = 0
    
    //右下の丸いViewに追加する。引っ張って拡大縮小と回転する。
    @objc private func c(_ sender: UIPanGestureRecognizer) {
    
         let touchLocation = sender.location(in: superview)
        
        switch sender.state {
        case .began:
            initialDistance = CGPoint.distance(from: center, to: touchLocation)
            initialiFontSize = label.fontSize

        default:
            
            /*回転する処理*/
 
            //viewの中心とタップした座標の正弦を取得する
            let angle = atan2(touchLocation.y - center.y, touchLocation.x - center.x)
            //現在の適応されている角度
            let dif = transform.rotateAngle
            //差分だけ回転する
            transform = transform.rotated(by: angle-dif)
            
            /*文字を大きくする(Viewを拡大する)処理*/
            let distance = CGPoint.distance(from: center, to: touchLocation)
            let scale = Float(distance / initialDistance)

            //フォントサイズを大きくする -> それに合うようにViewのSizeも大きくする。
            let size = initialiFontSize*CGFloat(scale)
            label.updateFontsize(to: max(size, minimumFontSize))
            adjustSizeToFitFont()
        }
    }
    

    //テキストが増えるたびにフォントは変えずにViewのサイズを変更する。
    private func adjustSizeToFitFont() {
        
        //現在のフォントサイズで、テキストをぴったり表示するのに必要なサイズを計算する。
        let attributedText = NSAttributedString(string: label.text!, attributes: [.font: label.font!])
        
        let greatest = CGSize(width: CGFloat.greatestFiniteMagnitude,
                              height: CGFloat.greatestFiniteMagnitude)
        let rect = attributedText.boundingRect(with: greatest, options: .usesLineFragmentOrigin, context: nil)
        label.bounds.size = rect.size
        
        bounds.size = CGSize(width: label.bounds.size.width + circleSize,
                             height: label.bounds.size.height + circleSize)
        label.center = bounds.center
    }
}


fileprivate extension CGPoint {

    static func distance(from: CGPoint, to: CGPoint) -> CGFloat {
        let dx = from.x - to.x
        let dy = from.y - to.y
        return sqrt(dx * dx + dy * dy)
    }
}


fileprivate extension CGRect {
    
    var center: CGPoint {
        return CGPoint(x: midX, y: midY)
    }
}

fileprivate extension CGAffineTransform {
    //radian
    var rotateAngle: CGFloat {
        return atan2(b, a)
    }
}

fileprivate extension UILabel {

    var fontSize: CGFloat {
        return font.pointSize
    }
    
    func updateFontsize(to: CGFloat) {
        font = UIFont(name: font.fontName, size: to)
    }
}

小さな会社でのインターンの記録

もしかしたら悪い印象を与えるかもしれませんが、自分の思っていることを素直に書きました。一部上から目線ですいません。

1社目

株式会社L メンバー5人

  • 早稲田の院生の先輩と同時にスタートしたが、5ヶ月後に受け入れ終了された。
  • 当時はインターン落ちまくっていたので正直採用してくれたことに感謝をしていた。

ひどい点

  • 無給でシフトとかない。忙しくて全然インターン構ってくれない。実務一切やらなかった。最終的にあちらのの都合で解散になった。

マイナス面

  • なし

よかったところ

  • mac book pro と ディスプレイをかして頂いた。
  • 妙な緊張感、インターンだから行かなければならない、などいい意味で束縛されていて勉強に専念できるようになった。 (家ならすぐに諦めそうなエラーが出ても粘ることができたのが非常に大きい)
  • 結果独学でiOSアプリを作れるようになった。

不満はかなりある。交通費すらも結局貰わなかったし。 とりあえずインターンやろう的なベンチャー企業が学生の時間を無駄にする事案が多発している気がする。


2社目

代表 + インターン8名程度

キャリアバイトで応募して採用をくださった2つ目の企業。

ひどい点

  • なし

マイナス面

  • 7月から中途で3人正社員が採用されたが2ヶ月以内に全員辞めてしまった。あまりいい環境でななかったと自分も思う。
  • 社長のエンジニアとしてのスキルは低い。(動けばいい的な考え)
  • インターンの学生が七人程度いたが、それぞれのタスクが違っていたため、黙々と一人で作業する感じだった。
  • もっとフィードバック受けれる環境のがよかったと今は思う。

よかったところ

  • 色々な経験ができた。
  • 行くだけで時給が発生した。960円から1400円まで上がった。
  • 最後にはクラウドワークスに掲載した案件に挑戦して実質10時間程度の作業で10万円もらえた。(報酬)

正直もっといい環境があったと思う。しかし自分を雇ってくれたこと、ちゃんと報酬をくれたこと、色々できた点で非常に感謝している。


3社目

株式会社N 個人事業主のところ。

ひどい点

  • ひどい訳ではないが、代表の気分次第のところがある

マイナス面

  • フルリモート(一人黙々やる環境よりは近くにiOSエンジニアがいる環境の方がいい)
  • フィードバックが少なかった
  • インターン生が4人雇われたのだが3人辞めちゃった。(他の子が長く続かないのも、ある種の指標になる気がする)
  • ちょっと成長するには厳しい環境。
  • 給与の申請が難しい。 リモートで時給制だったのだが、自分が費やした時間の1/3程度しか申請できなかったと思う。
  • mac book airでバイバリの開発は厳しかった。(前の2社ではProを貸してくれた。)

よかったところ。

  • 技術書があった。
  • 有名なアプリの開発に複数携われた。
  • 近所のオフィスが使えた。(一人でいると普通にメンタルやられる。)
  • 自分の成果物もできた。

代表は優しい人で、自分の話も聞いてくださり非常に良かったと思う。ただ自分の求める環境とは少し違っていたなと思う。


3社共にインターン生として受け入れてくださったことに非常に感謝している。

ただ自分がもっと成長できる環境が他にあるならもう、多少貪欲に環境を求める必要があるのかもしれないと思った。

特に2社目は、少しでも学べることがあるかもしれないと思い、結局1年近く続けてしまった。

何より、いろんな意味で情報量が少なすぎたことが悔やまれる。

前キータに書いたやつ

RxSwiftを2週間ほど触ってみて。

RxSwiftは学習コストが高いと言われている。 しかし2018年となった今は情報量が非常に多く日本語の記事もたくさんある。 すなわち学習難易度は低いのではないかと思う。

以下自分が2週間くらい手探りでわちゃわちゃやった結果、こんな感じで学んでいけばいいのかなと思ったことを整理して見た。


ざっくりと入門記事を眺めて見る。

オブザーバーパターンから始めるRxSwift入門

いきなり全部は無理。でもなんとなく読んで頭に入ってくる部分だけ吸収していけば多少は前提知識が生まれる。

 


適当にObservable作ってみる。

let observable = Observable<Int>.create { (observer) in 
observer.on(.next(0))
observer.on(.next(0))
observer.on(.next(0))
observer.on(.complete)
}


observable.subscribe { event in 
    switch event {
       case .next(let element): print(element)
       case .error(let error): print(error)
       case .completed: print("complete")
    }
}

RxSwift 入門 その1 - タコさんブログ

UIイベントを試してみる 

let label = UILabel()
let textField = UITextField()
textField.rx.text.bind(to: label.rx.text)

テキストの入力がラベルに反映される、button.rx.tapぐらいを試せれば十分だった。

あとは公式がすごい充実してる。

RxSwift/GettingStarted.md at master · ReactiveX/RxSwift · GitHub

RxSwift/SimpleValidationViewController.swift at master · ReactiveX/RxSwift · GitHub

 

似たような記事をぐるぐるぐるぐる何回も読めばだいたいわかってくる。

  • subjectってなんだ
  • hot, coldとは
  • weakとunownedって(基本)
  • Single Driverとは

気になったところを調べつつ、徐々に知識を広げて行く。

インクリメンタルサーチをやって見る。

これはREADMEに書いてあるやつ。私はオフィスにたまたま本があったのでこちらを使った。

gihyo.jp

最初怖いけどオペレータの使い方が徐々にわかってきた。

  • distinctUntilChanged
  • take
  • startWwith
  • catchErrorJustReturn

RxSwiftの機能カタログ

RxMarbles: Interactive diagrams of Rx Observables

 

MVVMで実装して見る。

rxswift mvvm sample とか github search mvvmとかで調べればいっぱい出てくる。

私はさっきの養成読本の著者の方のを参考に作ってみて

github.com

  • mearge
  • zip
  • withLatestFrom

などの合成を使えるようになった。何よりより実務に近付いた気がする。


これで入門は終わってなんとなく自分で動かせるようになった。 少し間違ってるかもしれないがMVVMの数字記憶アプリを作ってみたりした。

最低限はやったのであとはインターン行ったりして、できる人が近くにいる環境にいたい。

でもまだまだできることはたくさんあって。 公式のExampeとか

  • QiitaでRx関連の記事網羅したり
  • 勉強会の資料とか
  • そもそもRxSwiftのソース読めるところ結構あるし
  • RxSwift勉強すればRedux flux ReactorKitなども勉強できる。  

やることなかったら

Amazon CAPTCHA

ここら辺もきになる・・