Skip to main content
Version: 1.5.x

ThumbView

This option allows you to display our custom widget that display stories in an horizontal collectionView with an Instagram-like UI.

Layout

Using BasicThumbViewController

To add a widget, you can use a BasicThumbViewController and call startThumbView with your JoinStoriesThumbConfig

class YourThumbViewController: UIViewController {
let thumbView = BasicThumbViewController()
let config = JoinStoriesThumbConfig(alias: "<YOUR_ALIAS>", ...)

override func viewDidLoad() {
...
thumbView.startThumbView(config: config)
}
}

With SwiftUI, you need to bridge BasicThumbViewController with the protocol UIViewControllerRepresentable

struct YourThumbViewController: UIViewControllerRepresentable {

var config: JoinStoriesThumbConfig!

func makeUIViewController(context: Context) -> BasicThumbViewController {
let thumbViewController = BasicThumbViewController()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
thumbViewController.collectionView.setCollectionViewLayout(layout, animated: true)
return thumbViewController
}

func updateUIViewController(_ viewController: BasicThumbViewController, context: Context) {
viewController.startThumbView(config: config, onSuccess: {
...
}, onError: { error in
...
})
}
}

struct ContentView: View {
let config = JoinStoriesThumbConfig(alias: "<YOUR_ALIAS>", ...)

var body: some View {
VStack {
YourThumbViewController(config: config)
.frame(height: 150)
}
}
}

Optimize edges (available from v1.4.3)

To improve margin management, you can use the closure insetsProvider to dynamically add margins to the widget. If this doesn't work for you, you can override the collectionView(_:layout:insetForSectionAt:) method

let thumbView = BasicThumbViewController()
...
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.horizontalSizeClass == .compact {
thumbView.insetsProvider = {
return UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
}
} else {
thumbView.insetsProvider = {
return UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
}
}
}

By subclassing BasicThumbViewController

If you want to add another view in your widget, you can subclass your custom controller with BasicThumbViewController.

class YourCustomThumbViewController: BasicThumbViewController {

var activityIndicator = UIActivityIndicatorView()

override func viewDidLoad() {
super.viewDidLoad()

activityIndicator.startAnimating()
self.view.addSubview(activityIndicator)
}
}

By subclasssing StoryTransitionViewController

When working with custom widget, we are able to apply custom transition between stories collectionView (thumbnails) cells and stories player. To achieve this, you then need to inherits from a class called StoryTransitionViewController

Because you'll have an inherited CollectionView to display Thumbnails, you will need to implement methods from UICollectionViewDelegateFlowLayout and UICollectionViewDataSource (but UICollectionViewDelegate already being implemented by the parent class)

  • UICollectionViewDelegateFlowLayout should be implemented to match StoryViewConfig parameters.
public func collectionView(_: collectionViewLayout: sizeForItemAt:)
public func collectionView(_: collectionViewLayout: minimumLineSpacingForSectionAt:) -> CGFloat
  • Inside viewDidLoad, add viewController as dataSource of your collectionView
collectionView.dataSource = self
  • You also need to set the JoinStoriesThumbConfig instance you created
let config = JoinStoriesThumbConfig(...)
self.config = config
  • For UICollectionViewDataSource, implement numberOfItemsInSection and func collectionView(_:, cellForItemAt:)

Without subclasssing StoryTransitionViewController

  1. Create your custom ViewController and add protocol conformance with CollectionView
class YourViewController: StoryTransitionViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout 
  1. Add var stories: [StoryValue] = [] variable for stories loading
  2. Add collectionView variable and register StoryCollectionViewCell
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.showsHorizontalScrollIndicator = false
cv.showsVerticalScrollIndicator = false
cv.register(StoryCollectionViewCell.self, forCellWithReuseIdentifier: StoryCollectionViewCell.reuseIdentifier)
return cv
}()
  1. Inside viewDidLoad, add viewController as delegate and datasource of your collectionView
 override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// Add view controller as DataSource of `collectionView`
collectionView.dataSource = self
// Add view controller as Delegate of `collectionView`
collectionView.delegate = self
collectionView.backgroundColor = .black
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: "your_top_anchor_constant") .isActive = true
}
  1. Implement UICollectionViewDelegateFlowLayout methods to match storyViewConfig parameters.
