20210507のSwiftに関する記事は5件です。

コンピューテッドプロパティ swift

■計算されたプロパティ ■基本 コンピューテッドプロパティは、プロパティ自身では値を保存せず、すでに存在するストアドプロパティ などから計算して値を返すプロパティです。 関数に似ている。 ■書き方 var 変数名:型名 { get { return 文によって値を返す処理      } set { 値を更新する処理 プロパティに代入された値には定数newValueとしてアクセスできる      } } ■getとset コンピューテッドプロパティにはgetとsetというものがある。 ■get ほかのストアドプロパティなどから値を取得して、コンピューテッドプロパティの値として返す処理です。 と書いてる ■細かく言うと コンピューテッドプロパティにアクセスした時にgetの中で他のストアドプロパティを使って計算文を書いてそれの計算結果をreturnで返す。 getするぜ!! ■set プロパティに代入された値を使用して他のストアドプロパティを更新する。 ここ重要:他のストアドプロパティを更新する!! ■細かく言うと コンピュテッドプロパティに新たな値が代入されました。 その新たな値はsetの中にsetの特殊引数(newValue)としてされます。 newValueの型はコンピュテッドプロパティの型と同じものです。 setするぜ! ■とりあえずコードを書いてみよう! //他のストアドプロパティ var price = 100 var taxRate = 1.08 //コンピューテッドプロパティ var taxPrice:Double { get{ return Double(price) * taxRate} set{ price = Int(newValue * 3)} } //ただ単にコンピュテッドプロパティにアクセス print(taxPrice) //結果: 108.0 //コンピュテッドプロパティに値を代入してるからsetが呼ばれ値は引数のnewValueになる。 //他のストアドプロパティpriceが変更されている。 taxPrice = 200 //priceの値が変更されてから新たな値がreturnされている。 print(taxPrice) //結果: 648.0 ■縮めた書き方 var price = 100 var taxRate = 1.08 //getのみでも書ける。 var taxPrice:Double { get{ return Double(price) * taxRate} } //getとreturnを省いてもgetとなり同じ動きになる。 var taxPrice:Double { { Double(price) * taxRate} } 注意:setのみを書くことはできないsetを使うときは必ずgetが必要 ■カプセル化(変更しないように) コンピューテッドプロパティは値をカプセル化もできる。 getはただ単に中身の値を返すものなのでgetだけの場合使用上中身を絶対変更できない。 var loginkey:String {"key"} //変更しようとするとエラー loginkey = "aaa" //エラー:Cannot assign to value: 'aaa' is a get-only property //getしかないでそこ代入できへんってマジでって言ってそう... とりあえずアウトプットです。 何かあればまた更新します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】よくみかける立体的で丸いボタンをコードで作ってみた(解説付き)

