20210505のSwiftに関する記事は7件です。

(Mac) Wi-Fiが切れたら自動で再接続するメニューバー常駐アプリケーション

背景 うちの賃貸マンションはNuro光が無料で使えるのですが、手持ちのMacと相性が悪いのか、突然接続が切れることが度々あります。1日に1回以上の頻度で発生するため、さすがに対策が必要でした。 単にこちらをShell scriptあるいはApple Scriptで実装してもよいのですが、稼働している様子がわかるようにしたく、メニューバーに常駐するタイプのアプリケーションとして実装することにしました。 方法 基本的には、こちらのチュートリアルの通りに進めてメニューバー常駐アプリを作りました。 Swiftからshellコマンドを実行する方法としてこちらを使いました。 完成コード AppDelegate.swift import Cocoa protocol CommandExecuting { func run(commandName: String, arguments: [String]) throws -> String } enum BashError: Error { case commandNotFound(name: String) } struct Bash: CommandExecuting { func run(commandName: String, arguments: [String] = []) throws -> String { return try run(resolve(commandName), with: arguments) } private func resolve(_ command: String) throws -> String { guard var bashCommand = try? run("/bin/bash" , with: ["-l", "-c", "which \(command)"]) else { throw BashError.commandNotFound(name: command) } bashCommand = bashCommand.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) return bashCommand } private func run(_ command: String, with arguments: [String] = []) throws -> String { let process = Process() process.launchPath = command process.arguments = arguments let outputPipe = Pipe() process.standardOutput = outputPipe process.launch() let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() let output = String(decoding: outputData, as: UTF8.self) return output } } @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var menu: NSMenu! //メニューバーに表示されるアプリケーションを作成 let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.variableLength) func applicationDidFinishLaunching(_ aNotification: Notification) { // メニューバーに表示されるアプリ。今回は文字列で設定 statusItem.title = "NURO" statusItem.highlightMode = true statusItem.menu = menu //アプリ終了ボタンを作成 let quitItem = NSMenuItem() quitItem.title = "Quit Application" quitItem.action = #selector(AppDelegate.quit(_:)) menu.addItem(quitItem) // WiFi再接続のループ開始 startLoop() } func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application } @objc func quit(_ sender: Any) { NSApplication.shared.terminate(self) } func startLoop() { Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block:{ (timer) in let bash: CommandExecuting = Bash() var result = "" do { try result = bash.run(commandName: "networksetup", arguments: ["-getinfo", "Wi-Fi"]) }catch { print("failed to networksetup -getinfo") return } if (!result.contains("Subnet mask")) { do { try result = bash.run(commandName: "networksetup", arguments: ["-setairportnetwork", "en1", "HG8045-D94E-bg", "cuayh5ks"] ) } catch { print("failed to networksetup -setairportnetwork") } } }) } } ポイント .entitlementsファイルで、App SandboxをNOにする必要がある。 動作の様子
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swift いろんなプロパティについて整理する