public func collectionView(_: collectionViewLayout: sizeForItemAt:)
public func collectionView(_: collectionViewLayout: minimumLineSpacingForSectionAt:) -> CGFloat
  1. Implement UICollectionViewDataSource methods to provide numberOfItemsInSection and func collectionView(_:, cellForItemAt:)

  2. Implement UICollectionViewDelegate methods to present StoryPlayer by calling custom presentStoryPlayer(stories:beginningAt:animated:)

public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
presentStoryPlayer(stories: stories, beginningAt: indexPath, animated: true)
}
  1. Conforms to StoryNavigationDelegate and implement markAsSeen fonctionnality
public func storyItemShown(at indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? StoryCollectionViewCell else {
return
}
let storyAtIndex = stories[indexPath.row]
cell.storyPersistence.markAsSeen(id: storyAtIndex.id)
collectionView.reloadItems(at: [indexPath])
}

Trigger

To start the display of our thumbView widget, you need to call (in viewDidLoad for example):

JoinStories.startThumbView(config: "your_thumbView_config") { [weak self] result in
switch result {
case .success(let stories):
// Assign stories from result (Result<[StoryValue], StoriesAPIError>)
self?.stories = stories
// Refresh your view on the main thread
DispatchQueue.main.async {
// self?.collectionView.reloadData()
}
case .failure(let error):
// Handle error (by displaying a message for instance)
...
}
}

If you want to refresh stories when we came from background, you can add an observer inside viewDidLoad method as follow

 NotificationCenter.default.addObserver(self, selector: #selector(_your_method_name), name: UIApplication.willEnterForegroundNotification, object: nil)

Configuration

You can fine tune SDK behavior and UI, by specifying optional values on JoinStoriesThumbConfig.

Available configuration parameters:

NameTypeDefault ValueDescription
aliasStringYESThe Join Stories alias you were given to configure your integration
requestTimeoutIntervalTimeInterval60Timeout used for the stories fetching network requests (call and read)
fontNameStringHelveticaNeue-BoldThumb views label custom font (withLabel should be true)
labelColorUIColorUIColor.blackThumb views label custom text color (withLabel should be true)
thumbViewSpacingInt32Spacing between thumb views
withLabelBooltrueShow/hide text below each thumb view
loaderInnerViewWidthInt8Spacing between the image and the loader in a thumb view
loaderInnerViewColor[UIColor][UIColor.black]Color between the image and the loader in a thumb view
loaderColors[UIColor][UIColor.red, UIColor.blue]Colors of the thumb view loader. A sweep gradient will be applied on it
loaderWidthInt8Width of the thumb view loader
storyViewedIndicatorColorUIColorUIColor.grayColor of the thumb view indicator displayed when a story has been viewed by the user
storyViewedIndicatorAlphaFloat0.8Alpha of the thumb view indicator displayed when a story has been viewed by the user. Range [0..1]
thumbViewOverlayColorUIColorUIColor(hex8: 0x4C4C4CBB)Color of the overlay displayed on the thumb view image when a story was selected by the user and is loading
playerBackgroundColorUIColorUIColor.blackColor of the player's background, if the player height is smaller than screen dimensions
playerVerticalAnchorPlayerVerticalAnchor.bottomIf the player's height is smaller than screen height, anchor it to the top or bottom of the screen
playerShowShareButtonBooleantrueChange the visibility of the share button displayed on the Player view
playerClosingButtonBooleantrueChange the visibility of the close button displayed on the Player view
playerHorizontalMarginsInt0Apply the margin to the left and right of the player view. To maintain ratio, it also changes the height of the player view.
playerCornerRadiusInt0Apply radius to the 4 corners of the player view
playerProgressBarDefaultColorString?nilChange the background color of the progress bar in the player view. If no value or nil is filled, the default color will be used. Format is ##RRGGBB or #RRGGBBAA
playerProgressBarFillColorString?nilChange the fill color of the progress bar in the player view. If no value or nil is filled, the default color will be used. Format is ##RRGGBB or #RRGGBBAA
playerProgressBarThicknessInt?nilChange the thickness of the progress bar in the player view. If no value or nil is filled, the default thickness will be used.
playerProgressBarRadiusInt?nilChange the radius of the progress bar in the player view. If no value or nil is filled, the default thickness will be used. Radius will be applied to the 4 corners.

Multiple widgets

You can implement our thumbView widget multiple times on the same screen, with the same or different join_alias.

To do so, you just need to call the startThumbView method n times.