よく色々なアプリで見かける立体的で丸いボタンを作ってみました。 こんなやつです。 環境 Xcode12.4 Swift5 コード全体 先に上記で作成したボタンのコードを載せておきます。 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // ボタン作成 let customButton = UIButton() // スクリーンサイズ取得 let screenWidth:CGFloat = self.view.frame.width let screenHeight:CGFloat = self.view.frame.height // ボタンサイズ指定 let buttonWidth: CGFloat = 100 let buttonHeight: CGFloat = 100 // ボタンに反映(中央に位置調整) customButton.frame = CGRect( x: screenWidth/2-buttonWidth/2, y: screenHeight/2-buttonHeight/2, width: buttonWidth, height: buttonHeight) // ボタンの背景色 customButton.backgroundColor = UIColor.white // アプリ標準のシステム画像を設定 customButton.setImage(UIImage(systemName: "location.fill"), for: .normal) // 画像の色をdarkGrayに変更 customButton.tintColor = .darkGray // 縦幅・横幅いっぱいに画像を表示 customButton.contentHorizontalAlignment = .fill customButton.contentVerticalAlignment = .fill // ボタンを丸くする(サイズ/2で設定) customButton.layer.cornerRadius = 50 // 画像を縮小する(マージンのようなもの) customButton.imageEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30) // 影の色 customButton.layer.shadowColor = UIColor.black.cgColor // 影のぼかし? customButton.layer.shadowRadius = 3 // 影の位置 customButton.layer.shadowOffset = CGSize(width: 1.5, height: 1.5) // 影の濃さ customButton.layer.shadowOpacity = 0.3 // ボタンをViewに追加 view.addSubview(customButton) } } 解説 ボタン作成 まずはボタンを作って、画像を設定してみます。 ボタンの背景色は白だと見えないので一時的にlightGrayに設定。 // ボタン作成 let customButton = UIButton() // スクリーンサイズ取得 let screenWidth:CGFloat = self.view.frame.width let screenHeight:CGFloat = self.view.frame.height // ボタンサイズ指定 let buttonWidth: CGFloat = 100 let buttonHeight: CGFloat = 100 // ボタンに反映(中央に位置調整) customButton.frame = CGRect( x: screenWidth/2-buttonWidth/2, y: screenHeight/2-buttonHeight/2, width: buttonWidth, height: buttonHeight) // ボタンの背景色を設定(わかりやすいように一時的にlightGrayを設定) customButton.backgroundColor = UIColor.lightGray // アプリ標準のシステム画像を設定 customButton.setImage(UIImage(systemName: "location.fill"), for: .normal) // 画像の色をdarkGrayに変更 customButton.tintColor = .darkGray // ボタンをViewに追加 view.addSubview(customButton) 見た目はこんな感じです。 画像を幅いっぱいに広げる 画像を大きくしたいのでAligmentをfillにして幅いっぱいにする // 縦幅・横幅いっぱいに画像を表示 customButton.contentHorizontalAlignment = .fill customButton.contentVerticalAlignment = .fill ちょっと大きすぎますが、一旦このままで。 ボタンを丸くする いよいよボタンを丸くします。 値はボタンのサイズ/2で設定すると丸くなります。 // ボタンを丸くする(サイズ/2で設定) customButton.layer.cornerRadius = 50 画像がはみ出ていますが、丸くなりました。 先程Aligmentの設定をしましたが、さすがにcornerRadiusを考慮して自動的に小さくはならないですね。 画像の大きさを調整する 画像を小さくするために、UIEdgeInsetsを追加します。 ボタンと画像の間にマージンを入れているようなもので、値が大きくなる程間隔が大きくなり、画像が小さくなります。 // 画像を縮小する(マージンのようなもの) customButton.imageEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30) 画像の大きさが良い感じなりました。 ボタンに影をつける 最後にボタンを立体的に見せるために、影をつけます。 ボタンの背景色は白くします。 // ボタンの背景色を白に戻す customButton.backgroundColor = UIColor.white // 影の色 customButton.layer.shadowColor = UIColor.black.cgColor // 影のぼかし? customButton.layer.shadowRadius = 3 // 影の位置 customButton.layer.shadowOffset = CGSize(width: 1.5, height: 1.5) // 影の濃さ customButton.layer.shadowOpacity = 0.3 立体的で丸いボタンのできあがりです。 補足:影のプロパティについて 今回設定したshadowのプロパティが何なのかそれぞれみていきます。 影の色(shadowColor)、影の濃さ(shadowOpacity) 影の色と濃さはそのまんまです。 例えば、赤色でもっと濃くしてみます。 // 影の色 customButton.layer.shadowColor = UIColor.red.cgColor // 影の濃さ customButton.layer.shadowOpacity = 0.9 こんな感じです。 影のぼかし?(shadowRadius) 続いて影のぼかし?というのでしょうか。 値を大きすると影が広がります。 // 影のぼかし? customButton.layer.shadowRadius = 50 影が大分広がりました。 影の位置(shadowOffset) 最後に影の位置についてです。 値を大きくすると影が離れていきます。 // 影の位置 customButton.layer.shadowOffset = CGSize(width: 30, height: 30) 影がボタンから大きく離れました. おわり 最後まで読んで頂きありがとうございます。 不備や補足などありましたら教えて頂けますと助かります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xcodeで新規プロジェクトを立ち上げた際に最初に呼ばれる画面を変更する方法

Xcodeで新規プロジェクトを立ち上げた際に、デフォルトで用意されている画面ではなく新規に作成した画面を利用したい。 方法を探ったのですが、ちょっと躓いたので備忘録として記載します。 環境 Xcode12.4 Swift5 新規プロジェクトのファイル構成 新規のプロジェクトを立ち上げると下記のファイルが作成されます。(初期設定や環境で若干異なります。) 最初の画面に関与しているのは、赤線部分のファイル達。 表示したい画面を作成 表示したい画面を作成。 First.storyboardには3つの設定 ViewControllerとLabelを配置 ※Labelは表示されたことを確認するため ViewControllerのIs Initial View Controllerをチェック Custom ClassでFirstViewControllerクラスを選択して紐付け FirstViewController.swiftでは画面が表示された際にLabelにテキストを代入 FirstViewController.swift import UIKit class FirstViewController: UIViewController { @IBOutlet weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() label.text = "First!!" } } 画面の変更 前置きが長くなってきましたが、肝心の画面変更をします。 info.plistを開きます。 設定項目の内、以下3つの項目をFirstに変更します。 Storyboard Name Launch screen interface file base name Main storyboard file base name 一つ目のStoryboard Nameがネストしているので見つけるのに微妙に時間がかかってしまいました・・・ 結果 Buildしたら表示されました! Simulatorのスクリーンショットを撮る方法 おわり もし不備や補足があれば教えて頂けますと助かります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swiftコンソールにprint()でテーブルを書きたい

