SwiftUIのForEachでViewの繰り返し表示が上手く出来なかったときのメモ
上手く行かないやつ
init時点でstrListに存在するものに関しては表示されるが、途中で追加されたものが描画されなかった
class StateValue: ObservableObject { @Published var strList: [Moji] struct Moji { var title: String var location: CGPoint var size: CGFloat var angle: Angle var color: Color public init( title: String, location: CGPoint = .init(x: 120, y: 20), size: CGFloat = 20, angle: Angle = .init(degrees: 0.0), color: Color = .black ) { self.title = title self.location = location self.size = size self.angle = angle self.color = color } }
ForEach(0..<list.count) { index in let item = list[index] Text(item.title) .foregroundColor(item.color) .position(item.location) .rotationEffect(item.angle) .gesture( DragGesture() .onChanged { value in state.strList[index].location = value.location state.isDragging = true } .onEnded { _ in state.isDragging = false } ) }
上手く行かないのはForeachの仕様らしい
最終的に繰り返したい構造体をIdentifiableに準拠させて、Foreachの書き方を変えて動作させた
class StateValue: ObservableObject { @Published var strList: [Moji] struct Moji: Identifiable { // Identifiableに準拠させる var id = UUID() var title: String var location: CGPoint var size: CGFloat var angle: Angle var color: Color public init( title: String, location: CGPoint = .init(x: 120, y: 20), size: CGFloat = 20, angle: Angle = .init(degrees: 0.0), color: Color = .black ) { self.title = title self.location = location self.size = size self.angle = angle self.color = color } }
ForEach(Array(list.enumerated()), id: \.offset) { index, item in Text(item.title) .foregroundColor(item.color) .position(item.location) .rotationEffect(item.angle) .gesture( DragGesture() .onChanged { value in state.strList[index].location = value.location state.isDragging = true } .onEnded { _ in state.isDragging = false } ) }
iOSアプリのアーカイブ方法の種類
配布方法の選択
App Store Connect: リリース版
Ad Hoc: リモートでのテスト用
→委託開発でアプリ開発している場合
アプリ(ipaファイル)をメールなどで配布し、iPhone・iPadにインストールする方式
事前にProvisioning Portal(プロビジョニングポータル)で、デバイスの登録を行っておく必要がある
Enterprise: 企業内用
セキュアなサーバーにアプリ(ipaファイル)、マニフェストファイル(plist)をアップして、URLからインストール
デバイスの事前登録が不必要で、何台でもインストール出来る
OTAとかで使えるようにしたのがこれ
Development: 開発用
KeyChain調査メモ
KeyChainとは?
UserDefaultと比較してセキュアに値を保存できる アプリを削除してもデータは残る(AppleIDに紐づく) 大量のデータ保存には向かない
使い方
(そのまま使うより神が作ったライブラリを使ったほうが楽らしい)
検索
任意の値に一致したデータが存在するか確認したいときは↓の関数 func SecItemCopyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus 引数にクエリ(検索条件)と結果が見つかったときの結果の参照(ポインタ)を持つ
let search: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "service",// サービス名 必須じゃない。kSecAttrAccountと合わせてユニーク kSecReturnAttributes as String: kCFBooleanTrue as Any, // kSecReturnAttributes:属性を返すか返さないか kSecAttrAccount as String: "account1", // アカウント名 kSecAttrServiceと合わせてユニーク kSecMatchLimit as String: kSecMatchLimitOne] as [String : Any] // 上限 // 保存データが存在するかの確認 let res = SecItemCopyMatching(search as CFDictionary, nil)
SecItemCopyMatchingの結果のステータスでこの辺は使う機会多いかも errSecItemNotFound:データが無かったとき errSecSuccess:エラーが無く成功(データが見つかったとき) errSecDuplicateItem:アイテムが重複していたとき 他↓参照 https://developer.apple.com/documentation/security/1542001-security_framework_result_codes
データの保存
// パスワード let str = "password" let data = str.data(using: .utf8) guard let _data = data else { return false } let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecValueData as String: _data, // 保存したいもの kSecAttrService as String: "service"] // サービス名 var itemAddStatus: OSStatus? = itemAddStatus = SecItemAdd(dic as CFDictionary, nil)
削除
let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "service"] if SecItemDelete(dic as CFDictionary) == errSecSuccess { // 削除成功したとき } else { // 削除失敗したとき }
その他メモ
保存するものの種類(パスワード、証明書、暗号鍵など)によって付属できる属性が変わる https://developer.apple.com/documentation/security/keychain_services#//apple_ref/doc/uid/TP30000898-CH4g-SW18
kSecAttrAccountとkSecAttrServiceの値を使ってユニークなデータとして保存している。kSecAttrServiceが違えばkSecAttrAccountが同じでもOK kSecAttrService は必須ではなくてkSecAttrAccount だけで運用可。 *その場合は同じ kSecAttrAccount の値は複数登録できない
参考
https://qiita.com/ayutaso/items/960e1b1f553e53ca60f4 https://ameblo.jp/xcc/entry-10813323465.html http://cocoadays.blogspot.com/2011/02/ios-keychain-services.html https://qiita.com/mario7/items/11f06502a022c01cfbb5
UIViewの拡大・縮小
拡大・縮小
UIView.transition(with: self.squareView, duration: 1.0, options: [.transitionFlipFromLeft, .repeat, .autoreverse], animations: { self.squareView.transform = CGAffineTransform(scaleX: 2, y: 2); // これを(scaleX: 1/2, y: 1/2)にすると1/2の大きさになる }, completion: nil)
UIViewのアニメーションについてのメモ
UIView.animateによるアニメーション
duration は何秒でアニメーションさせるか
delay で開始まで何秒遅延するか指定
options でアニメーションの動き方を指定 (後述)
animations で動かしたいUIViewのプロパティを変化
completion は完了した後に呼ばれるクロージャー
例
// optionsを複数指定したい場合は[.repeat,.autoreverse, xx, xx...]な感じで指定する UIView.animate(withDuration: 1.0, delay: 0.0, options: .autoreverse, animations: { self.squareView.center.y += 100.0 }, completion: nil)
UIView.transitionによるアニメーション
UIView.transition(with: self.squareView, duration: 1.0, options: [.transitionFlipFromLeft, .repeat, .autoreverse], animations: { self.squareView.backgroundColor = UIColor.yellow }, completion: { (finished: Bool) in self.squareView.backgroundColor = UIColor.blue })
optionsについて
指定できるoptionはたくさんあるのでめぼしいものをメモしておく
option名 | 説明 |
---|---|
autoreverse | 逆再生する |
repeat | 繰り返し |
curveEaseIn | アニメーションが進行するにつれて早くなる |
curveEaseIn | アニメーションが進行するにつれて早くなり、完了する前に再び遅くなる |
curveEaseOut | アニメーションが進行するにつれて遅くなる |
curveLinear | 等速 |
transitionFlipFromLeft | 垂直軸を中心に左から右に反転する |
transitionFlipFromRight | 垂直軸を中心に右から左に反転する |
transitionFlipFromTop | 水平軸を中心に上から下に反転する |
transitionFlipFromBottom | 水平軸を中心に下から上に反転するトランジション |
複数の処理を連続的に行う
open class func addKeyframe(withRelativeStartTime frameStartTime: Double, relativeDuration frameDuration: Double, animations: @escaping () -> Swift.Void)
を使う
UIView.animateKeyframes(withDuration: 2.0, delay: 0.0, options: [.repeat], animations: { UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1, animations: { self.squareView.center.y += 100.0 self.squareView.center.x += 100.0 self.squareView.transform = CGAffineTransform(rotationAngle: 360) }) UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 1, animations: { self.squareView.center.y -= 100.0 self.squareView.center.x -= 100.0 self.squareView.transform = CGAffineTransform(rotationAngle: 360) }) }, completion: nil)
参考
Swiftで袋文字を作る
袋文字を作りたくて、UISliderで文字の大きさとフチのサイズを可変にする方法を調べた。
import UIKit class ViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBOutlet weak var fontSize: UISlider! @IBOutlet weak var edgeSize: UISlider! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func fontSizeChanged(_ sender: Any) { label.font = UIFont.boldSystemFont(ofSize: CGFloat(fontSize.value)) } @IBAction func edgeSizeChanged(_ sender: Any) { if let text = label.text { label.attributedText = NSAttributedString(string: text, attributes: label.makeAttributedString(font: label.font, strokeEdge: edgeSize.value, foreGroundColor: UIColor.black, strokeColor: UIColor.green)) } } } extension UILabel { func makeAttributedString(font: UIFont, strokeEdge: Float, foreGroundColor: UIColor, strokeColor: UIColor) -> [NSAttributedString.Key : Any] { return [.strokeWidth : strokeEdge, .font : font, .foregroundColor :foreGroundColor, .strokeColor : strokeColor ] as [NSAttributedString.Key : Any] } }
この後はカラースライダーで文字の色を可変にする方法を探したい
参考にしたページ
ローマ数字からアラビア数字に変換する
LeetCodeの Roman to Integerっていう問題を解いた
今回はローマ数字からアラビア数字だったけど逆の問題もあるのでそのうち解きたい
class Solution { func romanToInt(_ s: String) -> Int { var sum:Int = 0 var lastVal = 0 let valueMap: [Character: Int] = ["I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000] for char in s.reversed(){ if let val = valueMap[char] { if val >= lastVal { sum += val } else { sum -= val } lastVal = val } } return sum } }