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

[Swift] @_exportedの挙動調査

はじめに

Swiftの@_exportedについて、その挙動を調査したのでまとめる。

@_exportedとは

  • _がプレフィックスとしてついており、Swiftで公式には公開されていないAttribute
  • 以下のようにimport文で利用可能
@_exported import Foo

挙動調査

前提

以下のような3階層のモジュール構成を考える。

├── Main
│   └── Main.swift
├── SubModule
│   ├── SubModule1.swift
│   └── SubModule2.swift
└── SubSubModule
    └── SubSubModule.swift

同一モジュール内への影響調査

SubModule1.swiftにて、@_exported import SubSubModuleを記述する。
ファイルをまたいでSubModule2.swiftでもSubSubModule.swiftのAPIが利用できる。

├── Main
│   └── Main.swift
├── SubModule
│   ├── SubModule1.swift // @_exported import SubSubModuleを記述
│   └── SubModule2.swift // SubSubModule.swiftのAPIが利用できる
└── SubSubModule
    └── SubSubModule.swift

上位モジュールへの影響調査

さらに、Main.swiftにて、import SubModuleを記述する。
モジュールをまたいでMain.swiftでもSubModule.swiftのAPIが利用できる。

├── Main
│   └── Main.swift // import SubModuleを記述、SubSubModule.swiftのAPIが利用できる
├── SubModule
│   ├── SubModule1.swift // @_exported import SubSubModuleを記述
│   └── SubModule2.swift
└── SubSubModule
    └── SubSubModule.swift

おわりに

  • @_exportedをimportに付与すると、同一モジュール内でのimportが不要になる
  • また、上位モジュールが下位モジュールをimportでインポートした場合、下位モジュールで@_exportedでimportされている孫関係にあたる下位モジュールもimportされる
  • 以下のように、UIKitをimportするだけで、FoundationをimportしなくてもFoundationのAPIが利用できることから、UIKit内部で@_exported import Foundationが定義されていると推測される
import UIKit
import Foundation // import不要

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift における、Object、Methodを連結、作成する方法、AVPlayer で、外部URLのファイルを再生する方法。

ViewController.swift における、言わば、script における functionの連結。

import UIKit
import Avfoundation
class ViewController: UIViewController
のあと、Button Action の中や、
override func viewDidLoad(){
} 内に置いて。
スクリーンショット 2020-03-20 21.47.27.png
から、
スクリーンショット 2020-03-20 21.49.25.png
に、self.~の形で発進し、func ~(){} で受け取る
また、題材の
スクリーンショット 2020-03-20 21.53.18.png
の形で、いずれのカッコの中にもおかず、class ViewControler : UIController の直下にセットしデクレアしておく。
二種類のNSArrayは、viewDidLoad(){} 内に置いて、item = ["url","url2","url3"]
songName = ["title1","title2","title3"]
として置き、counter = 0などとし、item[counter]、songName[counter]で取り出す。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デフォルトTableViewサンプル集(swift)

はじめに

TableViewを使うときは毎回セルとかヘッダー・フッターのカスタムクラス作ったりしてたけどデフォルトのやつでも色々できるやん?ってことで色々作ってみました。

サンプル集

Cell Style AccessoryType Color Cell Selected Color
cell_style cell_accessory_type color_cell selected_color_cell
Separator Style Table Style Grouped Detail Color Section
separator_style table_style grouped_detail color_section

TableViewControllerを使うか否か

画面にtableViewしか表示しないって場合は基本的には UITableViewController を使う方が便利なのでできるだけ UITableViewController を使います。

便利機能

  • tableViewのひも付けとかしなくてもいい
  • var clearsSelectionOnViewWillAppear: Bool
  • var refreshControl: UIRefreshControl?
  • 編集モードの時に func setEditing(_ editing: Bool, animated: Bool) オーバーライドしなくてもいい
  • キーボード表示時に自動でスクロールしてくれる
  • static cell が使える
  • セーフエリアまわりもなんかいい感じにしてくれる

UITableViewControllerドキュメント

Cell Style

UITableViewCell には下記の Style があります。

  • default (Basic)
  • value1 (Right Detail)
  • value2 (Left Detail)
  • subtitle (Subtitle)

こんな感じ。

cell_style

default (Basic) 以外は detail テキストも設定できる。value2 (Left Detail) 以外は image も設定できる。

下記のように設定

cell.textLabel?.text = "title"
cell.detailTextLabel?.text = "detail"
cell.imageView?.image = UIImage(named: "sample")

Cell AccessoryType