はじめに 特に実用性はない。深夜テンションで思いついて気が付いた頃には実行ボタン押してた。 実行結果 使い方 せっかくなので使ってみてね あとで下に書くファイル一つ分をコピペするだけで使えるよ? AsciiTable.swift 実行コードもとっても簡単! Sample.swift // サンプル用のデータ let sample = Ascii.Table.Sample.jsonData // 初期化 let table = Ascii.Table(json: sample) // コンソール 出力 for text in table.texts { print(text) } // JSONに変換して取得 let json = table.jsonData コピペ用ファイル 丸々コピーして適当なファイルに貼り付けてね? Ascii.Tableクラスが本体だよ? String の配列とか、JSON とか色んな形式から初期化できるから試してみよう! AsciiTable.swift // // +------------------+ // | AsciiTable.swift | // +------------------+ // import Foundation /* +------------+ | How to Use | +------------+ `` let sample = Ascii.Table.Sample.jsonData # 1. init let table = Ascii.Table(json: sample) # 2. print for text in table.texts { print(text) } # 3. Serialize let json = table.jsonData `` */ extension Ascii.Table { public enum Sample { /// this is a sample public static let rawTexts: (columns: [String], rows: [[String]]) = { return ( columns: ["1", "2", "3"], rows: [ ["4", "5", "6"], ["7", "8", "9"] ] ) }() /// this is a sample public static let jsonData: Data = """ { "columns": [ {"key": "keyA", "width": 8}, {"key": "keyB", "width": 10} ], "rows": [ {"cells":[ {"key": "keyA", "value": "valueA0"}, {"key": "keyB", "value": "valueB0"} ]}, {"cells": [ {"key": "keyA", "value": "valueA1"}, {"key": "keyB", "value": "valueB1"} ]} ] } """.data(using: .utf8)! /// this is a sample public static let rows: [Row] = [ Row(cells: [ Cell(key: "XXX", value: "aaa"), Cell(key: "YYY", value: "bbb") ]), Row(cells: [ Cell(key: "XXX", value: "ccc"), Cell(key: "YYY", value: "ddd") ]), ] } } fileprivate protocol AsciiTable { /// [ /// "+----------+------------+", /// "| keyA | keyB |", /// "+----------+------------+", /// "| valueA0 | valueB0 |", /// "+----------+------------+", /// "| valueA1 | valueB1 |", /// "+----------+------------+" /// ] /// texts for output var texts: [String] { get } /// init with Swift Type init(columns: [Ascii.Table.Column]?, rows: [Ascii.Table.Row]) /// init with String Array init(columns: [String], rows: [[String]]) } public enum Ascii { public struct Table { public struct Column { let key: String var width: Int? } public struct Row { let cells: [Cell] } public struct Cell { let key: String let value: String } public let columns: [Column] public let rows: [Row] } } extension Ascii.Table: AsciiTable { init(columns: [Ascii.Table.Column]? = nil, rows: [Ascii.Table.Row]) { self.columns = (columns ?? rows.first?.cells.map { Column(key: $0.key) }) ?? [] self.rows = rows } init(columns: [String], rows: [[String]]) { self.columns = columns.map {Column(key: $0)} self.rows = rows.map {(row) -> Row in Row(cells: row.enumerated().map {Cell(key: columns[$0.offset], value: $0.element)})} } var texts: [String] { let borderLines = [String](repeating: self.horizontalBorder, count: self.rows.count).map { [$0] } let rowLines = self.rows.map {self.stringRow(for: $0.cells)} let zipped = zip(borderLines, rowLines) let alts = [String](zipped.map { $0.0 + $0.1 }.joined()) return [self.horizontalBorder] + [self.stringColumns] + alts + [self.horizontalBorder] } } extension Ascii.Table: JSONSerializable {} extension Ascii.Table.Row: JSONSerializable {} extension Ascii.Table.Column: JSONSerializable {} extension Ascii.Table.Cell: JSONSerializable {} fileprivate extension Ascii.Table.Cell { func lineBrokenValue(by count: Int) -> [String] { return self.value.split(by: count).map {$0.filled(to: count)} } } fileprivate extension Ascii.Table { func cells(at line: Int) -> [Ascii.Table.Cell]? { return self.rows[at: line]?.cells } var horizontalBorder: String { return "+-" + self.columns.map {[String](repeating: "-", count: $0.width ?? $0.key.count).joined()}.joined(separator: "-+-") + "-+" } var stringColumns: String { return "| " + self.columns.map {$0.key.filled(to: $0.width ?? $0.key.count)}.joined(separator: " | ") + " |" } func stringRow(for cells: [Ascii.Table.Cell]) -> [String] { let lineBrokens = self.columns.map { c -> [String] in return cells.first(where: {$0.key == c.key})!.lineBrokenValue(by: c.width ?? c.key.count) } let cellHeight = lineBrokens.max(by: {return $0.count < $1.count})!.count let filledToHeight = lineBrokens.map {$0 + [String](repeating: "", count: cellHeight - $0.count)} return filledToHeight.transpose().map {"| " + $0.joined(separator: " | ") + " |"} } } protocol JSONSerializable: Codable { init(json: Data) var jsonData: Data { get } } extension JSONSerializable { init(json: Data) { self = try! JSONDecoder().decode(Self.self, from: json) } var jsonData: Data { return try! JSONEncoder().encode(self) } } fileprivate extension String { func split(by count: Int) -> [String] { let arrayValue = Array(self) if arrayValue.isEmpty { return [] } var parent: [[Element]] = [] for i in 1 ... arrayValue.count { if (i % count == 0) { parent.append(arrayValue[(i - count)...(i - 1)].map{$0}) } } if arrayValue.count % count != 0 { parent.append(arrayValue.suffix(arrayValue.count % count)) } return parent.map({String($0)}) } func filled(to count: Int, with pad: String = " ", isleftJustified: Bool = false) -> String { let main = String(self.prefix(count)) let pads = String(repeating: pad, count: count - main.count) return isleftJustified ? (main + pads) : (pads + main) } } fileprivate extension Array { subscript (at index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } extension Array where Element: RandomAccessCollection, Element.Index == Int { func transpose() -> [[Element.Element]] { return self.isEmpty ? [] : (0...(self.first!.endIndex - 1)).map { i -> [Element.Element] in self.map { $0[i] } } } } さいごに 実用性はないと書きましたが、実は色んなコーディング要素が詰まっているので何かの題材に使えそうかも?! せっかくだから他の機能も増やしてコマンドラインツールでも作ってみようかしら。 ちょうどテーブル表示なのでDB初期化用の init.sql と .json ファイルの相互変換とかが良いでしょうか。 ... ... ... はぁ、平日の夜中に何やってんだ俺。。。もう無理、マヂ寝ョ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Storyboard脱却への第一歩

