SwiftUIでbyClippingを実現する
これは何
以前書いた、3点リーダーの省略は改行される場合にテキストが中途半端で途切れる問題がありました。
改行を許可せず、尚且つ3点リーダーを出さないUIKit
のUILabel
にあるbyClipping
の設定で使える表示をSwiftUIでもしたいなと思って調べたのでやり方を備忘録として書いていきます。
before | After |
---|---|
サンプルコードはこちらです。
最初の例
以下のようなコードを書いた場合
struct ParentView: View {
var body: some View {
VStack(spacing: 30) {
Text("Hello!! World")
.lineLimit(1)
}
}
}
struct TextByClipping_Previews: PreviewProvider {
static var previews: some View {
ParentView()
.frame(width: 60, height: 20)
.background(Color.red)
}
}
Text
の本来の横幅に対して親Viewの幅が足りないので、表示はこのようになります。
こちらの3点リーダーを省略して、親ViewいっぱいにTextを描画するように修正していきます。
手順
Textを本来のサイズで描画する
Text
にfunc fixedSize(horizontal: Bool, vertical: Bool)
をつけることによって、親Viewのサイズに左右されず、子View本来のサイズで描画することができます。
struct ParentView: View {
var body: some View {
VStack(spacing: 30) {
Text("Hello!! World")
.lineLimit(1)
// fixedSizeを指定して、Text本来のサイズで描画する
.fixedSize(horizontal: true, vertical: false)
}
}
}
この際にhorizontal
をtrue
、vertical
をfalse
にすると水平方向のみ、親Viewのサイズの制約がなくなるのでTextが横に伸びるようになります。
親Viewからはみ出た部分を切り取る
親Viewからはみ出た箇所は切り取ってしまうようにします。
GeometryReader
などで親Viewのサイズを取得することができます。
取得できた親Viewのサイズをもとにfunc frame(width: CGFloat)
でTextにサイズを指定します。
この際に親Viewからはみ出た箇所はfunc clipped()
で切り取ってしまいます。
struct ParentView: View {
var body: some View {
// GometryReaderで親View内の描画可能範囲を取得
GeometryReader { reader in
Text("Hello!! World")
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
// frameでTextのサイズを指定する
.frame(width: reader.size.width)
// 指定サイズをはみ出た箇所を削除
.clipped()
}
}
}
切り取った後の表示はこのようになります。
中央部分が切り取られているので、Textのサイズに合わせて切り取り箇所を変えていきます。
Textを左寄せにする
func frame(width: CGFloat, alignment: Alignment)
で左寄せになるように修正します。
alignmentを.leading
に指定すると左寄せになります。
struct ParentView: View {
var body: some View {
GeometryReader { reader in
Text("Hello!! World")
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
// alignmentを指定して、左寄せを基準にする
.frame(width: reader.size.width, alignment: .leading)
.clipped()
}
}
}
Textのサイズに合わせて、Textを中央寄せか左寄せを選択する
func alignmentGuide(_ g: HorizontalAlignment, computeValue: @escaping (ViewDimensions) -> CGFloat)
でTextのサイズに合わせて、動的に左側のマージンを調整します。
このメソッドのViewDimensions
から自身のサイズが取得できます。
親ViewとTextのサイズを比較して、Textのサイズが大きければ左寄せ、小さければ中央寄せになるように左マージンを計算します。
この際のマージンの値のプラスマイナスに注意してください。
struct ParentView: View {
var body: some View {
GeometryReader { reader in
Text("Hello!!")
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
// clipされる前のサイズからalignmentを計算する
.alignmentGuide(.leading, computeValue: { dimension in
// 親ViewとTextの横幅の差分をとる
let difference = reader.size.width - dimension.width
if difference > 0 {
// 親Viewの方が大きい場合中央寄せ
// マイナスに注意
return -(difference / 2)
} else {
// Textの方が大きい場合左寄せ
return 0
}
})
.frame(width: reader.size.width, alignment: .leading)
.clipped()
}
}
}
結果、Textの長さによって動的に中央寄せと左寄せが変わるようになりました 👏
はみ出る場合は左寄せ | はみ出なければ中央寄せ |
---|---|
Discussion