20210417のSwiftに関する記事は6件です。

【Swift】RxSwift勉強してみたPart8

はじめに 前回 今回もRxSwiftを学習していく中での備忘録です。 学んだこと イベントを流す 単数 justでInt->Observable<Int>になっている。 1というイベントを流している。 let _ = Observable.just(1) .subscribe(onNext: { event in print(event) }) .disposed(by: disposeBag) // 1 複数 fromを使って複数のイベントを流してObservableを生成 let _ = Observable.from([1, 2, 3]) .subscribe(onNext: { event in print(event) }) .disposed(by: disposeBag) // 1 // 2 // 3 ofを使って引数で渡した要素が順に通知されるObservableを生成 let _ = Observable.of(1, 2, 3) .subscribe(onNext: { event in print(event) }) .disposed(by: disposeBag) // 1 // 2 // 3 ofとfromの違いは渡す引数がfromは配列でofは一つ一つ渡す。of([1, 2, 3])のようにすると[1, 2, 3]が出力される。 任意の処理を行うObservableの生成 訳すと、Create演算子を用いてObservableを最初から生成できます。 let _ = Observable<Int>.create { observer in observer.onNext(1) observer.onNext(2) observer.onCompleted() observer.onNext(3) return Disposables.create() } .subscribe(onNext: { print($0) }, onError: { print($0) }, onCompleted: { print("onCompleted") }, onDisposed: { print("onDisposed") } ) .disposed(by: disposeBag) // 1 // 2 // onCompleted // onDisposed Subject PublishSubject サブスクライブした後にObservableによって発行されたものをオブザーバーに発行する。 let subject = PublishSubject<String>() subject.onNext("1") subject .subscribe( onNext: { string in print(string) }, onError: { error in print(error) }, onCompleted: { print("completed") }, onDisposed: { print("disposed") }) .disposed(by: disposeBag) subject.onNext("2") subject.onNext("3") subject.onCompleted() subject.onNext("4") // 2 // 3 // completed // disposed BehaviorSubject サブスクライブした時点でオブザーバブルが最後に発行したものまたは、初期値を発行することから始まり、後に発行されたものを発行し続ける。 let subject = BehaviorSubject(value: "1") subject.onNext("2") subject .subscribe { print($0) } onError: { error in print("error") } onCompleted: { print("completed") } onDisposed: { print("disposed") } .disposed(by: disposeBag) subject.onNext("3") subject.onNext("4") subject.onCompleted() subject.onNext("5") // 2 // 3 // 4 // completed // disposed ReplaySubject 指定したbufferSizeだけの過去のeventを受け取れるSubject。 let subject = ReplaySubject<String>.create(bufferSize: 2) subject.onNext("1") subject.onNext("2") subject.subscribe(onNext: { print("subscribe1 - " + $0) }) .disposed(by: disposeBag) subject.onNext("3") subject.onNext("4") subject.onNext("5") subject.subscribe(onNext: { print("subscribe2 - " + $0) }) .disposed(by: disposeBag) subject.onNext("6") // subscribe1 - 1 // subscribe1 - 2 // subscribe1 - 3 // subscribe1 - 4 // subscribe1 - 5 // subscribe2 - 4 // subscribe2 - 5 // subscribe1 - 6 // subscribe2 - 6 BehaviorRelay 参考 nextイベントだけ流せる。nextイベントをながすにはacceptを使う。 let relay = BehaviorRelay(value: "1") relay.accept("2") relay.subscribe(onNext: { print($0) }) .disposed(by: disposeBag) relay.accept("3") relay.accept("4") // 2 // 3 // 4 let relay = BehaviorRelay(value: ["Item 1"]) var value = relay.value value.append("Item 2") value.append("Item 3") relay.accept(value) relay.subscribe(onNext: { print($0) }) .disposed(by: disposeBag) // ["Item 1", "Item 2", "Item 3"] PublishRelay BehaviorRelayとPublishRelayの違い BehaviorRelay PublishRelay next ○ ○ error × × complete × × 初期値 ○ × value ○ × 現在地を流す ○ × おわりに おわりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIKit+Metal でちょっとリッチなアニメーションをする背景を作ってみる

 UIKitベースのアプリで、ちょっと変わったアニメーションを背景にしたアプリをたまに見かけます。個人的に使うMVNOのアプリではこんな感じのアニメーションが使われています。  UIKitのアニメーション機能だけで実装するのは大変そうです。  この記事では、シェーダーでUIKitのアニメーション機能だけでは表現が難しいアニメーションを実装する方法を紹介します。 (ちなみに、某MVNOのアプリがシェーダーで実装してるのかどうかはわかりません)。 次のようなサンプルプログラムをGithubに置いています。 「プールの底のようなアニメーション」はこちらの記事『【UnityShader】Unityで水面の模様を描画するシェーダ・セルラーノイズ』を参考にさせてもらいました(ほぼそのままです。。) 「回路っぽいアニメーション」のシェーダーの元ネタはこちらです。とてもすばらしいアニメーションなのでソースを含めたいところですが作者の許可が必要そうなので外しています。 Shader Artの世界  シェーダー(GLSL)で美しいアニメーションを作る天才さんたちが数多くの作品を公開する世界があります。例えば、次のサイトに行くと美麗なアニメーションを見ることができます。 Shadertoy GLSL Sandbox  こんなアニメーションを作り出すのは大変だと思いますが、今回作成したサンプルもようなちょっと画像を歪めるアニメーションをさせるくらいなら、難しくありません。 MSL(Metal Shading Language)  サンプルはAppleのMSLというシェーダー言語で記述しています。C++をベースにした言語で、独特な修飾子が使われるので戸惑う部分もありますが、この記事では「そんな部分もあるのね。」と、一旦脇に置いて、描画ロジックに着目します。  ちなみに、MSLはGLSLとそっくりなので、こちらの記事『Metalシェーダことはじめ - WebGL/GLSLの豊富なサンプルを参考にMSLを書く』で紹介されているように、主に型名を変換するくらいでGLSLから移植できます。 シェーダーで星を回転させてみる それでは、シェーダーアニメーションの手始めとして星の回転アニメーションを実装します。 まず、円を書いてみます。 サンプルプログラムの shader.metalファイルにある circleShader() に次のように記述します。 shader.metal fragment half4 circleShader(ColorInOut in [[ stage_in ]]) { float2 uv = in.texCoord; // 縦方向でビューのアスペクト比を調整 uv.y *= in.aspectRatio; // 左上(0,0)からの距離を取得 float r = length(uv); // step()関数を使って、rが0.1以下なら 1.0、大きければ 0.0 に二値化 half color = step(r, 0.1); // RGBA の順番。Redだけ色を指定する return half4(color, 0.0, 0.0, 1.0); } 左上に1/4の赤い円が表示されました。 in.texCoord にはこれから描画する点の座標が格納されています。 シェーダーは描画する点ごとに呼び出されます。 Metalは左上が(0, 0)で右下が(1, 1)です。 in.aspectRatioで、これから描画する点のy座標を変更しています。これはシェーダーで描画するサイズがViewのサイズに関係なく0.0~1.0の範囲(座標系)なので、Viewのアスペクト比を使って描画が潰れないように調整するためのものです。 円は中心からの距離が同じ位置が境界となるので、length(uv) でこれから描画する点と(0, 0)との距離を計算しています。 step(r, 0.1)で、半径が0.1以下の場合に color に 1.0 を設定します。 half4(color, 0.0, 0.0, 1.0) で描画する点の色を作ります。RGBAの順番なので、ここでは赤だけ設定しています。half というのは見慣れない型ですが、これは16bitの浮動小数点型で、色を扱うには十分な精度があることからAppleのサンプルでもよく使われています。 補足:length() や step() はMSL組み込みの関数です。MSLではないですがこちらの記事『GLSLについてのメモ』が参考になります。 次に、円をビューの中心に表示させます。 shader.metal fragment half4 circleShader(ColorInOut in [[ stage_in ]]) { float2 uv = in.texCoord; uv -= 0.5; // この1行を追加 円が中心に表示されました。 左上が(0, 0)だったので、(0.5, 0.5)の位置が原点になるように、uv内のx, yともに0.5引きます。一瞬、引くんじゃなくて足すんじゃない?と思うかもしれません。私もそう思いましたが、Metalが(0.5, 0.5)の座標を描画しようとした時に、「そこは(0.0, 0.0)として計算したい!」ので、0.5を引きます。 次にこの円を星形にしてみます。 shader.metal fragment half4 circleShader(ColorInOut in [[ stage_in ]]) { float2 uv = in.texCoord; uv -= 0.5; // 縦方向でビューのアスペクト比を調整 uv.y *= in.aspectRatio; // 左上(0,0)からの距離を取得 float r = length(uv); // ここから ----------------------- // 中心から描画する点の角度を求める float theta = atan2(uv.y, uv.x); // sin関数で1周で5つの山谷を作ります float wave = sin(5 * theta); // 中心からの距離に、上記で作った波の値を小さめにして(0.02とかかけて)足しこみます。 // これで中心からの距離が波の高さ分変わるので、星っぽくなります。 r += wave * 0.02; // ----------------------- ここまで追加 // step()関数を使って、rが0.1以下なら 1.0、大きければ 0.0 に二値化 half color = step(r, 0.1); // RGBA の順番。Redだけ色を指定する return half4(color, 0.0, 0.0, 1.0); } 星形になりました。 中心からの距離を変化させることで星形にします。 変化は正弦波を使います(山谷がつくれれば正弦波でなくても良いです)。 正弦波に与える角度を atan2(uv.y, uv.x) で計算します。描画する点が中心位置からどの角度にあるのかを求めています。 次に星を回転アニメーションさせてみます。 追加・修正するのは次の2箇所だけです。 shader.metal float time = in.u_time / 10; // 追加 float wave = sin(5 * theta + time); // time を足しこむ 星が回転しました(動画の上から2番目の部分)。 正弦波に与える角度に連続して変化する値(in.u_time)を追加しています。これで波が起きるのでアニメーションすることになります。 in.u_timeはフレームが描画される(iPhoneの場合、60fpsで描画)ごとに1増える値です。10で割って回転速度を調整します。 冒頭の「たぷたぷしたアニメーション」はこの星のアニメーションを組み合わせて実現しました。 ShaderArtView.swift サンプルプログラムでは、面倒なMetal部分のセットアップを書かずに、作成したシェーダー名を設定するだけでUIViewと同様に扱える ShaderArtView を用意しています。 こんな感じで使えます。 ViewController.swift let artView = ShaderArtView() artView.setupView(shaderName: "circleShader") view.addSubview(artView) MTKView を継承しているので、isPaused でアニメーションを止めたり、preferredFramesPerSecond を設定することでフレームレートの変更が可能です。 画像を波うたせてみる 次に任意の画像をシェーダーに与えて、アニメーションをさせてみます。 冒頭のGIFアニメーションにある「波打つアニメーション」は画像を与えて、それに波打つ加工をしています。 shader.metal fragment half4 waveShader(ColorInOut in [[ stage_in ]], texture2d<half> texture [[ texture(0) ]]) { // 1秒に1すすむ値にする float time = in.u_time / 60; // 中心が(0, 0)になるように座標変換 float2 uv = in.texCoord - 0.5; // 縦方向でビューのアスペクト比を調整 uv.y *= in.aspectRatio; // 中心から描画する点の角度を求める float theta = atan2(uv.y, uv.x); // 中心から描画点までの距離 float r = length(uv); // 中心からの距離に正弦波で算出した値を加える(背景画像の取得位置を変更する) r += sin(-r * 20 + time * 3) * 0.05; // 中心から上記で計算した距離の色を取得 float2 distR = float2(r * cos(theta), r * sin(theta)) + 0.5; half4 colorSample = texture.sample(s, distR); return colorSample; } 前半は星の描画と同様ですが、後半の次の部分が異なります。 shader.metal // 中心から上記で計算した距離の色を取得 float2 distR = float2(r * cos(theta), r * sin(theta)) + 0.5; half4 colorSample = texture.sample(s, distR); やっていることは、 与えられた画像から色を取得する。 色を取得する位置は、これから描画しようとしている位置の座標ではなく、そこからズレた位置の座標から取得する。これにより画像を歪ませる。 です。 float2(r * cos(theta), r * sin(theta)) + 0.5 の部分が、中心からの距離と角度を使って、色を取得する位置を計算している部分です。r は、sin(-r * 20 + time * 3) * 0.05 で時間により増減する値になっているので、その分の距離だけズレた位置になります。 texture.sample(s, distR) は画像から色を取得する関数です。上記で計算したズレた位置にある色を取得します。 ちなみに、この画像はどこで受け取っているかと言うと、この部分です。 fragment half4 waveShader(ColorInOut in [[ stage_in ]], texture2d<half> texture [[ texture(0) ]]) { この [[ stage_in ]] や [[ texture(0) ]] 部分が??となると思います。[[ stage_in ]] は「描画する点の位置や時刻の情報が入っている」という目印で、[[ texture(0) ]] が「CPU側とGPU側で画像の受け渡しに使う」という目印で、複数の画像(テクスチャ)をやりとりできる中で「0番目の画像」という意味です。 このあたり(Metal仕様周り)の詳細はわかりにくいので、興味のある方はMetalの入門書籍を1冊読むのが良いと思います。 swift側から画像を与えているのはこの部分です。 ViewController.swift artView3.setupView(shaderName: "waveShader", imageName: "sampleImage") Assetsの画像名を指定することでシェーダー側に画像を渡しています。 最後に  シェーダーの学習は、こちらのサイトwebgl developer orgがわかりやすいです。WebGLでの解説ですがMSLに読み替えることは容易ですし、シェーダー意外のテクニックも豊富に丁寧に説明があり参考になります(自分もあんまり理解できていませんが)。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[SwiftUI]functionを勉強し直してみた!

はじめに 今回はとある課題でfunctionを使用する上でつまづく所が多かった為、この機会に学び直す事にしたのでまとめてみました! 環境 ・ /macOS Big Sur 11.3 ・ SwiftUI : 2.0 ・ Xcode : 12.4 functionの基本的な使用 Swiftで関数を使用する場合主に”関数名”、”仮引数”、”型”の指定をして使用します。 func 関数名 (仮引数: 型) -> (型) { コード..... } とこんな感じがオーソドックスかと。 また関数名、仮引数名ともローワーキャメルケースで書くことが推奨されています。 (キャメルケースについてはコチラをご覧ください。) 仮数名や型はオプションなので省略することも可能です。つまり最小の実装は func 関数名() { コード..... } となります。 では早速SwiftUIで使用してみましょう。 エラーが出ました!これはSwiftUIではViewをコードで生成させる為、Viewを記載する箇所がありますが、その中でfunncは使用しないで下さいね、と言うエラーです。 なのでこのようにViewの外に書いてあげればOK! 続いてfunctionを使用してみましょう。 @State var showHogeText = false func hogehoge () { self.showHogeText.toggle() } Button(action: { hogehoge() }) { Text("hogehogeボタン") } Text(showHogeText ? "ボタンを押した後" : "ボタンを押す前") .foregroundColor(showHogeText ? .red : .black) } } 上記のコードを実行するとGifのようなViewを生成することができます。 値を返すreturn 上記のように値を返さない関数は仮引数や型を省略するか -> ()とするか、-> voidとします。 ここでは値を返す事も試してみます。 func num (n: Int) -> Int { total += n return Int(total) } この様値を返す時はreturnを使用します。 そして上記の関数の使用方法は struct funcTtaining: View { @State var total = 0 var body: some View { VStack{ Button(action: { total = num(n: 1) }) { Text("ボタン") } Text("\(String(total))") } } } で使用できます。 ここではボタンを押すとnum関数の仮引数nに1が指定されているのでその1が関数内でtotalに足されて、足されたtotalがreturnで返され、button内のactionでtotalに渡される流れとなります。 まとめ functionの基本的な使い方を整理してみました。 この記事は引き続きfunctionに関して気づいた点があれば随時追記していこうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