今回はStoryboardを使わない開発について書いていきます。 SwiftUIを勉強したということもあり、全てコードでの開発をしてみたいと思いました。 なぜStoryboardを使わない開発をするのか 一番大きな理由は、チーム開発時のコードレビューがやりにくいと感じたからです。 GitHub上では、StoryboardはXML形式で表示されます。XMLを眺めているだけだとViewがどんな見た目をしているのか把握することが難しいです。他には、コンフリクト修正をやりやすくするという点があります。Storyboardの場合だと、コンフリクトしている両ブランチをcloneしてどちらの見た目を優先するのか考える必要がありますが、SwiftファイルでViewを書くとその手間が省けるという利点があります。 後は個人的な理由ですが、私が使っているmacだとスペックがあまり高くないので、Storyboardを開くまでに時間がかかってしまい、ストレスに感じていることも理由の一つです。 でも初心者はStoryboardを使った方がいいかも StoryboardはUIを直感的に配置でき、オートレイアウトもStoryboard上で完結させることができます。オートレイアウトは慣れが必要なのですが(私自身も試行錯誤しながらオートレイアウトつけます...)、Viewを配置することに関してはStoryboardは時間をかけずに簡単にできます。 入門書もStoryboardを使って紹介しているものが多いので、初心者はまずStoryboardを使ってiOSアプリ開発を体験した方が挫折せず取り組めるかと思います。 Main.storyboardを削除する さて、ここから本題です。 実際に、Storyboardを使わない開発の第一歩を踏み出します。 プロジェクト立ち上げ時にデフォルトでMain.storyboardあるので、このファイルを削除します。 「え? ホントに消すの?」と不安な人もいるかと思いますが、大丈夫です。思い切って消しましょう!! この時点でビルドすると、「Mainっていう名前のstroyboardが見つけることができないよ」といった旨のエラーがでます。  Info.plistのStoryboard Nameを削除 上の写真にもあるようにStoryboard Nameの項目を削除します。 この時点でビルドすると、成功しますが、真っ黒な画面になります。 xcodeprojのMain Interfaceを空にする TARGETS > General > Development InfoにあるMain Interfaceの項目を空白に設定します。 この時点でもビルドすると、成功しますが、真っ黒な画面になります。 SceneDelegate.swiftを編集 以下の通りにSceneDelegate.swiftを編集します。 SceneDelegate.swift func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: windowScene.coordinateSpace.bounds) window?.windowScene = windowScene window?.rootViewController = ViewController() window?.makeKeyAndVisible() } rootViewControllerに設定することで、今回はViewControllerが初期画面として表示されます。 ViewController.swiftを編集 分かりやすいように背景を青色に設定してみます。 ViewController.swift import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .blue } } ビルド 無事、表示できました!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む