Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
86
Help us understand the problem. What is going on with this article?
@ngo275

Swift(iOS)でイケてるトースト・バナー・ポップアップを導入する (SwiftEntryKit)

More than 1 year has passed since last update.

SwiftEntryKit

トーストやポップアップ、プッシュ通知のようなビューを簡単に導入できるすごいライブラリ SwiftEntryKit をこの記事で紹介する。いくつか同様のライブラリ(Toast-Swiftなど)を試したが、SwiftEntryKitが圧倒的にイケてるUIUXを作れると感じた。イメージは以下の通り。

Toasts Notes Floats Popups
toasts_example notes_example floats_example popups_example
Alerts Forms Rating More...
alerts_example forms_example rating_example custom_example

インストール

ここに丁寧に書いてあるのでここでは言及しない。CocoaPodsやCarthageで入れるだけ。

使い方のイメージ

import SwiftEntryKit

///

func test() {
    var attributes = EKAttributes.bottomFloat // bottomFloat以外に様々なタイプがある
    // プロパティを設定していく
    // attributes.xxx = xxx
    // たとえば以下の通り
    attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(UIColor.red), EKColor(UIColor.orange)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
    attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
    attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
    attributes.statusBar = .light
    attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
    attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.bounds.width), height: .intrinsic)

    // EKNotificationMessageViewなど、EKxxxViewを作ってattributesとともにSwiftEntryKitに渡す
    // UIFont() は任意の形に変更する必要がある
    let title = EKProperty.LabelContent(text: "Sample Notification", style: .init(font: UIFont(), color: .standardContent))
    let description = EKProperty.LabelContent(text: "Body message", style: .init(font: UIFont(), color: .standardContent))
    let image = EKProperty.ImageContent(image: UIImage(named: "some image"), size: CGSize(width: 35, height: 35))
    let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)
    let notificationMessage = EKNotificationMessage(simpleMessage: simpleMessage)
    let contentView = EKNotificationMessageView(with: notificationMessage)

    // あとはSwiftEntryKit.displayを呼ぶだけ
    SwiftEntryKit.display(entry: contentView, using: attributes)
}

このライブラリでは、ビューのトップ、中央、ボトムの3つの位置(top / center / bottom)を選択でき、ビューのタイプは以下のような感じで10個以上定義されている。位置とビューのタイプを組み合わせて使うことになる。

  • EKAlertMessageView
  • EKFormMessageView
  • EKNotificationMessageView
  • EKNoteMessageView
  • EKPopUpMessageView
  • EKRatingMessageView

iOSのプッシュ通知のようなバナーを表示する

struct NotificationBanner {
    static func show(title: String, body: String, image: UIImage) {
        var attributes = EKAttributes.topFloat
        attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(UIColor.red), EKColor(UIColor.orange)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
        attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
        attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
        attributes.statusBar = .light
        attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
        attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.bounds.width), height: .intrinsic)

        // UIFont() は任意の形に変更する必要がある        
        let title = EKProperty.LabelContent(text: title, style: .init(font: UIFont(), color: .white))
        let description = EKProperty.LabelContent(text: body, style: .init(font: UIFont(), color: .white))
        let image = EKProperty.ImageContent(image: image, size: CGSize(width: 35, height: 35))
        let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)
        let notificationMessage = EKNotificationMessage(simpleMessage: simpleMessage)

        let contentView = EKNotificationMessageView(with: notificationMessage)
        SwiftEntryKit.display(entry: contentView, using: attributes)
    }
}

利用するのはこんな感じ。

NotificationBanner.show(title: "Notification title", body: "Notification body", image: UIImage(named: "xxx"))

お洒落なアラートを表示する

struct Alert {
    // ここではアクションが一つの場合
    static func show(title: String, body: String, image: UIImage, action: @escaping () -> Void) {
        var attributes = EKAttributes.topFloat
        attributes.displayDuration = .infinity
        attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(UIColor.purple), EKColor(UIColor.blue)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
        attributes.screenBackground = .visualEffect(style: .prominent)
        attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
        attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
        attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
        attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.bounds.width), height: .intrinsic)
        // これがないとアクションが実行されない
        attributes.entryInteraction = .absorbTouches

        // UIFont() は任意の形に変更する必要がある
        let title = EKProperty.LabelContent(text: title, style: .init(font: UIFont(), color: .white))
        let description = EKProperty.LabelContent(text: body, style: .init(font: UIFont(), color: .white))
        let image = EKProperty.ImageContent(image: image, size: CGSize(width: 35, height: 35))
        let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)        

        let label = EKProperty.LabelContent(text: "OK", style: .init(font: UIFont(), color: .black))
        let button = EKProperty.ButtonContent(label: label, backgroundColor: .white, highlightedBackgroundColor: .standardBackground) {
            // dismissを自分で呼ばないとビューが消えない
            SwiftEntryKit.dismiss()
            action()
        }        
        let content = EKProperty.ButtonBarContent(with: [button], separatorColor: EKColor(UIColor.black), expandAnimatedly: true)

        let alertMessage = EKAlertMessage(simpleMessage: simpleMessage, buttonBarContent: content)
        let view = EKAlertMessageView(with: alertMessage)

        SwiftEntryKit.display(entry: view, using: attributes)
    }
}