高さ可変のinputViewを表示する(Swift)

はじめに 以前書いた下記記事でキーボードの高さもいい感じになってると思っていたのですがそうでもなかった模様 inputAccessoryView を表示するとわかるのですがカスタムの inputView が表示領域を飛び出していた。 これをいい感じに表示したい。ついでに高さを可変にしてみます。 いろいろチャレンジ CustomInputView っていうのを xib で作成して使用しています。 どうにか高さを設定してやればいいと思い色々試してみました。 オートレイアウト 下記のように高さの制約をつけてみましたが高さ 500 にはなりませんでした(通常のキーボードと同じ高さになりました)。 let v = CustomInputView() v.translatesAutoresizingMaskIntoConstraints = false v.heightAnchor.constraint(equalToConstant: 500).isActive = true textField.inputView = v 詳しくわからないですが inputView に制約を追加してもまだ add されてないから制約が効かないとかなんでしょうか frame設定 UIView の frame に高さを設定してみるとそれっぽい表示になりました let v = CustomInputView() var frame = v.frame frame.size.height = 500 v.frame = frame textField.inputView = v この方法でいけそう 高さを可変にする 上記のように frame 設定で高さ調整ができました。ここからは需要があるかわかりませんが高さを可変にすることに挑戦します。 方法としては xib で Auto Layout を設定して、frame.height に systemLayoutSizeFitting で計算した高さを設定してやればいけるはずです。 完成形(とくに意味はないですがチェックボックスを並べてみました) ソース protocol CustomKeyboardViewDelegate: AnyObject { func customKeyboardView(_ customKeyboardView: CustomKeyboardView, didToggleButton checked: CustomKeyboardView.Checked) } final class CustomKeyboardView: UIView { struct Hidden { let isFirst: Bool let isSecond: Bool let isThird: Bool let isFourth: Bool let isFifth: Bool let isSixth: Bool } struct Checked { let isFirst: Bool let isSecond: Bool let isThird: Bool let isFourth: Bool let isFifth: Bool let isSixth: Bool } @IBOutlet weak var firstButton: UIButton! @IBOutlet weak var secondButton: UIButton! @IBOutlet weak var thirdButton: UIButton! @IBOutlet weak var fourthButton: UIButton! @IBOutlet weak var fifthButton: UIButton! @IBOutlet weak var sixthButton: UIButton! weak var delegate: CustomKeyboardViewDelegate? func setupDefaultValue(hidden: Hidden, checked: Checked) { firstButton.isHidden = hidden.isFirst secondButton.isHidden = hidden.isSecond thirdButton.isHidden = hidden.isThird fourthButton.isHidden = hidden.isFourth fifthButton.isHidden = hidden.isFifth sixthButton.isHidden = hidden.isSixth firstButton.isSelected = checked.isFirst secondButton.isSelected = checked.isSecond thirdButton.isSelected = checked.isThird fourthButton.isSelected = checked.isFourth fifthButton.isSelected = checked.isFifth sixthButton.isSelected = checked.isSixth } override init(frame: CGRect) { super.init(frame: frame) loadNib() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! loadNib() } private func loadNib() { let view = Bundle.main.loadNibNamed("CustomKeyboardView", owner: self, options: nil)?.first as! UIView view.frame = bounds view.translatesAutoresizingMaskIntoConstraints = true view.autoresizingMask = [.flexibleWidth, .flexibleHeight] addSubview(view) } @IBAction private func toggleButton(_ sender: UIButton) { sender.isSelected.toggle() delegate?.customKeyboardView(self, didToggleButton: .init( isFirst: firstButton.isSelected, isSecond: secondButton.isSelected, isThird: thirdButton.isSelected, isFourth: fourthButton.isSelected, isFifth: fifthButton.isSelected, isSixth: sixthButton.isSelected )) } } class FirstViewController: UIViewController { @IBOutlet private weak var textField: UITextField! private var isFirstHidden: Bool = false private var isSecondHidden: Bool = false private var isThirdHidden: Bool = false private var isFourthHidden: Bool = false private var isFifthHidden: Bool = false private var isSixthHidden: Bool = false private var isFirstChecked: Bool = false private var isSecondChecked: Bool = false private var isThirdChecked: Bool = false private var isFourthChecked: Bool = false private var isFifthChecked: Bool = false private var isSixthChecked: Bool = false override func viewDidLoad() { super.viewDidLoad() let toolbar = UIToolbar() toolbar.items = [ UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil), UIBarButtonItem.init(title: "閉じる", style: .done, target: self, action: #selector(close)) ] toolbar.sizeToFit() textField.inputAccessoryView = toolbar } @objc private func close() { textField.resignFirstResponder() } @IBAction private func pattern1(_ sender: Any) { textField.resignFirstResponder() isFirstHidden = false isSecondHidden = false isThirdHidden = false isFourthHidden = false isFifthHidden = false isSixthHidden = false setupInputView() textField.becomeFirstResponder() } @IBAction private func pattern2(_ sender: Any) { textField.resignFirstResponder() isFirstHidden = false isSecondHidden = true isThirdHidden = false isFourthHidden = true isFifthHidden = false isSixthHidden = true setupInputView() textField.becomeFirstResponder() } @IBAction private func pattern3(_ sender: Any) { textField.resignFirstResponder() isFirstHidden = false isSecondHidden = false isThirdHidden = true isFourthHidden = true isFifthHidden = true isSixthHidden = true setupInputView() textField.becomeFirstResponder() } private func setupInputView() { let view = CustomKeyboardView() view.delegate = self let hidden: CustomKeyboardView.Hidden = .init(isFirst: isFirstHidden, isSecond: isSecondHidden, isThird: isThirdHidden, isFourth: isFourthHidden, isFifth: isFifthHidden, isSixth: isSixthHidden) let checked: CustomKeyboardView.Checked = .init(isFirst: isFirstChecked, isSecond: isSecondChecked, isThird: isThirdChecked, isFourth: isFourthChecked, isFifth: isFifthChecked, isSixth: isSixthChecked) view.setupDefaultValue(hidden: hidden, checked: checked) var frame = view.frame frame.size.height = view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height view.frame = frame textField.inputView = view } } extension FirstViewController: CustomKeyboardViewDelegate { func customKeyboardView(_ customKeyboardView: CustomKeyboardView, didToggleButton checked: CustomKeyboardView.Checked) { isFirstChecked = checked.isFirst isSecondChecked = checked.isSecond isThirdChecked = checked.isThird isFourthChecked = checked.isFourth isFifthChecked = checked.isFifth isSixthChecked = checked.isSixth } } 高さも可変でチェック状態も引継げる inputView ができました おわりに 使い道があるかは知りませんが無事高さ可変の inputView ができました 他にも下記のような方法があるみたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

modal view に navigation bar を表示する方法

はじめに segueで遷移したmodal viewにnavigation barを表示させる方法をメモしました. 方法 遷移前のviewをA, 遷移後のmodal viewをBとします. Bでnavigation barを表示したい場合, Aのroot view controllerがnavigation controllerだとしても, 新たにAとBの間にnavigation controllerを挟んであげる必要があります. segueの関係(ex) A -> navigation controller : Present Modally navigation controller -> B : Relationship "root view controller" まとめ 他にもコードベースのやり方があるようですが,今回は最も簡単なstoryboardを用いたやり方を行いました. 参考 Swift: Force show Navigation Bar in Modal 【Swift】モーダル遷移でNavigationBarを表示する [初心者向け]swiftにおける値渡しの方法の一部をご紹介
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ドラッグしてゴミ箱に捨てるやつ

画面端のゴミ箱までドラッグすると、ゴミ箱が開いてコンテンツを消せるやーつです。 // pan ジェスチャをつける @IBAction func pan(_ sender: UIPanGestureRecognizer) { panObject(recognizer: sender) } func panObject(recognizer:UIPanGestureRecognizer) { switch recognizer.state { case .changed: // 移動中 let location = recognizer.location(in: view) // 触ってる場所を取得 trashLabel.center = location if trashView.frame.contains(location) { // もしゴミ箱と重なっていたら // ゴミ箱を赤くする trashView.image = UIImage(systemName: "trash.circle.fill") trashView.tintColor = .red } else { // 重なっていなかったらゴミ箱を元に戻す trashView.image = UIImage(systemName: "trash.circle") trashView.tintColor = .systemBlue } default: // 指を離した時 let location = recognizer.location(in: view) // 触ってる場所を取得 if trashView.frame.contains(location) { // もしゴミ箱と重なっていたら trashLabel.removeFromSuperview() // ラベルを消す } } } ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLを使ったアプリを作っています。 機械学習関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む