UITableViewCell には下記の AccessoryType があります。

  • none
  • disclosureIndicator
  • detailDisclosureButton
  • checkmark
  • detailButton

こんな感じ。

accessoryType

下記のように設定(もしくは Storyboard で設定)

cell.accessoryType = .none

Cell 背景色

UITableViewCell の背景色を設定する。

こんな感じ。

cell_color

下記のように設定

if cell.backgroundView == nil {
  cell.backgroundView = UIView()
}
cell.backgroundView?.backgroundColor = .red

上記のように backgroundView を設定する。確か cell.backgroundColor = .red はダメだった気がするけど動いた(勝手にいい感じにしてくれるのかも??)

Cell 選択時の背景色

UITableViewCell の選択時の背景色を設定する。

こんな感じ。

cell_selected_color

下記のように設定

let v = UIView()
v.backgroundColor = .red
cell.selectedBackgroundView = v

選択時の背景色の設定はよくわからない...

cell.selectedBackgroundView?.backgroundColor = .red ではダメだった:scream:

リファレンスに下記のように書いてあるが別に nil でもなかった...

Default is nil for cells in UITableViewStylePlain, and non-nil for UITableViewStyleGrouped.

Separator

Style

UITableView には下記の SeparatorStyle があります。

  • none
  • singleLine (Default)
  • singleLineEtched (deprecated)

こんな感じ。

separator

Storyboard で設定する場合は Default も含めて4パターンあるが Default は singleLine なので変わらない。また、singleLineEtched は Deprecated になっておりこちらも singleLine 同様の見た目になった。(tableView のスタイルを Grouped にしても変化なし)

なので SeparatorStyle は下記の2パターンのみ

  • none
  • singleLine

下記のように設定(もしくは Storyboard で設定)

tableView.separatorStyle = .none

Color

区切り線も色も設定できるが、セルごとの設定は不可。

こんな感じ。

separator_color

下記のように設定(もしくは Storyboard で設定)

tableView.separatorColor = .blue

Inset

区切り線の inset はセルごとの設定も可。

こんな感じ。

テーブルに設定 セルごとに設定
inset_all inset_cell

下記のように設定(もしくは Storyboard で設定)

// すべて同じ値を設定する場合
tableView.separatorInset = .zero
// セルごとに設定する場合
cell.separatorInset = .init(top: 0, left: 15 * CGFloat(indexPath.row), bottom: 0, right: 0)

小技(セルがない部分の区切り線を消す)

下記のようにするとセルがない部分の区切り線を消すことができる:v:

tableView.tableFooterView = UIView()
通常 footer設定
line line_none

Table Style

UITableView には下記の Style があります。

  • plain
  • grouped
  • insetGrouped

こんな感じ。(Plain とか表示してるのは TableView とは関係ないただの label です)

instGrouped はそのままだとわかりにくいので tableView の背景色を変えています。

table_style

ぱっと見わかりにくいですが下記のようにヘッダー・フッターを設定すると違いがわかります。

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
  return "section header \(section)"
}

func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
  return "section footer \(section)"
}

plain の場合スクロールしてもヘッダー部分は固定される。

plain

groupedinsetGrouped の場合スクロールするとヘッダー部分も一緒にスクロールされる。ヘッダーの文字はすべて大文字になる。

grouped insetGrouped
grouped inset_grouped

小技(ヘッダー・フッターの文字色を変える)

下記のようにするとヘッダー・フッターの文字色を設定できる:clap:

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
  (view as? UITableViewHeaderFooterView)?.textLabel?.textColor = .red
}

func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
  (view as? UITableViewHeaderFooterView)?.textLabel?.textColor = .blue
}

こんな感じ

section_color

Grouped の detailText 設定

groupedinsetGrouped の場合ヘッダー部分に detailTextLabel を設定できる。

If you configured your table view with the group style, you can also configure the detailTextLabel property.

こんな感じ(ヘッダーは大文字にならない)

grouped_detail

下記のように設定する。

Storyboardで Sections のところの Automatic にチェックをつける

layout

これをしておかないと textLabeldetailTextLabel が重なったりする。

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  var header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
  if header == nil {
    header = UITableViewHeaderFooterView(reuseIdentifier: "header")
  }
  header?.textLabel?.text = "section header \(section)"
  header?.detailTextLabel?.text = "detail"
  return header
}

Section Color (ヘッダー・フッター)

セクションのヘッダー・フッターの背景色を設定する。

こんな感じ

color_section

下記のように設定する。

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  var header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
  if header == nil {
    header = UITableViewHeaderFooterView(reuseIdentifier: "header")
  }
  if header?.backgroundView == nil {
    header?.backgroundView = UIView()
  }
  header?.backgroundView?.backgroundColor = .red
  return header
}

