画面半分表示のModal View

iOS 15+では、新しいSafariブックマークのような画面半分表示のModal Viewが作れるようになりました。 f:id:machinemxy:20220203220839p:plain

let modalVC = ModalViewController()
let modalNC = UINavigationController(rootViewController: modalVC)
if #available(iOS 15.0, *) {
    if let sheet = modalNC.sheetPresentationController {
        // 以下の設定より、デフォルトは半分で表示、ドラッグしてほぼ全画面まで拡大も可能
        sheet.detents = [.medium(), .large()]
    }
}
self.present(modalNC, animated: true)

Content Hugging PriorityとContent Compression Resistance Priority

Content Hugging Priority:二つのコンテンツが並んで、十分なスペースがある場合、そのpriority高いやつはもっとタイトな形式で表示する。
例:textAのContent Hugging PriorityはtextBより大きい場合: f:id:machinemxy:20220131121843p:plain

Content Compression Resistance Priority:二つのコンテンツが並んで、十分なスペースがない場合、そのpriority高いやつは圧縮されない。
例:longlongTextAのContent Compression Resistance PriorityはlonglongTextBより大きい場合: f:id:machinemxy:20220131122146p:plain

frameとbounds

frameはsuperViewに対する位置、x, yはsuperViewによって変わる。
boundsはview自身に対する位置、x, yは常に0。
だからviewAのsubView viewBを作って、viewBはviewAを完全重合したい場合、viewB.frame = viewA.boundと書く。
因みに、viewを移動したり変形したり場合、frameは共に変化するが、boundsは変わらない。

UIKitでStoryboardなしのアプリを作る

  1. XcodeでUIKitのアプリを作成する。
  2. Main.storyboardを削除する。
  3. ProjectファイルのTarget -> General -> Deployment Info -> Main Interfaceを空欄にする。
  4. Info.plist -> Application Scene Manifest -> Scene Configuration -> Application Session Role -> Storyboard Nameという項目を削除
  5. SceneDelegate.swiftのguard let _ = (scene as? UIWindowScene) else { return }を下記コードに変更:
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window

実用的なString Extension

SwiftのStringは色々な考慮上、便利なSubstring方法を提供されていない。LeetCodeをやる時Stringの問題が合うとかなり面倒くさい。下記のextensionをコピペーすれば大変助かる。

extension StringProtocol {
    subscript(_ offset: Int)                     -> Element     { self[index(startIndex, offsetBy: offset)] }
    subscript(_ range: Range<Int>)               -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: ClosedRange<Int>)         -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: PartialRangeThrough<Int>) -> SubSequence { prefix(range.upperBound.advanced(by: 1)) }
    subscript(_ range: PartialRangeUpTo<Int>)    -> SubSequence { prefix(range.upperBound) }
    subscript(_ range: PartialRangeFrom<Int>)    -> SubSequence { suffix(Swift.max(0, count-range.lowerBound)) }
}

参考:https://stackoverflow.com/questions/24092884/

??の実行順序について

LeetCodeをやっている時バグを遭遇した。

dic[a] = dic[a] ?? 0 + 1

のようなコードを書いた時、dic[a]は常に1になってしまう。
原因は0 + 1はいつも先に実行される。
だから上記にコードは下記のように書かないといけない:

dic[a] = (dic[a] ?? 0) + 1