利用するのはこんな感じ。

Alert.show(title: "Alert title", body: "Alert body") {
    print("tapped!!")
}

ポップアップを表示する

struct Popup {
    static func show(title: String, body: String, action: @escaping () -> Void) {
        var attributes = EKAttributes.centerFloat
        attributes.displayDuration = .infinity
        attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(UIColor.purple), EKColor(UIColor.blue)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
        attributes.screenBackground = .visualEffect(style: .prominent)
        attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
        attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
        attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
        attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.bounds.width), height: .intrinsic)
        // これがないとアクションが実行されない
        attributes.entryInteraction = .absorbTouches
        // UIFont() は任意の形に変更する必要がある
        let title = EKProperty.LabelContent(text: title, style: .init(font: UIFont(), color: .white))
        let description = EKProperty.LabelContent(text: body, style: .init(font: UIFont(), color: .white))

        let label = EKProperty.LabelContent(text: "OK", style: .init(font: UIFont(), color: .black))
        let button = EKProperty.ButtonContent(label: label, backgroundColor: .white, highlightedBackgroundColor: .standardBackground)
        let action: EKPopUpMessage.EKPopUpMessageAction = {
            SwiftEntryKit.dismiss()
            // ここにボタン押下時のアクションを入れることができる
            action()
        }
        let popupMessage = EKPopUpMessage(title: title, description: description, button: button, action: action)
        let view = EKPopUpMessageView(with: popupMessage)

        SwiftEntryKit.display(entry: view, using: attributes)
    }
}

利用するのはこんな感じ。

Popup.show(title: "Popup title", body: "Popup body") {
    print("tapped!!")
}

フォーム付きのポップアップを表示する

struct Form {
    // フォームが1つの場合
    static func show(title: String, placeholder: String = "", image: UIImage, action: @escaping (String) -> Void) {
        var attributes = EKAttributes.centerFloat
        attributes.displayDuration = .infinity
        attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(UIColor.purple), EKColor(UIColor.blue)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
        attributes.screenBackground = .visualEffect(style: .prominent)
        attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
        attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
        attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
        attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.bounds.width), height: .intrinsic)
        // これがないとアクションが実行されない
        attributes.entryInteraction = .absorbTouches

        let white = EKColor(UIColor.white)
        let black = EKColor(UIColor.black)
        let gray = EKColor(UIColor.gray)

        let whiteStyle = EKProperty.LabelStyle(font: UIFont(), color: white)
        let blackStyle = EKProperty.LabelStyle(font: UIFont(), color: black)

        let title = EKProperty.LabelContent(text: title, style: whiteStyle)
        let ok = EKProperty.LabelContent(text: "OK", style: blackStyle)
        let placeholder = EKProperty.LabelContent(text: placeholder, style: whiteStyle)

        let content = EKProperty.TextFieldContent(keyboardType: .asciiCapable, placeholder: placeholder, tintColor: white, displayMode: .inferred, textStyle: whiteStyle, leadingImage: image, bottomBorderColor: white)

        let button = EKProperty.ButtonContent(label: ok, backgroundColor: .white, highlightedBackgroundColor: gtray) {
            SwiftEntryKit.dismiss()
            action(content.textContent)
        }

        let view = EKFormMessageView(with: title, textFieldsContent: [content], buttonContent: button)

        SwiftEntryKit.display(entry: view, using: attributes)
    }
}

利用するのはこんな感じ。

Form.show(title: "Form title", body: "Form body") { value in
    print("input value: \(value)")
}

まとめ

他にもToast、Note、Rating等のビューが用意されているが、同様の要領で利用できる。繰り返しになるが、EKNotificationMessageViewなど、EKxxxViewを作ってattributesとともにSwiftEntryKitに渡せば良い。

86
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ngo275
フルスタックめなエンジニアやっています

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
86
Help us understand the problem. What is going on with this article?