Skip to content

An easily implemented spotlight library to walk the user through the features

License

Notifications You must be signed in to change notification settings

noursandid/SundeedSpotlight

Repository files navigation

Sundeed

SundeedSpotlight

CocoaPods Compatible Platform License Language Last Commit HitCount

Example1Example2Example3Example4Example5

SundeedSpotlight is an easily implemented spotlight library to walk the user through the features

Requirements

  • iOS 9.0+
  • XCode 10.3+
  • Swift 5+

Installation


Installation via CocoaPods

SundeedSpotlight is available through CocoaPods. CocoaPods is a dependency manager that automates and simplifies the process of using 3rd-party libraries like MarkdownKit in your projects. You can install CocoaPods with the following command:

gem install cocoapods

To integrate SundeedSpotlight into your Xcode project using CocoaPods, simply add the following line to your Podfile:

pod "SundeedSpotlight"

Afterwards, run the following command:

pod install

Features

  • withAbilityToTapThroughSpotlight(_ passthrough: Bool): To state whether the user interactions with the spotlighted view is enabled.
    • Parameters:
      • passthrough (Bool):
        • True: SpotlightedView (e.g: UIButton) will perform its action when tapped while in the spotlight
        • False: SpotlightedView (e.g: UIButton) will not be notified that it is tapped
  • withInfoCornerRadius(_ cornerRadius: CGFloat): To specify the corner radius of the info view
    • Parameters:
      • cornerRadius: A number to specify the corner radius of the info view
  • withBackgroundColor(_ color: UIColor): To specify the background color of the dimmed view
    • Parameters:
      • color: A color to full view background color (e.g: UIColor.black.withAlphaComponent(0.7))
  • withCustomInfoView(_ view: CustomSpotlightInfoView): To show a custom info view
    • Parameters:
      • view: A view conforming to protocol CustomSpotlightInfoView, implementing function to listen to changes happening. (e.g: didMoveToItem)
  • addView(_ view: UIView, withInfo info: String? = nil, withUserInfo: [String: Any?] = [:], withCustomRadius radius: CGFloat? = nil): Add a spotlighted view (UIView) with parameters sent to customize it
    • Parameters:
      • view: A view that will be in the middle of the spotlight
      • info: Value of the string shown in the DefaultInfoView
      • userInfo: Dictionary containing any object that will be passed the the CustomSpotlightInfoView to maximize the customization
      • radius: If specified, the spotlight will be a full circle with the chosen radius, otherwise, the spotlight will be an ellipse circuling exactly the spotlightedView
  • addTabBarItem(at index: Int, in tabBar: UITabBar?, withInfo info: String? = nil, withUserInfo: [String: Any?] = [:], withCustomRadius radius: CGFloat? = nil): Add a tabBarItem by passing the tabBar with a specific index
    • Parameters:
      • index: An integer indicating the index of the chosen tabBarItem to highlight
      • tabBar: The tabBar containing the tabBarItem to highlight
      • info: Value of the string shown in the DefaultInfoView
      • userInfo: Dictionary containing any object that will be passed the the CustomSpotlightInfoView to maximize the customization
      • radius: If specified, the spotlight will be a full circle with the chosen radius, otherwise, the spotlight will be an ellipse circuling exactly the spotlightedView
  • wait(for string: String): To indicate to the library that it should wait for a certain event to happen (e.g: an API call) before continuing the journey
    • Parameters:
      • string: An identifier to wait for, to be sent with the 'continue' function
  • func continue(for string: String): To indicate to the library that the event that it was waiting for was done (e.g: an AI call)
    • Parameters:
      • string: The identifier used to wait, so the library knows that it can continue the journey
  • waitForInsertion(): To indicate to the library that other spotlighted views will be inserted and that it should wait for them (e.g: when navigating back and forth between ViewControllers holding spotlighted views in a journey)
  • insertView(_ view: UIView, withInfo info: String? = nil, withUserInfo: [String: Any?] = [:], withCustomRadius radius: CGFloat? = nil): Insert a spotlighted view (UIView) in an already running Spotlight journey with parameters sent to customize it
    • Parameters:
      • view: A view that will be in the middle of the spotlight
      • info: Value of the string shown in the DefaultInfoView
      • userInfo: Dictionary containing any object that will be passed the the CustomSpotlightInfoView to maximize the customization
      • radius: If specified, the spotlight will be a full circle with the chosen radius, otherwise, the spotlight will be an ellipse circuling exactly the spotlightedView
  • insertTabBarItem(at index: Int, in tabBar: UITabBar?, withInfo info: String? = nil, withUserInfo: [String: Any?] = [:], withCustomRadius radius: CGFloat? = nil): Insert a tabBarItem, in an already running Spotlight journey, by passing the tabBar with a specific index
    • Parameters:
      • index: An integer indicating the index of the chosen tabBarItem to highlight
      • tabBar: The tabBar containing the tabBarItem to highlight
      • info: Value of the string shown in the DefaultInfoView
      • userInfo: Dictionary containing any object that will be passed the the CustomSpotlightInfoView to maximize the customization
      • radius: If specified, the spotlight will be a full circle with the chosen radius, otherwise, the spotlight will be an ellipse circuling exactly the spotlightedView
  • show(): Add the previously added views and tabBarItems into the Spotlight journey, either added at the beginning or inserted in between (with insert functions)

Supported Items

  • UIView
  • TabBarItem

Documentation

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var label1: UILabel!
    @IBOutlet weak var button: UIButton!
    static let sundeedWalkthrough = SundeedWalkthrough()
    override func viewDidLoad() {
        super.viewDidLoad()
        ViewController.sundeedWalkthrough
            .withAbilityToTapThroughSpotlight(true)
            .withInfoCornerRadius(40)
            .withBackgroundColor(UIColor.black.withAlphaComponent(0.8))
            .withCustomInfoView(WalkthroughCustomView())
            .addView(label, withInfo: "1",
                            withCustomRadius: 80)
            .addTabBarItem(at: 1,
                           in: self.tabBarController?.tabBar,
                           withInfo: "2",
                           withCustomRadius: 40) // This will make the user tap on the second item in the tabBar, thus you will move to the secondViewController
            .waitForInsertion() // This will tell the library that second ViewController will insert some of its spotlightedViews here
            .addView(button, withInfo: "7")
            .wait(for: "label1")
            .addView(label1, withInfo: "8")
            .show()
    }
    
    @IBAction func didPress(_ sender: Any) {
        print("button pressed")  // This will be printed because abilityToTapThroughSpotlight was set to true
        DispatchQueue.main.asyncAfter(deadline: .now()+2) {
            self.label1.isHidden = false
            ViewController.sundeedWalkthrough.continue(for: "label1") // This shal tell the library to continue the flow after waiting 2 seconds
        }
    }
}

class SecondViewController: UIViewController {
    @IBOutlet weak var label1: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        ViewController.sundeedWalkthrough
            .insertView(self.label1, withInfo: "3")
            .insertTabBarItem(at: 0,
                              in: self.tabBarController?.tabBar,
                              withInfo: "6",
                              withCustomRadius: 40) // This will make the user tap on the tabBarItem at 0 and go back to the first ViewController
        .show() // This will tell the library to add label1 and tabBarItem at 0 to the flow in the current flow
    }
}

CustomSpotlightInfoView

An example for a custom info view instead of the default one

class WalkthroughCustomView: UIView, CustomWalkthroughInfoView {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    
    //manager variable is optional, you only need to add it if you wish to have a "Next" or "Skip" buttons and to control the flow manually.
    var manager: SpotlightInfoViewManager? = InfoViewManager()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required public init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        fromNib()
        self.backgroundColor = .clear // set to Clear to remove the borders and the small arrow pointing to the spotlighted view
    }
    func walkthroughIsWaitingForInsertion() {
        self.titleLabel.text = "Waiting..."
        self.subtitleLabel.text = ""
        self.descriptionLabel.text = ""
    }
    func walkthroughDoneWaitingForInsertion() {
        print("walkthroughDoneWaitingForInsertion")
    }
    func walkthroughDidMoveToItem(at index: Int,
                                  withInfo info: String?,
                                  withUserInfo userInfo: [String : Any?]) {
        self.descriptionLabel.text = info
        if let title = userInfo["title"] as? String,
            let subtitle = userInfo["subtitle"] as? String {
                self.titleLabel.text = title
                self.subtitleLabel.text = subtitle
        }
    }
    func walkthroughIsWaiting(for identifier: String) {
        print("waiting for API")
    }
    func walkthroughDidContinue(for identifier: String) {
        print("api responded and the flow continued")
    }
    @IBAction func skipButtonPressed(_ sender: Any) {
        self.manager?.skip()
    }
    
    @IBAction func nextButtonPressed(_ sender: Any) {
        self.manager?.next()
    }
}

License

MIT