override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
  var footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: "footer")
  if footer == nil {
    footer = UITableViewHeaderFooterView(reuseIdentifier: "footer")
  }
  if footer?.backgroundView == nil {
    footer?.backgroundView = UIView()
  }
  footer?.backgroundView?.backgroundColor = .red
  return footer
}

さいごに

カスタムクラスをつくらなくてもわりと色々できそう。

Table関連はよく使うけど実装方法忘れたりするのでもっと充実させたい。

プルリクくれてもええんやで:rolling_eyes:

サンプル集

プルリク大歓迎(だれかきてくれないかな:eyes::eyes::eyes:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firestoreで指定の条件にマッチするDocumentだけ削除する

self.db.collection("orders").whereField("uid", isEqualTo: self.user.uid).getDocuments { (snapshot, error) in
                snapshot?.documents.forEach({ (doc) in
                    doc.reference.delete()
                })
            }

https://stackoverflow.com/questions/47180076/how-to-delete-document-from-firestore-using-where-clause

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIでWebViewを使う(WKWebViewのObservableObject化)

SwiftUIでWebViewを使うを参考にした。

import SwiftUI
import WebKit

public class SUIWebBrowserObject: WKWebView, WKNavigationDelegate, ObservableObject {
    private var observers: [NSKeyValueObservation?] = []

    private func subscriber<Value>(for keyPath: KeyPath<SUIWebBrowserObject, Value>) -> NSKeyValueObservation {
        observe(keyPath, options: [.prior]) { object, change in
            if change.isPrior {
                self.objectWillChange.send()
            }
        }
    }

    private func setupObservers() {
        observers = [
            subscriber(for: \.title),
            subscriber(for: \.url),
            subscriber(for: \.isLoading),
            subscriber(for: \.estimatedProgress),
            subscriber(for: \.hasOnlySecureContent),
            subscriber(for: \.serverTrust),
            subscriber(for: \.canGoBack),
            subscriber(for: \.canGoForward)
        ]
    }

    public override init(frame: CGRect = .zero, configuration: WKWebViewConfiguration = WKWebViewConfiguration()) {
        super.init(frame: frame, configuration: configuration)
        navigationDelegate = self
        setupObservers()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        navigationDelegate = self
        setupObservers()
    }
}

public struct SUIWebBrowserView: UIViewRepresentable {
    public typealias UIViewType = UIView

    private var browserObject: SUIWebBrowserObject

    public init(browserObject: SUIWebBrowserObject) {
        self.browserObject = browserObject
    }

    public func makeUIView(context: Self.Context) -> Self.UIViewType {
        browserObject
    }

    public func updateUIView(_ uiView: Self.UIViewType, context: Self.Context) {
        //
    }
}

struct WebBrowser: View {
    @ObservedObject var browser = SUIWebBrowserObject()

    init(address: String) {
        guard let a = address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
        guard let u = URL(string: a) else { return }
        browser.load(URLRequest(url: u))
    }

    func ItemImage(systemName: String) -> some View {
        Image(systemName: systemName)
            .imageScale(.large).aspectRatio(contentMode: .fit)
            .frame(width: 32, height: 32)
    }

    var Title: Text {
        Text(verbatim: browser.url?.absoluteString.removingPercentEncoding ?? "")
    }

    var LItems: some View {
        HStack {
            Button(action: {
                self.browser.goBack()
            }) {
                ItemImage(systemName: "chevron.left")
            }.disabled(!browser.canGoBack)
        }
    }

    var TItems: some View {
        HStack {
            Button(action: {
                if self.browser.isLoading {
                    self.browser.stopLoading()
                } else {
                    self.browser.reload()
                }
            }) {
                ItemImage(systemName: browser.isLoading
                    ? "xmark" 
                    : "arrow.clockwise"
                )
            }
        }
    }

    var body: some View {
        NavigationView {
            SUIWebBrowserView(browserObject: browser)
                .navigationBarTitle(Title, displayMode: .inline)
                .navigationBarItems(leading: LItems, trailing: TItems)
        }.navigationViewStyle(StackNavigationViewStyle())
    }
}

struct ContentView: View {
    var body: some View {
        WebBrowser(address: "https://www.apple.com/jp/")
    }
}

75049C07-A975-4E53-9230-7B6EFD503215.jpeg

WKWebViewをObservableObject化したSUIWebBrowserObjectと、そのビューワーとなるSUIWebBrowserViewを使って自由なUIのカスタムウェブブラウザを簡単に作れるようになった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む