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

Empty State表示共通化の事例 / Case of empty state commonization

kentaro
December 18, 2018

Empty State表示共通化の事例 / Case of empty state commonization

potatotips #57の資料です。
https://potatotips.connpass.com/event/109263/

kentaro

December 18, 2018
Tweet

Other Decks in Technology

Transcript

  1. &NQUZ4UBUFදࣔڞ௨Խͷࣄྫ
    QPUBUPUJQT,FOUBSP5BHVDIJ !LFOLFOLFO@

    View Slide

  2. ֓ཁ
    ϧʔϧܾΊ
    ࣮૷ʹ͍ͭͯ
    Ξ δ Σ ϯ μ

    View Slide

  3. ֓ཁ

    View Slide

  4. &NQUZ4UBUFͷදࣔΛ౷Ұͨ͠
    Կ Λ ͠ ͨ ͷ ͔

    View Slide

  5. ը໘࡞੒࣌ʹ౎౓σβΠϯɾ࣮૷͞Ε͍ͯͨ
    ը໘ʹΑͬͯද͕ࣔόϥόϥ
    σβΠϯɾ࣮૷Λʮߟ͑Δʯ޻਺͕ൃੜ
    ͳ ͥ ΍ ͬ ͨ ͷ ͔

    View Slide

  6. C F G P S F

    View Slide

  7. B G U F S

    View Slide

  8. w ΞϓϦ಺ͷ&NQUZ4UBUFද͕ࣔ౷Ұ͞Εͨ
    w ίετ࡟ݮʢاըɾσβΠϯɾ࣮૷ʣ
    Կ ͕ ಘ Β Ε ͨ ͷ ͔

    View Slide

  9. ϧʔϧܾΊ

    View Slide

  10. σβΠφʔͱ૬ஊ
    ࢓༷ɾσβΠϯͷϧʔϧΛܾΊͨ
    ֓ ཁ

    View Slide

  11. จݴΛදࣔ͢Δ͚ͩʢ࠶ݕࡧ౳ͷػೳ͚ͭͳ͍ʣ
    $73Λ௥͍ͬͯͳ͍
    γϯϓϧͳ࢓༷Ͱ0,
    ࢓ ༷

    View Slide

  12. දࣔ͢ΔཁૉʹରԠͨ͠จݴΛఆΊΔ
    ༧໿৘ใ
    ʮ༧໿৘ใ͸͋Γ·ͤΜʯ
    จ ݴ

    View Slide

  13. w ϑΥϯτͷαΠζɾΧϥʔΛ౷Ұ
    w 7JFXͷதԝʹϥϕϧΛ഑ஔ
    σ β Π ϯ

    View Slide

  14. ϧʔϧ͸FTBʹ·ͱΊͯνʔϜʹڞ༗
    ʢશࣾһӾཡՄೳʣ
    ʢΤϯδχΞ޲͚ʹJ04࣮૷·ͱΊ΋هࡌʣ
    υ Ω ϡ ϝ ϯ τ Խ

    View Slide

  15. ࣮૷

    View Slide

  16. protocolͱenumΛ࢖ͬͯ
    ൚༻తʹར༻Ͱ͖ΔΑ͏ʹ࣮૷͢Δ
    ํ ਑

    View Slide

  17. FOVNͰදࣔཁૉΛදݱ
    ରԠ͢Δจݴ΋ఆٛ
    จ ݴ

    View Slide

  18. /// empty stateΛఆٛ͢Δenum
    enum EmptyState {
    /// ༧໿
    case reservation
    /// ςʔϒϧ
    case table
    // ʢଞলུʣ
    /// λΠτϧ
    var title: String {
    switch self {
    case .reservation: return NSLocalizedString("key_of_reservation", comment: "")
    case .table: return NSLocalizedString("key_of_table", comment: “")
    // ʢଞলུʣ
    }
    }
    }

    View Slide

  19. QSJWBUFͰ࣮૷
    ֎͔Β༨ܭͳૢ࡞ͤ͞ͳ͍
    ݟͨ໨͕౷Ұ͞ΕΔ
    W J F X

    View Slide

  20. /// empty state༻ͷView
    private final class EmptyStateView: UIView {
    /// λΠτϧϥϕϧ
    private let titleLabel: UILabel = {
    let titleLabel = UILabel()
    // ϥϕϧͷઃఆ
    return titleLabel
    }()
    init(_ state: EmptyState) {
    super.init(frame: .zero)
    setup(with: state)
    }
    required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }
    /// ηοτΞοϓΛ͢Δ
    private func setup(with state: EmptyState) {
    titleLabel.text = state.title
    addSubview(titleLabel)
    titleLabel.edges(equalTo: self)
    }
    }

    View Slide

  21. ֎͔Β͸͍ͭ͜Λ͍͡Δ
    &NQUZ4UBUF7JFXͱಉ͡ϑΝΠϧʹ࣮૷
    ) P M E F S

    View Slide

  22. /// empty stateΛදࣔ͢ΔViewͷϗϧμʔ
    final class EmptyStateHolder {
    /// ຊϓϩύςΟܦ༝ͰviewͷisHiddenΛ֎͔Βηοτ͢Δ
    var isHidden = false {
    didSet {
    view.isHidden = isHidden
    }
    }
    /// empty stateΛදࣔ͢ΔView
    ///
    /// fileprivateʹͯ͠༨ܭͳPropertyΛϑΝΠϧ֎͔Βૢ࡞ͤ͞ͳ͍
    /// ͱ͢Δ͜ͱͰݟͨ໨͕ΞϓϦ಺Ͱ౷Ұ͞ΕΔ͜ͱΛ୲อ
    fileprivate let view: EmptyStateView
    /// state͔Βࣗ਎Λੜ੒͢Δ
    /// 1ՕॴͰෳ਺λΠϓͷEmptyStateΛग़͢ཁ͕݅ͳ͍લఏͷ࣮૷
    ///
    /// - Parameter state: EmptyState
    init(_ state: EmptyState) {
    view = EmptyStateView(state)
    }
    }

    View Slide

  23. ԼهΛࢀߟʹ&EHFʹର੍ͯ͠໿Λ͚ͭΔ
    6*7JFXͷFYUFOTJPOΛ࣮૷
    IUUQTTQFBLFSEFDLDPNLBNFJLFGBSFXFMMTOBQLJU
    ิ ଍ ɿ 7 J F X ͷ F Y U F O T J P O

    View Slide

  24. extension UIView {
    /// ࣗ਎ͷEdgeʹର੍ͯ͠໿Λ͚ͭΔ
    ///
    /// - Parameters:
    /// - view: ੍໿ͷج४ͱͳΔview
    /// - offset: ੍໿஋ (σϑΥϧτ஋: .zero )
    func edges(equalTo view: UIView, offset: UIEdgeInsets = .zero) {
    translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
    topAnchor.constraint(equalTo: view.topAnchor, constant: offset.top),
    bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -offset.bottom),
    leftAnchor.constraint(equalTo: view.leftAnchor, constant: offset.left),
    rightAnchor.constraint(equalTo: view.rightAnchor, constant: -offset.right)
    ])
    }
    }

    View Slide

  25. ڞ௨Խ༻ͷQSPUPDPMΛ࡞੒
    Q S P U P D P M

    View Slide

  26. // MARK: - ڞ௨Խ༻ϓϩτίϧ
    /// empty stateදࣔϓϩτίϧ
    protocol EmptyStateDisplayable where Self: UIResponder {
    /// empty stateΛදࣔ͢ΔViewͷϗϧμʔ
    var emptyStateHolder: EmptyStateHolder { get }
    /// emptyStateViewΛadd͢Δ
    func addEmptyStateView()
    }

    View Slide

  27. 6*7JFX$POUSPMMFSɾ6*7JFX
    ͦΕͧΕσϑΥϧτ࣮૷Λ௥Ճ
    ʢجຊˢΛݺͿ͚ͩʣ
    Q S P U P D P M F Y U F O T J P O

    View Slide

  28. // MARK: - UIViewControllerͷ৔߹
    extension EmptyStateDisplayable where Self: UIViewController {
    /// emptyStateViewΛviewʹaddSubview͢Δ
    func addEmptyStateView() {
    view.addSubview(emptyStateHolder.view)
    emptyStateHolder.view.edges(equalTo: view)
    }
    }
    // MARK: - UIViewͷ৔߹
    extension EmptyStateDisplayable where Self: UIView {
    /// emptyStateViewΛࣗ਎ʹaddSubview͢Δ
    func addEmptyStateView() {
    addSubview(emptyStateHolder.view)
    emptyStateHolder.view.edges(equalTo: self)
    }
    }

    View Slide

  29. /// ར༻ଆͷ࣮૷ྫ
    /// EmptyStateDisplayableʹ४ڌͤ͞Δ
    final class SampleViewController: UIViewController, EmptyStateDisplayable {
    /// EmptyStateDisplayableͷϓϩύςΟ
    /// ରԠ͢ΔEmptyState(enum)͔ΒॳظԽ
    let emptyStateHolder = EmptyStateHolder(.reservation)
    override func viewDidLoad() {
    super.viewDidLoad()
    // EmptyStateDisplayableͷϝιουΛݺ΂͹EmptyStateView͕addSubView͞ΕΔ
    addEmptyStateView()
    // EmptyStateΛදࣔ͢Δ৚݅ʹ߹ΘͤͯemptyStateHolder.isHiddenʹ஋Ληοτ
    emptyStateHolder.isHidden = false
    }
    }

    View Slide

  30. &NQUZ4UBUFͰ͸ָΛͯ͠
    ྗΛೖΕΔ΂͖ͱ͜Ζʹ
    ஫ྗͰ͖ΔΑ͏ʹͳΔͱϋοϐʔ

    View Slide