0.はじめに swiftにはプロパティといってもたくさんのプロパティがあるため、整理してみようと思います。 1.プロパティの分類 プロパティの分類には2通りの分け方があります。 プロパティが紐づく対象による分類 ・インスタンスプロパティ ・スタティックプロパティ ・クラスプロパティ 値を保持するかどうかによる分類 ・ストアドプロパティ ・コンピューテッドプロパティ このようにプロパティの分類には2通りの分け方があり、それぞれ3個、2個の分類があります。 2. プロパティが紐づく対象による分類 インスタンスプロパティ struct Animal { var name:String = "ライオン" } var lion = Animal() print(lion.name) // ライオン print(Animal.name) // エラー その名の通り、インスタンスに紐づくプロパティです。 インスタンスからはアクセスできますが、クラスからはアクセス出来ません。 スタティックプロパティ struct Animal { static var needOxygen: Bool = true } var lion = Animal() print(lion.needOxygen) // エラー print(Animal.needOxygen) // true クラスに紐づくプロパティです。 クラスからはアクセスできますが、インスタンスからはアクセスすることが出来ません。 クラスプロパティ class Animal { class var needOxygen: Bool { get { return true } } } var lion = Animal() print(lion.needOxygen) // エラー print(Animal.needOxygen) // true スタティックプロパティと同様クラスに紐づくメソッドでアクセスの制限も同じですが、次のような違いがあります。 オーバーライド ストアドプロパティの定義 コンピューテッドプロパティの定義 スタティックプロパティ できない できる できる クラスプロパティ できる できない できる どちらかを利用するか迷ったときは、オーバーライドしたいときはクラスプロパティ、したくないときはスタティックプロパティという感じでいいと思います。 また構造体ではクラスプロパティが利用できないので注意が必要です。構造体そのものに紐付けたいときはstaticをつけましょう。 3. 値を保持するかどうかによる分類 ストアドプロパティ struct Human { var name = "hoge" } var human = Human() human.name = "taro" print(human.name) その名の通り、値が保持される(stored)プロパティです。 またストアドプロパティには、値の変更を管理するプロパティオブザーバという機能があります。 struct Human { var name = "hoge" { willSet { print("\(self.name)が\(newValue)に変更されます。") self.name = newValue } didSet { print("Human.nameに\(self.name)がセットされました。") } } } var human = Human() human.name = "taro" // hogeがtaroに変更されます。 // Human.nameにtaroがセットされました。 print(human.name) // taro "hoge"に続くwillSet とdidSetがプロパティオブザーバの機能です。 willSetは値が代入される直前に呼ばれ、didSetは値が代入された直後に呼ばれます。 newValueには代入される値が格納されます。 コンピューテッドプロパティ struct Customer { var age: Int! var drink:String { get { if self.age >= 20 { return "お酒がおすすめです" } else { return "ソフトドリンクがおすすめです" } } } init?(age: Int) { guard age >= 0 && age < 120 else { return nil } self.age = age } } var customer = Customer(age: 20)! print(customer.drink) // お酒がおすすめです customer.age = 18 print(customer.drink) // ソフトドリンクがおすすめです その名の通り、値が計算されて(computed)出力されるプロパティです。 あくまで計算して値を返しているだけで、値を保持しているわけではないということを意識してください。 init?(失敗のあるイニシャライザ)とコンピューテッドプロパティに関しては下記のリンクで詳しく解説しているので、よければ御覧ください。 ・失敗のあるイニシャライザって? ・get setってなんのこと? 本記事では2つの分類方法と、5つのプロパティの分類について整理してきました。整理できると楽しいですね! 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVCについてまとめてみた

概要 MVCの理解がぼんやりと出来てきたような気がするので、 イメージしやすいようにまとめてみました。 MVCって何? IBOutletでボタンなどのUIパーツを接続したり、ボタンをタップした際の処理、 API通信などの様々な処理を全てViewControllerに書いてしまうと、ViewControllerに膨大な行数のコードが書かれることになってしまい、コードを追ったり修正したりすることなどが難しくなってしまいます。俗に言うFat ViewControllerです。 このような問題を解決するために、責務の切り分けという作業を行う必要があります。様々なアーキテクチャが存在します。今回はそんなアーキテクチャの中でMVCに焦点を当てて解説してみます。 現在よく使われているMVCパターンはcocoa MVCと呼ばれるものです。 MVCとは、 ModelのM, ViewのV, ControllerのCを合わせてMVCと言っています。 Modelの役割 Modelの役割としては、 ・データ構造の表現 ・WebAPIとのやり取り ・ローカルデータベースなどのデータの永続化 ・データの振る舞いに関するロジック が挙げられます。 Viewの役割 Viewの役割は、 ・UIの表示 ・Controllerからデータを受け取り、UIに反映させる ・ユーザーの操作を(IBAction,IBOutletなどによって)認知し、 です。つまり、アプリ上でボタンを押す、textFieldをタップして文字を入力するなどの操作を行うと、 Viewに通知されます。 実際にアプリ開発を行う際にはカスタムViewを作成し、Viewとする場合が多いです。 Controllerの役割 Controllerの役割は、 ・Modelからデータを受け取り、Viewに渡してデータを更新 ・ユーザーインタラクションをViewから受け取り、適切なアクションを行う ・ライフサイクル処理や画面遷移などの処理を行う です。cocoa MVCでは Viewcontroller が Controller となります。 参考になった説明 ・ModelとViewは、お互いのことを知らない ・Modelの役割は、viewとControllerがやらないこと全部と考えても良い ・カスタムViewを含むViewは、ユーザー操作をControllerに伝達し、Controllerの要求に応じて 情報を表示するのみの"アホな"存在であるべき まとめ 以上の説明を図にすると、以下のようになります。 ①ユーザーがView上のUIパーツを操作 ②その通知を元にControllerがModelを更新 ③ModelがControllerに通知 ④Modelから通知をControllerが受け取ったことで、ViewのUIを更新 といった流れです。 自分も学習したばかりなので、不明点や間違っている点があれば教えていただきたいです。 サンプルコード等は後日更新予定です。 最後まで読んでいただき、ありがとうございました! 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

StackViewの挙動を確認した

準備 色と大きさを指定できるUIViewを作成 class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } 表示するViewを作成。この中で先程のColorViewを使っている。 class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .gray stackView.axis = .horizontal stackView.alignment = .fill stackView.distribution = .fillProportionally addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } axis これは理解しやすい。積み上げる方向が水平方向か鉛直方向か指定できる。 UIKit.UIStackViewでの定義 extension NSLayoutConstraint { public enum Axis : Int { case horizontal = 0 case vertical = 1 } } alignment alignmentは名詞で一列、整列線といった意味。方向を定めた後にどこに並ぶのかを決める。 UIKit.UIStackViewでの定義 public enum Alignment : Int { /* Align the leading and trailing edges of vertically stacked items or the top and bottom edges of horizontally stacked items tightly to the container. */ case fill = 0 /* Align the leading edges of vertically stacked items or the top edges of horizontally stacked items tightly to the relevant edge of the container */ case leading = 1 public static var top: UIStackView.Alignment { get } case firstBaseline = 2 // Valid for horizontal axis only /* Center the items in a vertical stack horizontally or the items in a horizontal stack vertically */ case center = 3 /* Align the trailing edges of vertically stacked items or the bottom edges of horizontally stacked items tightly to the relevant edge of the container */ case trailing = 4 public static var bottom: UIStackView.Alignment { get } case lastBaseline = 5 // Valid for horizontal axis only } fill fillは高さが揃うようにaxisと垂直方向の辺の長さを広げる。 中身のViewが持っていた高さの制約よりも優先される。 leading/top axisがhorizontalのt時はtop、verticalの時はleadingが適切であるよう(Storyboardだと表示されなくなる)だが、どちらでも動く。中身のUIViewの上の辺(topAnchor)がx,y座標の小さい方の上の辺に揃う。 firstBaseLine StackViewにUILabelが含まれている時、その1行目に高さが揃う。 なお、firstBaseLineとlastBaseLineで用いている画像は他のものと異なっている。 import PlaygroundSupport import UIKit class ColorView: UIView { let size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let label = UILabel() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 20)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 40)) let yelloView = ColorView(color: .yellow, size: CGSize(width: 100, height: 60)) let stackView = UIStackView() override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white addSubViewsConstrains() setupStackView() setupLabel() } private func addSubViewsConstrains() { //StackView addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.9), stackView.heightAnchor.constraint(equalToConstant: 500) ]) //Label addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.centerYAnchor.constraint(equalTo: stackView.centerYAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupLabel() { label.backgroundColor = .white label.numberOfLines = 5 label.text = """ first BaseLine second BaseLine third BaseLine 4th BaseLine last BaseLine """ } private func setupStackView() { stackView.backgroundColor = .gray stackView.axis = .horizontal stackView.alignment = .lastBaseline stackView.distribution = .fillEqually let views: [UIView] = [redView,label,orangeView,yelloView] views.forEach { view in stackView.add![スクリーンショット 2021-05-05 15.19.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/68321e3f-f289-6d9d-14a8-55a1761c3919.jpeg) ArrangedSubview(view) } } } PlaygroundPage.current.liveView = View() center StackViewの鉛直中央(centerYAnchor)に中身の鉛直中央が揃うように配置される。 trailing/bottom leading/topの逆ver。 lastBaseLine StackViewにUILabelが含まれている時、その最後の行に高さが揃う。 destribution destributionは名詞で配分や散布という意味。axisで定めた方向に対して、中身のUIViewをどのように広げるのかを定めるのがdestributionであるといえる。 UIKit.UIStackViewでの定義 public enum Distribution : Int { case fill = 0 case fillEqually = 1 case fillProportionally = 2 case equalSpacing = 3 case equalCentering = 4 } fill StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまでUIViewを広げる。中身の辺の幅が足りない時にどのUIViewの幅が伸ばされるのかということについては調査中であるが、UILayoutPriorityあたりが絡んでいそう。画像では最初に追加したredViewのwidthが伸ばされている。 なお、destributionで説明する画像の中でalignmentはcenterにしてある。 import PlaygroundSupport import UIKit class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .gray setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .white stackView.axis = .horizontal stackView.alignment = .center stackView.distribution = .fill addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } PlaygroundPage.current.liveView = View() fillEqualy StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまで中身のViewを均等に広げる。 fillProportionally 中身のUIViewの幅の和がStackViewの幅に満たない時、中身のUIViewの幅の比をもとに各UIViewを広げる。 これは各UIViewのwidthを1,2,3,4,5にしているので、stackViewのwidthを1:2:3:4:5に分割するように広げられる。 let redView = ColorView(color: .red, size: CGSize(width: 1, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 2, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 3, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 4, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 5, height: 100)) equalSpacing 中身のUIViewの間隔が等しくなるように配置する。 equalCentering 中身のUIViewの中心の間隔が等しくなるように配置される。 ソースコード コピペでPlaygroundsで動きます。 import PlaygroundSupport import UIKit class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 60, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 90, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 120, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 150, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 180, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .gray setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .white stackView.axis = .horizontal stackView.alignment = .center stackView.distribution = .equalCentering addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } PlaygroundPage.current.liveView = View()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

StackViewの挙動を確認した話

準備 色と大きさを指定できるUIViewを作成 class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } 表示するViewを作成。この中で先程のColorViewを使っている。 class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .gray stackView.axis = .horizontal stackView.alignment = .fill stackView.distribution = .fillProportionally addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } axis これは理解しやすい。積み上げる方向が水平方向か鉛直方向か指定できる。 UIKit.UIStackViewでの定義 extension NSLayoutConstraint { public enum Axis : Int { case horizontal = 0 case vertical = 1 } } alignment alignmentは名詞で一列、整列線といった意味。方向を定めた後にどこに並ぶのかを決める。 UIKit.UIStackViewでの定義 public enum Alignment : Int { /* Align the leading and trailing edges of vertically stacked items or the top and bottom edges of horizontally stacked items tightly to the container. */ case fill = 0 /* Align the leading edges of vertically stacked items or the top edges of horizontally stacked items tightly to the relevant edge of the container */ case leading = 1 public static var top: UIStackView.Alignment { get } case firstBaseline = 2 // Valid for horizontal axis only /* Center the items in a vertical stack horizontally or the items in a horizontal stack vertically */ case center = 3 /* Align the trailing edges of vertically stacked items or the bottom edges of horizontally stacked items tightly to the relevant edge of the container */ case trailing = 4 public static var bottom: UIStackView.Alignment { get } case lastBaseline = 5 // Valid for horizontal axis only } fill fillは高さが揃うようにaxisと垂直方向の辺の長さを広げる。 中身のViewが持っていた高さの制約よりも優先される。 leading/top axisがhorizontalのt時はtop、verticalの時はleadingが適切であるよう(Storyboardだと表示されなくなる)だが、どちらでも動く。中身のUIViewの上の辺(topAnchor)がx,y座標の小さい方の上の辺に揃う。 firstBaseLine StackViewにUILabelが含まれている時、その1行目に高さが揃う。 なお、firstBaseLineとlastBaseLineで用いているコードは他のものと異なっている。 import PlaygroundSupport import UIKit class ColorView: UIView { let size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let label = UILabel() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 20)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 40)) let yelloView = ColorView(color: .yellow, size: CGSize(width: 100, height: 60)) let stackView = UIStackView() override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white addSubViewsConstrains() setupStackView() setupLabel() } private func addSubViewsConstrains() { //StackView addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.9), stackView.heightAnchor.constraint(equalToConstant: 500) ]) //Label addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.centerYAnchor.constraint(equalTo: stackView.centerYAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupLabel() { label.backgroundColor = .white label.numberOfLines = 5 label.text = """ first BaseLine second BaseLine third BaseLine 4th BaseLine last BaseLine """ } private func setupStackView() { stackView.backgroundColor = .gray stackView.axis = .horizontal stackView.alignment = .lastBaseline stackView.distribution = .fillEqually let views: [UIView] = [redView,label,orangeView,yelloView] views.forEach { view in stackView.add![スクリーンショット 2021-05-05 15.19.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/68321e3f-f289-6d9d-14a8-55a1761c3919.jpeg) ArrangedSubview(view) } } } PlaygroundPage.current.liveView = View() center StackViewの鉛直中央(centerYAnchor)に中身の鉛直中央が揃うように配置される。 trailing/bottom leading/topの逆ver。 lastBaseLine StackViewにUILabelが含まれている時、その最後の行に高さが揃う。 destribution destributionは名詞で配分や散布という意味。axisで定めた方向に対して、中身のUIViewをどのように広げるのかを定めるのがdestributionであるといえる。 UIKit.UIStackViewでの定義 public enum Distribution : Int { case fill = 0 case fillEqually = 1 case fillProportionally = 2 case equalSpacing = 3 case equalCentering = 4 } fill StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまでUIViewを広げる。中身の辺の幅が足りない時にどのUIViewの幅が伸ばされるのかということについては調査中であるが、UILayoutPriorityあたりが絡んでいそう。画像では最初に追加したredViewのwidthが伸ばされている。 なお、destributionで説明する画像の中でalignmentはcenterにしてある。 import PlaygroundSupport import UIKit class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .gray setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .white stackView.axis = .horizontal stackView.alignment = .center stackView.distribution = .fill addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } PlaygroundPage.current.liveView = View() fillEqualy StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまで中身のViewを均等に広げる。 fillProportionally 中身のUIViewの幅の和がStackViewの幅に満たない時、中身のUIViewの幅の比をもとに各UIViewを広げる。 これは各UIViewのwidthを1,2,3,4,5にしているので、stackViewのwidthを1:2:3:4:5に分割するように広げられる。 let redView = ColorView(color: .red, size: CGSize(width: 1, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 2, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 3, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 4, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 5, height: 100)) equalSpacing 中身のUIViewの間隔が等しくなるように配置する。 equalCentering 中身のUIViewの中心の間隔が等しくなるように配置される。 ソースコード コピペでPlaygroundsで動きます。 import PlaygroundSupport import UIKit class ColorView: UIView { var size: CGSize init(color: UIColor, size: CGSize) { self.size = size super.init(frame: CGRect()) backgroundColor = color } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: size.width, height: size.height) } } class View: UIView { let stackView = UIStackView() let redView = ColorView(color: .red, size: CGSize(width: 60, height: 100)) let orangeView = ColorView(color: .orange, size: CGSize(width: 90, height: 100)) let yellowView = ColorView(color: .yellow, size: CGSize(width: 120, height: 100)) let greenView = ColorView(color: .green, size: CGSize(width: 150, height: 100)) let blueView = ColorView(color: .blue, size: CGSize(width: 180, height: 100)) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .gray setupStackView() setupColorViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupStackView() { stackView.backgroundColor = .white stackView.axis = .horizontal stackView.alignment = .center stackView.distribution = .equalCentering addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor), stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor), stackView.widthAnchor.constraint(equalTo: self.widthAnchor) ]) } private func setupColorViews() { let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView] colorViews.forEach { view in stackView.addArrangedSubview(view) } } } PlaygroundPage.current.liveView = View()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【algorithm】線形探索法、二分探索法

はじめに 今回は探索アルゴリズムを紹介したいと思います。 探索アルゴリズムとは 探索アルゴリズム(サーチアルゴリズム)とは、大量のデータから目的のデータを見つけるときに用いられるアルゴリズムです。 探索アルゴリズムには線形探索法(リニアサーチ)と二分探索法(バイナリサーチ)があります。 それぞれ見ていきましょう。 線形探索法(リニアサーチ) 線形探索法は、先頭から順番に目的の値が見つかるまで探す単純なアルゴリズムです。 線形探索法のポイントは二つです。 ・探す値が見つかったときはその配列番号(0または自然数)を使う ・探す値が見つからなかったときはマイナスの値を使う このようなルールで考えることにします。 流れとして、配列番号を入れる変数にマイナスの値を入れて初期化しておきます。もし見つかった場合はその変数を目的のデータの配列番号を代入して更新します。目的のデータが見つかった場合はその時点で終了します。 let nums = [1, 4, 6, 0, 3, 5] let searchNum = 3 var index = -1 for n in 0..<nums.count { if nums[n] == searchNum { index = n break // forを抜ける } } print("番号:", index) // 4 配列は0番から要素番号が始まるので、番号が4ということは、先頭から5番目に目的の値が存在していたということになりますね。 二分探索法(バイナリサーチ) 二分探索法は、調べる範囲を半分に絞りながら探していきます。範囲を二つ(バイナリ)に分けて探していくので、バイナリサーチと呼ばれます。 ただし、この方法が使えるのはデータがソートされている(順番に並べる)必要があります。 小さい順に値が並んだ配列を考えます。探す値を決め、データの真ん中の値を調べます。一致すればこの時点で終了です。もし、真ん中の値が探す値より小さい場合は、真ん中より左はさらに小さい値しかないので、右側だけ探せばいいことになります。これの繰り返しになります。 let nums = [1, 4, 9, 12, 14, 23, 29, 30, 100] let searchNum = 30 var index = -1 var left = 0 var right = nums.count - 1 while left <= right { let middle = Int((left + right) / 2) if nums[middle] == searchNum { index = middle break } else if nums[middle] < searchNum { left = middle + 1 } else if nums[middle] > searchNum { right = middle - 1 } } print("番号:", index) // 7 探す値より真ん中の値が大きければ、それより右は探す必要がないので、右(right)を詰めます。 探す値より真ん中の値が小さければ、それよ左は探す必要がないので、左(left)を詰めます。 おわりに 今回は探索アルゴリズムを紹介しました。 次回は整列アルゴリズムを紹介したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【algorithm】合計、平均、最大最小、データ交換

はじめに いつもSwiftの投稿をしていますが、アルゴリズムの投稿も今後はしていきたいと思います。 今回は合計値、平均値、最大値最小値、データ交換のアルゴリズムをSwiftで書いてみたいと思います。 また、Swiftには最大値、最小値などを求める便利な関数が用意されていますが、今回はそれらを使わずにアルゴリズムの基礎として、考えていきたいと思います。 合計値 配列の全ての要素の合計した値を求めます。 要素の一つ一つにアクセスしてそれらを都度合計値sumに足していくイメージです。 let nums = [1, 4, 3, 8, 5] var sum = 0 for n in 0..<nums.count { sum += nums[n] } print("合計:", sum) // 21 以下のような別解も考えられます。 let nums = [1, 4, 3, 8, 5] var sum = 0 for num in nums { sum += num } print("合計:", sum) // 21 平均値 合計値をデータの個数で割ったものが平均値になります。 一点注意点として、Swiftでは型推論というものがあります。sumはInt型になっており、array.countももちろんInt型になっています。これらを単純に割り算してしまうと求める答えもInt型になってしまいます。これでは少数点が正しく出力されてくれません。なので、型変換をして少数を扱える方にしてあげる必要があります。sumもarray.countもFloat型にしてあげましょう。 let nums = [1, 4, 3, 8, 5] var sum = 0 for n in 0..<nums.count { sum += array[n] } let average = Float(sum) / Float(nums.count) print("平均:", average) 最大値と最小値 最大値も最小値もやっていることは同じなので、今回は最大値を求めるアルゴリズムを紹介します。 max変数を用意して、データ一つ一つとその変数を比較し、用意した変数より大きければmax変数を更新して更新した値を以降の比較に用いるイメージです。データを全て確認した時点でmax変数に入っている値がそのデータの最大値になります。このように、全てのデータを確認し、もし条件に一致したら何かしらの処理をするというときはfor文の中にif文を書きます。 let nums = [12, 1, 14, 19, 10, 3] var max = nums[0] for n in 0..<nums.count { if max < nums[n] { max = nums[n] } } print("最大値:", max) // 19 データの交換 単純に二つのデータを入れ替えるときはそのデータが入った変数と仮の変数を一つ用意します。理由は二つしかない場合は一方を入れ替えるともう一方に入れるはずのデータが既に上書きされてしまうためです。 var a = 1 var b = 2 var t = 0 t = a a = b b = t print("a:", a) // 2 print("b:", b) // 1 おわりに 今後はアルゴリズムの投稿も増やしていきたいと思います。 次回は探索アルゴリズムを紹介したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む