$30 off During Our Annual Pro Sale. View Details »

SwiftUIでUIViewを使うときのレイアウト処理 / Layout process when using UIKit view in SwiftUI

Mike Apurin
December 17, 2021

SwiftUIでUIViewを使うときのレイアウト処理 / Layout process when using UIKit view in SwiftUI

Mike Apurin

December 17, 2021
Tweet

More Decks by Mike Apurin

Other Decks in Technology

Transcript

  1. SwiftUIͰUIViewΛ࢖͏ͱ͖ͷ
    ϨΠΞ΢τॲཧ
    Apurin Mikhail @ YUMEMI.swift #14

    View Slide

  2. ࣗݾ঺հ
    Apurin Mikhail ΞϓϦϯɾϛϋΠϧ
    auramagi
    apurin.me
    􀒡
    - ΏΊΈͷiOSΤϯδχΞ

    - ࠓݱࡏϩγΞ͔ΒϦϞʔτϫʔΫ

    - ژ౎ → ϞεΫϫͷఱؾΪϟοϓ͸͙͍͑ (+13° → -17°)

    - ିग़ͷM1 Max MacBook Proಧ͘௚લʹߦͬͨͷ͕੯͍͠

    View Slide

  3. ࠓ೥ͷৼΓฦΓ
    ͳΜͱͳ͘SwiftUIΛ৮Δػձ͕ଟ͔ͬͨҰ೥ͩͬͨ

    - UIKitͷҊ݅ͰϓϨϏϡʔΛಋೖ

    - iOS 13.0·ͰҾ্͖͛Δ४උ

    - σϞΞϓϦ౳ͷ࡞੒

    - ΏΊΈࣾ಺ͷSwiftUIษڧձ։࠵

    - ݸਓ։ൃ
    🥰

    View Slide

  4. SwiftUIͰUIKitͷϏϡʔ͕࢖͍͍ͨ
    - εΫϥονͷҊ݅Ͱ͸ͳ͍ͳΒɺ஥ྑ͘ڞଘ͢Δඞཁ͕͋Δ

    - Apple͕४උ͞Ε͍ͯΔ΋ͷΛ࢖͑͹Ͱ͖Δ

    - UIView → UIViewRepresentable

    - UIViewController → UIViewControllerRepresentable

    View Slide

  5. struct LabelRepresentable: UIViewRepresentable {


    let text: String




    func makeUIView(context: Context) -> UILabel {


    .init()


    }




    func updateUIView(_ uiView: UILabel, context: Context) {


    uiView.numberOfLines = 0


    uiView.text = text


    }


    }


    UIViewRepresentableΛॻ͍ͯΈΔ
    ؆୯ʹॻ͚·͢ʂ

    ඞਢϝιου:

    - Ϗϡʔੜ੒ makeUIView

    - Ϗϡʔߋ৽ updateUIView

    View Slide

  6. struct LabelRepresentable_Previews: PreviewProvider {


    static var previews: some View {


    LabelRepresentable(


    text: "Journey before destination"


    )


    }


    }


    ΍ͬͨʂ

    ɻɻɻ

    Μʁ

    தԝἧ͑ʹͳ͍ͬͯͳ͍ʂʂ
    PreviewͰ࢖ͬͯΈΔ

    View Slide

  7. struct LabelRepresentable_Previews: PreviewProvider {


    static var previews: some View {


    LabelRepresentable(


    text: "Journey before destination"


    )


    .border(.red)


    .padding()


    }


    }
    ը໘શମͷαΠζΛऔ͍ͬͯΔ

    SwiftUI ͷ Text ͷΑ͏ʹɺͽͬͨΓͷαΠζΛͱͬͯ

    தԝἧ͑ʹͳΔΠϝʔδ͕ͩͬͨɺ·͍͍͔͋ʁ
    PreviewͰ࢖ͬͯΈΔ

    View Slide

  8. struct LabelRepresentable_Previews: PreviewProvider {


    static var previews: some View {


    LabelRepresentable(


    text: "To love the journey is to accept no such

    end. I have found, through painful experience,

    that the most important step a person can take

    is always the next one."


    )


    .border(.red)


    .padding()


    }


    }
    PreviewͰ࢖ͬͯΈΔ
    Ξ

    ͩΊͩʔ orz

    View Slide

  9. SwiftUIͷϨΠΞ΢τॲཧ
    WWDC 2019 — Building Custom Views with SwiftUI
    ͦ΋ͦ΋SwiftUIͰͲ͏΍ͬͯҐஔͱαΠζ͕

    ܾΊΒΕΔͷ͔ʁ

    ϨΠΞ΢τॲཧͷৄࡉ͸ඇެ։ͳͷͰ

    υΩϡϝϯτ͕΄΅ͳ͍ 😔

    جຊͷॲཧ͸ҎԼͷεςοϓ͔ΒͳΔ

    1. ਌View͕ࢠViewʹαΠζΛఏҊ͢Δ

    2. ࢠView͕ࣗ෼ͷαΠζΛܾΊΔ

    3. ਌View͕ࣗ෼ͷ࠲ඪۭؒͰࢠViewΛ഑ஔ͢Δ

    View Slide

  10. SwiftUIͷϨΠΞ΢τॲཧ
    ViewͷαΠζ͸࣍ͷܗʹͳ͍ͬͯΔͱਪଌͰ͖Δ
    struct Dimension {


    let min: CGFloat


    let ideal: CGFloat


    let max: CGFloat


    }
    - min: ࠷௿஋

    - ideal: ཧ૝αΠζ

    - max: ࠷ߴ஋

    - ؔ܎Λݫक: min <= ideal <= max

    View Slide

  11. SwiftUIͷϨΠΞ΢τॲཧ
    ఏҊαΠζ͸࣍ͷܗͰ౉͞ΕΔͱਪଌͰ͖Δ
    struct ProposedSize {


    let width: CGFloat?


    let height: CGFloat?


    }
    - ஋͕ࢦఆ͞Ε͍ͯΔ৔߹͸ɺͦΕΛϕʔεʹαΠζΛܭࢉ

    - nilͷ৔߹͸ɺViewࣗ਎ͷidealαΠζΛͱΔ

    - .
    fi
    xedSizeͰnilʹมߋ͞ΕΔ

    View Slide

  12. SwiftUIͷϨΠΞ΢τॲཧ
    ௨ৗͷ৔߹͸ɺҎԼͷܭࢉ͕ࣜదԠ͞ΕΔ
    if let proposedWidth = proposedSize.width {


    // dimensionsͰ্ݶ஋ɾԼݶ஋Λ཈͑Δ


    width = max(


    dimensions.min,


    min(dimensions.max, proposedWidth)


    )


    } else {


    width = dimensions.ideal


    }


    // height΋ಉ༷

    View Slide

  13. Color.mint


    .frame(


    minWidth: 100, maxWidth: 200,


    minHeight: 100, maxHeight: 200


    )


    .frame(width: 300, height: 300)


    .border(.mint)
    SwiftUIͷϨΠΞ΢τॲཧ
    📱
    W: 390


    H: 763
    .frame(minWidth: 200, …)
    .frame(width: 300, height: 300)
    Color
    ఏҊαΠζ ࣮αΠζ
    W: 300


    H: 300
    W: 200


    H: 200
    W: 300


    H: 300
    W: 200


    H: 200
    W: 200


    H: 200

    View Slide

  14. Text("Hello YUMEMI.swift")


    .fixedSize()


    .border(.mint)


    .frame(width: 300, height: 300)


    .border(.mint)


    SwiftUIͷϨΠΞ΢τॲཧ
    📱
    W: 390


    H: 763
    .fixedSize()
    .frame(width: 300, height: 300)
    Text("Hello YUMEMI.swift")
    ఏҊαΠζ ࣮αΠζ
    W: 300


    H: 300
    W: nil


    H: nil
    W: 300


    H: 300
    W: 149.7


    H: 20.3
    W: 149.7


    H: 20.3

    View Slide

  15. UIKitͱͷޓ׵ੑ
    Ͱ͸ɺͦͷॲཧͰͲ͏΍ͬͯUIViewͷαΠζ͕ܾΊΒΕΔʁ

    • SwiftUIଆͷϥούʔ͕Α͠ͳʹ΍ͬͯ͘ΕΔʢʣ

    • UIViewRepresentable

    • UIViewControllerRepresentable

    • ͦΕͧΕͷڍಈ͕ҧ͏

    View Slide

  16. UIViewRepresentable
    AutoLayoutͷintrinsicαΠζͱpriorityΛݩʹͯ͠αΠζΛܭࢉ
    Intrinsic size Compression Resistance Hugging
    ݁Ռ

    (min … ideal … max)
    (-1) - - 0 … 0 … ∞
    x < 750 < 750 0 … x … ∞
    x < 750 >= 750 0 … x … x
    x < 750 < 750 x … x … ∞
    x >= 750 >= 750 x … x … x
    -1 = UIView.noIntrinsicMetric

    750 = UILayoutPriority.defaultHigh

    View Slide

  17. UIViewRepresentable
    1. AutoLayoutͷϓϩύςΟ͔ΒUIView͕औΕΔαΠζΛܾఆ

    2. ී௨ͷܭࢉࣜͰɺఏҊ͞ΕͨαΠζ͔Β࣮αΠζΛܭࢉ

    3. UIViewͷ invalidateIntrinsicContentSize() ͕ݺ͹ΕͨΒ΍Γ௚͠

    View Slide

  18. UIViewRepresentable
    1 ͱ 2 ͷڍಈ͸ௐ੔Ͱ͖Δඇެ։API͕͋Δ

    ⚠ ඇެ։APIͳͷͰϦϦʔεϏϧυʹೖΕͪΌμϝ ⚠
    public protocol UIViewRepresentable {


    // 1 (AutoLayoutϓϩύςΟ → ͳΕΔαΠζ)


    func _overrideLayoutTraits(


    _ layoutTraits: inout SwiftUI._LayoutTraits,


    for uiView: Self.UIViewType


    )




    // 2 (ఏҊαΠζ → ࣮αΠζ)


    func _overrideSizeThatFits(


    _ size: inout CoreGraphics.CGSize,


    in proposedSize: SwiftUI._ProposedSize,


    uiView: Self.UIViewType


    )


    }

    View Slide

  19. UIViewControllerRepresentable
    UIViewRepresentableͱൺ΂ͯɺௐ੔Ͱ͖Δ͜ͱ͕গͳ͍

    • preferredContentSize ͰidealαΠζΛࢦఆՄೳ

    • ҎԼͷViewαΠζ͕ܾఆ͞ΕΔ

    • 0 … preferredContentSize … ∞

    View Slide

  20. import RepresentableKit


    struct UILabel_Previews: PreviewProvider {


    static var previews: some View {


    UIViewAdaptor {


    let view = UILabel()


    view.numberOfLines = 0


    view.text = "To love the journey is to accept no

    such end. I have found, through painful


    experience, that the most important step a


    person can take is always the next one."


    return view


    }


    .padding()


    }


    }
    RepresentableKitͷ঺հ
    UIViewRepresentableͷαΠζܭࢉΛந৅Խͯ͠

    ΑΓ؆୯ʹ࢖͑ΔΑ͏ʹϔϧύʔϥΠϒϥϦΛ

    ࡞Γ·ͨ͠ɻ

    https://github.com/yumemi-inc/RepresentableKit

    View Slide

  21. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide