- 投稿日:2020-09-13T23:23:52+09:00
Content Hugging Priority と Content Compression Resistance Priority を理解する
今回は AutoLayout 関連で、なんとなく使っていて詳しく理解していなかった
Content Hugging Priority
とContent Compression Resistance Priority
を調べていきたいと思います。Content hugging priority とは?
この Priority(優先度) は View が本来のサイズよりも大きくなることに抵抗する度合いを設定します。つまり、この値が大きくなればなるほど、View がそのコンテンツよりも大きくならないようになります。
View Content hugging priority UIView 250 UITextView 250 UIButton 250 UILabel 251 UIImageView 251 UISwitch 750 Content Compression Resistance Priority とは?
この Priority は View が本来のサイズよりも小さくなることに抵抗する度合いを設定します。つまり、この値が大きくなればなるほど、View がそのコンテンツよりも小さく縮小されないようになります。
View Content Compression Resistance Priority UIView 750 UITextView 750 UIButton 750 UILabel 750 UIImageView 750 UISwitch 750 知っておくといいこと
Q1
みなさんが仕事で AutoLayout を使っている場合、少なくとも一回ぐらいは Xib で下記のように Label の高さを指定せずに Label 同士を繋げて View のコンテンツとして制約を貼った時に起こる Error に遭遇したことがあるかと思います。
Label 単体で制約を貼った場合は Error が出ないのに、高さを指定していない Label を連結するとどうして Error が出るのでしょう?少し考えてみてください?
答え
答えは、
Content hugging priority
の vertial の値がどちらも251だからです?つまり、どちらも同じ値なので、制約を満たすためにどちらかの Label を広げて表示させたいのですが、値が競合しているためシステムが判断できないよ、とのことです。それでは、試しに
blueLabel
のContent hugging priority
の vertical 値を1ポイント下げて広げやすくしてみます。Q2
下記のようにラベルの幅の制約(幅40pt)を
priority750
でセットして省略されてしまうような長い文字列を挿入した時、またも Error に遭遇してしまいましたこれはなぜでしょうか?答え
答えは、
Content Compression Resistance Priority
の horizontal の値が横幅(NSLayoutConstraint
)の priority と競合しているためです。これによって、システムは横幅で指定されている40ptとコンテンツサイズの大きさに合わせるContent Compression Resistance Priority
の値のどちらを優先していいか分からず Error が出てしまっているのですね?それでは、試しに
Content Compression Resistance Priority
の値を1ポイント上げて751にしてみましょう。すると、下記のようにコンテンツ幅が優先されて表示されるようになりました?また、
Content Hugging Priority
およびContent Compression Resistance Priority
は、Viewにサイズを決定するための十分な制約がない場合にNSContentSizeLayoutConstraint
という制約をつくるのにも使用されるそうで、プログラム中でNSContentSizeLayoutConstraint
とNSLayoutConstraint
の Priority の値が同じ場合はNSLayoutConstraint
が優先されるようです。参考
- 投稿日:2020-09-13T20:25:29+09:00
【Swift】CoreNFCのコールバック地獄から逃れる
地獄から逃れる為のライブラリ作りました。
I've created a library that wraps CoreNFC for use in RxSwift.https://t.co/LRBq9doL9f#iOS #Swift #RxSwift #CocoaPods #Carthage pic.twitter.com/H9UnPUj0Mn
— かりば (@karibash) July 24, 2020コールバック辛すぎ問題
FeliCaを読み取っていろいろやりたい場合のコールバック処理がつらい
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { let tag = tags.first! session.connect( to: tag ) { (error) in if let _ = error { session.invalidate(errorMessage: "NFCタグの読み取りに失敗しました。") return } guard case .feliCa(let feliCaTag) = tag else { session.restartPolling() return } feliCaTag.requestService( nodeCodeList: self.SERVICE_CODE_LIST ) { nodeKeyVersionList, error in if let _ = error { session.invalidate(errorMessage: "NFCタグの読み取りに失敗しました。") return } feliCaTag.readWithoutEncryption( serviceCodeList: self.SERVICE_CODE_LIST, blockList: self.BLOCK_LIST ) { status1, status2, dataList, error in session.invalidate() // do something. } } } }RxCoreNFCでコールバック地獄を解決
RxCoreNFCを使うと下記のようのフラットに記述できるようになります。
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092]) let tags = sessions.begin().tags().felicaTags() let connectedTags = Observable.combineLatest(tags, sessions) .flatMap { tag, session in session.connect(tag) } .share() let invalidates = connectedTags .requestService(nodeCodeList: SERVICE_CODE_LIST) .withLatestFrom(connectedTags) .readWithoutEncryption(serviceCodeList: SERVICE_CODE_LIST, blockList: BLOCK_LIST }) .do(onNext: { result in // Do something }) .withLatestFrom(sessions) .invalidate() .first() button.rx.tap .flatMapFirst { invalidates } .subscribe() .disposed(by: disposeBag)
- 投稿日:2020-09-13T15:49:09+09:00
[Swift Packages] Xcodeのbuild locationを変更する
Swift Package Managerを利用している場合、TargetのBuild Settingsからビルドロケーション (Build Locations) を任意の場所に変更すると、Xcode 11現在ではパッケージ追加時に以下のエラーが出ることがあります。
Error occurred; cancel and retry operation. Swift packages are not supported when using legacy build locations, but the current project has them enabled.詳しい原因は未調査ですが、TargetのBuild Settingsを変更すると、Xcodeが勝手にレガシーなビルドロケーション設定をしていると認識してしまうために起こります。Xcodeの不具合かもしれません。
解決方法
Xcodeメニューの File → Project Settings の「Pre-User Project Settings」にある「Advanced...」を押し、Build Locationを
Legacy
からCustom
に変更します。ProductsとIntermediatesの場所を任意の場所に変更した後は「Done」を押して閉じ、Xcodeを再起動すると、問題なくパッケージが追加できるようになります。Voila!
- 投稿日:2020-09-13T08:39:05+09:00
[Xcode] 実務的Tips: Storyboard IDの2つの用途
今日からでもすぐに取り入れられて、
- コードをよりクリーンにできる、とか
- 工数を削減できる、とか
そんなTipsを紹介していく記事シリーズです。
「知らなかった」「気づかなかった」「忘れていた」そんな誰かの役に立てば幸いです。前提環境
- Xcode 11.3.1
- Swift 5.1.3
割とメジャーなStoryboard IDの用途
こちらの記事にあるように、
画面遷移をコードで行う場合に、StoryboardからUIViewControllerのインスタンスを取得するために使う、という用途です。
【Swift】画面遷移の方法まとめ - Segueを使わない画面遷移(たぶん)マイナーなStoryboard IDの用途
Storyboard Referenceによって、Storyboard-AからStoryboard-Bに遷移する場合に、Storyboard-Bの先頭じゃなく後方のUIViewControllerに遷移したい時に使う、という用途です。
例
Storyboard-B(遷移先)
後方のViewController(この例ではFourthViewController)に任意のStoryboard IDを設定しておきます。
Storyboard-A(遷移元)
↑FirstViewControllerからStoryboard-BのFourthViewControllerに遷移する導線があります(赤枠部分)。↓このStoryboard Referenceで、「Referenced ID」に上で設定したStoryboard IDを指定します。
そうするとFirstViewControllerからStoryboard-BのFourthViewControllerに遷移できます。
このように、Storyboard IDををうまく使うことで、1つのStoryboardで複数の導線をカバーすることができます。
- 投稿日:2020-09-13T08:39:05+09:00
[Xcode] 実務的Tips: Storyboard IDの用途はコード上でUIViewControllerのインスタンスを作る「だけじゃない」
今日からでもすぐに取り入れられて、
- コードをよりクリーンにできる、とか
- 工数を削減できる、とか
そんなTipsを紹介していく記事シリーズです。
「知らなかった」「気づかなかった」「忘れていた」そんな誰かの役に立てば幸いです。前提環境
- Xcode 11.3.1
- Swift 5.1.3
割とメジャーなStoryboard IDの用途
こちらの記事にあるように、
画面遷移をコードで行う場合に、StoryboardからUIViewControllerのインスタンスを取得するために使う、という用途です。
【Swift】画面遷移の方法まとめ - Segueを使わない画面遷移(たぶん)マイナーなStoryboard IDの用途
Storyboard Referenceによって、Storyboard-AからStoryboard-Bに遷移する場合に、Storyboard-Bの先頭じゃなく後方のUIViewControllerに遷移したい時に使う、という用途です。
例
Storyboard-B(遷移先)
後方のViewController(この例ではFourthViewController)に任意のStoryboard IDを設定しておきます。
Storyboard-A(遷移元)
↑FirstViewControllerからStoryboard-BのFourthViewControllerに遷移する導線があります(赤枠部分)。↓このStoryboard Referenceで、「Referenced ID」に上で設定したStoryboard IDを指定します。
このように、Storyboard IDををうまく使うことで、1つのStoryboardで複数の導線をカバーすることができます。
- 投稿日:2020-09-13T01:10:01+09:00
【Swift】String.SubSequenceとは何か?
はじめに
文字列の日付を桁揃え(yyyy/mm/dd)しようとして、こんな感じで書いてみたところ・・・
let str2 = "2020/7/1" let array = str2.split(separator: "/") if (array.count >= 3){ let str3 = array[0]+"/"+String(format: "%02d", Int(array[1]))+"/"+String(format: "%02d", Int(array[2])) }Int(array[1])) のところで、以下のようなエラーが出ました。
Initializer 'init(_:)' requires that 'String.SubSequence' (aka 'Substring') conform to 'BinaryInteger'String.SubSequenceとは?
String.SubSequenceって何?ってことで公式ページを見ると「typealias SubSequence = Substring」となっているので、Substringのページを見てみる。
Overview
When you create a slice of a string, a Substring instance is the result. Operating on substrings is fast and efficient because a substring shares its storage with the original string. The Substring type presents the same interface as String, so you can avoid or defer any copying of the string’s contents.概観
文字列のスライスを作成すると、Substringインスタンスが結果になります。サブストリングは元のストリングとストレージを共有するため、サブストリングの操作は高速で効率的です。 SubstringタイプはStringと同じインターフェースを提供するため、文字列の内容のコピーを回避または延期できます。つまりString.split()の戻り値が、StringではなくSubstringになっているらしい。typeを確認してみると、確かにSubstringの配列になっていた。
let array = str2.split(separator: "/") print(type(of: array))Array<Substring>
SubStringをStringに変換すれば問題なさそうなので、以下のようにコードを書き換えました。
let str2 = "2020/7/1" let array = str2.split(separator: "/") print(type(of: array)) if (array.count >= 3){ let str3 = array[0]+"/"+String(format: "%02d", Int(String(array[1]))!)+"/"+String(format: "%02d", Int(String(array[2]))!) }これで正しく変換できました。
"2020/07/01"
まとめ
この辺はたぶんはSwift4でStringがコレクション化されたことによるのかなと思われます。
以下、参考リンクです。実行環境
- Xcode 11.2.1(Playgroundで実行)