20220318のiOSに関する記事は3件です。

【iOS,Swift】上下左右の罫線を指定可能なCustomViewを作った(@IBDesignable)

環境 Xcode 13.2.1 Swift 5.5.2 上下左右に罫線となるUIViewを配置したXibファイルを作る 制約は省きますが、↓画像のような、上下左右にWidth:5pxのUIViewを配置したXibファイルを作ります (わかりやすいように上下左右の罫線はそれぞれ別の色にしてます) ※このXibのファイル名は「DesinableBorderView.xib」としてます。 Xibファイルに対応したCustomViewクラスを作成する コードそのまま載せます。 ポイントとしては、上下左右の表示・非表示を設定ができる、BoolのIBInspectable を作ったことです。 import UIKit @IBDesignable class DesinableBorderView: UIView { /* IBOutletを引く時は、XibファイルのFile's Ownerにこのクラスを指定する */ @IBOutlet weak var leftBorder: UIView! @IBOutlet weak var rightBorder: UIView! @IBOutlet weak var topBorder: UIView! @IBOutlet weak var bottomBorder: UIView! /// 左の線を描画するかどうか @IBInspectable var isHiddenLeft : Bool = true { didSet { if self.rightBorder != nil { self.leftBorder.isHidden = isHiddenLeft } } } /// 右の線を描画するかどうか @IBInspectable var isHiddenRight : Bool = true { didSet { if self.rightBorder != nil { self.rightBorder.isHidden = isHiddenRight } } } /// 上の線を描画するかどうか @IBInspectable var isHiddenTop : Bool = true { didSet { if self.topBorder != nil { self.topBorder.isHidden = isHiddenTop } } } /// 下の線を描画するかどうか @IBInspectable var isHiddenBottom : Bool = true { didSet { if self.bottomBorder != nil { self.bottomBorder.isHidden = isHiddenBottom } } } override init(frame: CGRect) { super.init(frame: frame) loadNib() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) loadNib() } override func prepareForInterfaceBuilder() { // StoryBoard上でリアルタイムに反映する用 super.prepareForInterfaceBuilder() loadNib() } func loadNib() { NSLog("loadNib") let bundle = Bundle(for: type(of: self)) let nibName = String(describing: DesinableBorderView.self) let view = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self, options: nil).first as! UIView view.frame = self.bounds addSubview(view) } } このファイルを作成したら、Xibファイルのほうに戻って、FilesOwnerやIBOutletを引くと、 ≡みたいなアイコンのタブに、IBInspectable の選択肢が出ると思います。 「Is Hidden Left」をONにすれば、左の罫線を非表示できます。 「Is Hidden Left」をOFFにすれば、左の罫線を表示できます。 (ほかの方向も同じです) 最後に 需要があるかわかりませんが、 罫線を引くのにプログラムでいちいちやらなきゃいけなくてめんどくて、 IBDesignable でリアルタイムで見れるようなやつを作りました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[SwiftUI + ProgressView] 処理の進捗に応じて ProgressView を更新

この記事の内容 ・処理の進捗に応じて ProgressView を更新 ※ SwiftUI の ProgressView を利用 開発環境 ハードウエア 項目 内容 PC MacBook Air(M1,2020) メモリ:16GB ストレージ:1TB ソフトウエア 項目 内容 言語 Swift 5.6 IDE Xcode Ver 13.3 バージョン管理 GitHub 実際のコードは下記リンクからご確認いただけます この処理のイメージ(動画) シーケンス図 ポイント ・メインスレッドとは別のスレッドで時間がかかる処理を実行 ・上記処理の進捗をクロージャで取得 ・処理の途中に Start ボタンを押せないようにする コード DownloadManager クラス ・時間のかかる処理を実装している(※今回はダミーの処理を実装した) ・進捗をクロージャに渡す 注意 時間のかかる処理は、メインスレッドではなく別のスレッドで行うこと [理由] アプリがクラッシュするため DownloadManager.swift import Foundation class DownloadManager { static func startDownload(completion: @escaping (Int) -> Void){ print("start Download") var progress = 0 // 時間がかかる処理 DispatchQueue.global().asyncAfter(deadline: .now()){ while(progress <= 100){ print(progress) completion(progress) // 進捗をクロージャに渡す progress += progress < 90 ? Int.random(in: 1...10) : 1 sleep(1) } } } } 処理の途中に Start ボタンを押せないようにする 注意 今回のコードで時間のかかる処理でエラーが発生した場合(例:途中で処理が中断した等)を考慮していない。 ContentView.swift import SwiftUI struct ContentView: View { @State private var progress:Int = 0 @State private var canButtonTap:Bool = true var body: some View { VStack { Spacer() ProgressView("\(progress)%",value: Float(progress),total: Float(100)) .progressViewStyle(.linear) .padding() Button(action: tapButton,label: { Text("Start") }) .disabled(!canButtonTap) // ここでボタンの活性⇄非活性を切り替える .buttonStyle(.borderedProminent) Spacer() } } private func tapButton(){ self.canButtonTap = false // ボタンをタップしたら、ボタンを非活性にする ∵ 処理中のボタン2度押しを禁止するため DownloadManager.startDownload { progress in self.progress = progress // グロージャで渡らせれた進捗状況をビューに表示 self.toggleButton() } } private func toggleButton(){ if progress == 100 { canButtonTap = true // 進捗が 100 であればボタンを押せるようにする } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } 感想 ・クロージャの使い方が、やっと腑に落ちた 今後 ・今回の処理とファイルのダウンロード処理と連携したい ※URLSessionDownloadDelegate を利用すれば、ダウンロード処理の開始、終了及び進捗状況の更新ができそう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIRefreshControlを使ったPullToRefreshでrefreshのタイミングを変えたい

はじめに を参考にしたUIRefreshControlに addTarget(self, action: #selector(refresh), for: .valueChanged) するやり方だとrefreshのタイミングに違和感があったので別のタイミングでrefreshを行いたかった 結論 UIScrollViewのdelegateの任意のタイミングでrefreshの処理を呼び出しましょう 個人的にはscrollViewWillEndDraggingかscrollViewWillBeginDeceleratingぐらいで refreshするのがsafariやtwitterのPullToRefreshに近くてしっくりきました class XXXViewController: UIViewController, UIScrollViewDelegate { @IBOutlet weak var webView: WKWebView! { didSet { webView.scrollView.delegate = self webView.scrollView.refreshControl = UIRefreshControl() } } override func viewDidLoad() { super.viewDidLoad() } private func refresh() { reload() } private func didRefresh() { webView.scrollView.refreshControl?.endRefreshing() } // 指を離してスクロール位置が上まで戻った時 func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { guard let isRefreshing = webView.scrollView.refreshControl?.isRefreshing else { return } if isRefreshing { webView.refresh() } } // 指を離した時 func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { guard let isRefreshing = webView.scrollView.refreshControl?.isRefreshing else { return } if isRefreshing { webView.refresh() } } } おまけ addTargetするやり方も一応記載しておきます 下に引っ張ると閾値(50ptぐらい?)を超えたタイミングでrefreshが呼び出されます class XXXViewController: UIViewController { @IBOutlet weak var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView.scrollView.refreshControl = UIRefreshControl() webView.scrollView.refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged) } @objc private func refresh() { reload() } private func didRefresh() { webView.scrollView.refreshControl?.endRefreshing() } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む