Skip to content

Instantly share code, notes, and snippets.

@leoneparise
Created December 6, 2016 13:58
Show Gist options
  • Save leoneparise/24445282491af68d579cc1e9fdbc9cb3 to your computer and use it in GitHub Desktop.
Save leoneparise/24445282491af68d579cc1e9fdbc9cb3 to your computer and use it in GitHub Desktop.
Share View Controller Code
//
// ShareViewController.swift
// Kuoter
//
// Created by Leone Parise Vieira da Silva on 23/09/16.
// Copyright © 2016 Kuoter. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
import TTTAttributedLabel
import WebImage
import SVProgressHUD
import Photos
import DAKeyboardControl
import Appz
import FBSDKShareKit
import Async
import Photos
import Permission
import RxOptional
class ShareViewController: RxViewController<ShareViewModel>, CloseableViewControllerType {
@IBOutlet weak var contentView:UIView!
@IBOutlet weak var quoteImageView:UIImageView!
@IBOutlet weak var authorLabel:UILabel!
@IBOutlet weak var overlayView:UIView!
@IBOutlet weak var changeFontButton:UIButton!
@IBOutlet weak var changePicButton:UIButton!
@IBOutlet weak var toggleOverlayButton:UIButton!
@IBOutlet weak var decLineButton:UIButton!
@IBOutlet weak var decWidthButton:UIButton!
@IBOutlet weak var textAlignCenterButton:UIButton!
@IBOutlet weak var toggleAuthorButton:UIButton!
@IBOutlet weak var facebookShareButton:UIButton!
@IBOutlet weak var instagramShareButton:UIButton!
@IBOutlet weak var whatsappShareButton:UIButton!
@IBOutlet weak var messengerShareButton:UIButton!
@IBOutlet weak var saveButton:UIButton!
@IBOutlet weak var copyButton:UIButton!
@IBOutlet weak var shareButton:UIButton!
@IBOutlet weak var shareScrollView:UIScrollView!
@IBOutlet weak var sharePageControl:UIPageControl!
@IBOutlet weak var editView:EditTextAccessoryView!
@IBOutlet weak var toolsScrollView:UIScrollView!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
private var quoteLabel:TTTAttributedLabel!
private var doc:UIDocumentInteractionController!
private var pasteboard:UIPasteboard!
private var didAppear = false
private var fontPickerView:FontPickerView!
private var lineSpacePickerView:ArrayPickerView<CGFloat>!
private var widthPickerView:ArrayPickerView<Int>!
private var textAlignPickerView:ArrayPickerView<NSTextAlignment>!
override func viewDidLoad() {
title = "Share"
let fontToolbar = toolbar(title: "Font & Size", icon: R.image.changeFontButton())
fontPickerView = FontPickerView()
fontPickerView.accessoryView = fontToolbar
fontPickerView.add(toViewController: self)
let lineSpaceToolbar = toolbar(title: "Line Spacing", icon: R.image.lineSpaceDecButton())
lineSpacePickerView = ArrayPickerView<CGFloat>()
lineSpacePickerView.values = (-14).stride(through: 14, by: 2).map{ CGFloat($0) }
lineSpacePickerView.startValue = 0
lineSpacePickerView.titleForValue = { "\($0) pt" }
lineSpacePickerView.accessoryView = lineSpaceToolbar
lineSpacePickerView.add(toViewController: self)
let widthToolbar = toolbar(title: "Width", icon: R.image.widthDecButton())
widthPickerView = ArrayPickerView<Int>()
widthPickerView.values = 20.stride(through: 100, by: 5).map{ $0 }
widthPickerView.startValue = 100
widthPickerView.titleForValue = { "\($0) %" }
widthPickerView.accessoryView = widthToolbar
widthPickerView.add(toViewController: self)
let textAlignToolbar = toolbar(title: "Text Alignment", icon: R.image.centerAlignButton())
textAlignPickerView = ArrayPickerView<NSTextAlignment>()
textAlignPickerView.values = [.Left, .Center, .Right]
textAlignPickerView.startValue = .Center
textAlignPickerView.titleForValue = {
switch $0 {
case .Left: return "Left"
case .Center: return "Center"
case .Right: return "Right"
default: return ""
}
}
textAlignPickerView.accessoryView = textAlignToolbar
textAlignPickerView.add(toViewController: self)
// Connect editor tools
fontToolbar.nextField = lineSpacePickerView.hiddenTextField
lineSpaceToolbar.backField = fontPickerView.hiddenTextField
lineSpaceToolbar.nextField = widthPickerView.hiddenTextField
widthToolbar.backField = lineSpacePickerView.hiddenTextField
widthToolbar.nextField = textAlignPickerView.hiddenTextField
textAlignToolbar.backField = widthPickerView.hiddenTextField
pasteboard = UIPasteboard.generalPasteboard()
quoteLabel = TTTAttributedLabel(frame: CGRectZero)
quoteLabel.textColor = .whiteColor()
quoteLabel.numberOfLines = 0
quoteLabel.textAlignment = .Center
quoteLabel.lineBreakMode = .ByWordWrapping
quoteLabel.layer.cornerRadius = 5
quoteLabel.clipsToBounds = true
quoteLabel.hidden = true
self.contentView.addSubview(quoteLabel)
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
self.quoteLabel.addGestureRecognizer(pan)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(self.editQuote))
doubleTap.numberOfTapsRequired = 2
self.quoteLabel.addGestureRecognizer(doubleTap)
shareScrollView.rx_contentOffset.map{ $0.x }.subscribeNext {[unowned self] x in
let w = self.shareScrollView.frame.width
self.sharePageControl.currentPage = Int(round(x / w))
}.addDisposableTo(rx_disposeBag)
let didCropImage = { [weak self] (image:UIImage) -> Void in
self?.quoteImageView.image = image
UIApplication.topMostVC?.dismiss()
}
let didSelectImage = { (image:UIImage) -> Void in
if let parent = UIApplication.topMostVC as? UINavigationController {
Navigator.push(MainRoute.Crop(image: image, action: didCropImage), parent: parent)
}
}
changePicButton.rx_tap
.track(AppEvents.shareGalleryOpen)
.subscribeNext { [unowned self] in
if let parent = self.navigationController {
Navigator.present(MainRoute.Gallery(action: didSelectImage), parent: parent)
}
}.addDisposableTo(rx_disposeBag)
setupCloseButton()
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
self.view.addKeyboardPanningWithFrameBasedActionHandler(nil) {[unowned self] (keyboardRect, opening, closing) in
let bottomSpace = max(0, self.view.bounds.h - keyboardRect.y)
self.bottomConstraint.constant = bottomSpace
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
if closing {
self.editView.hidden = true
}
}
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if didAppear { return }
//showOverlay() TODO: Enable when I develop better overlays
viewModel.initWidth(contentView.frame.w - 20)
updateLabelSize(width: contentView.frame.w - 20)
quoteLabel.center = contentView.center
quoteLabel.hidden = false
didAppear = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.view.removeKeyboardControl()
}
func handlePan(sender: UIPanGestureRecognizer) {
if sender.state == .Began {
self.showLabel()
} else if sender.state == .Ended {
self.hideLabel()
}
let translation = sender.translationInView(self.view)
sender.view!.center = CGPoint(x: sender.view!.center.x + translation.x,
y: sender.view!.center.y + translation.y)
sender.setTranslation(CGPointZero, inView: self.view)
}
func updateLabelSize(width width:CGFloat? = nil) {
// Center quote label
let frame = quoteLabel.frame
let size = quoteLabel.sizeThatFits(CGSize(width: width ?? frame.width, height: CGFloat.max))
let w = min(contentView.frame.w, width ?? frame.width)
quoteLabel.preferredMaxLayoutWidth = w
quoteLabel.frame = CGRect(x: frame.x + (frame.w - w) / 2,
y: frame.y,
width: w,
height: size.height)
}
override func bindToViewModel() {
changeFontButton.rx_tap
.track(AppEvents.shareFontChange)
.subscribeNext { [unowned self] in
self.fontPickerView.show()
}.addDisposableTo(rx_disposeBag)
decLineButton.rx_tap
.track(AppEvents.shareLineSpacingChange)
.subscribeNext {[unowned self] in
self.lineSpacePickerView.show()
}.addDisposableTo(rx_disposeBag)
decWidthButton.rx_tap
.track(AppEvents.shareWidthChange)
.subscribeNext{[unowned self] in
self.widthPickerView.show()
}.addDisposableTo(rx_disposeBag)
textAlignCenterButton.rx_tap
.track(AppEvents.shareTextAlignmentChange)
.subscribeNext{[unowned self] in
self.textAlignPickerView.show()
}.addDisposableTo(rx_disposeBag)
// INPUTS
viewModel.quote.bindTo(editView.rx_text)
.addDisposableTo(rx_disposeBag)
editView.rx_text.bindTo(viewModel.inputs.quote)
.addDisposableTo(rx_disposeBag)
lineSpacePickerView.rx_valueSelected
.bindTo(viewModel.inputs.lineSpace)
.addDisposableTo(rx_disposeBag)
widthPickerView.rx_valueSelected
.bindTo(viewModel.inputs.width)
.addDisposableTo(rx_disposeBag)
textAlignPickerView.rx_valueSelected
.bindTo(viewModel.inputs.textAlignment)
.addDisposableTo(rx_disposeBag)
toggleAuthorButton.rx_tap
.track(AppEvents.shareAuthorToggle)
.bindTo(viewModel.inputs.toggleAuthor)
.addDisposableTo(rx_disposeBag)
toggleOverlayButton.rx_tap
.track(AppEvents.shareOverlayToggle)
.bindTo(viewModel.inputs.toggleOverlay)
.addDisposableTo(rx_disposeBag)
facebookShareButton.rx_tap
.track(AppEvents.shareWithFacebook)
.bindTo(viewModel.inputs.facebookShare)
.addDisposableTo(rx_disposeBag)
messengerShareButton.rx_tap
.track(AppEvents.shareWithMessenger)
.bindTo(viewModel.inputs.messengerShare)
.addDisposableTo(rx_disposeBag)
whatsappShareButton.rx_tap
.track(AppEvents.shareWithWhatsapp)
.bindTo(viewModel.inputs.whastappShare)
.addDisposableTo(rx_disposeBag)
instagramShareButton.rx_tap
.track(AppEvents.shareWithInstagram)
.bindTo(viewModel.inputs.instagramShare)
.addDisposableTo(rx_disposeBag)
copyButton.rx_tap
.track(AppEvents.shareQuoteCopy)
.bindTo(viewModel.inputs.copyShare)
.addDisposableTo(rx_disposeBag)
shareButton.rx_tap
.track(AppEvents.shareMoreActions)
.bindTo(viewModel.inputs.moreShare)
.addDisposableTo(rx_disposeBag)
saveButton.rx_tap
.track(AppEvents.shareImageSave)
.bindTo(viewModel.inputs.saveShare)
.addDisposableTo(rx_disposeBag)
// OUTPUTS
viewModel.quote.bindTo(rx_quote)
.addDisposableTo(rx_disposeBag)
viewModel.author.bindTo(authorLabel.rx_text)
.addDisposableTo(rx_disposeBag)
viewModel.font.bindTo(rx_quoteFont)
.addDisposableTo(rx_disposeBag)
// The font output must be binded first to change the font picker view state
viewModel.font.bindTo(fontPickerView.rx_fontSelected)
.addDisposableTo(rx_disposeBag)
// Bind font picker view to view model input
fontPickerView.rx_fontSelected.mapToOptional()
.bindTo(viewModel.inputs.font)
.addDisposableTo(rx_disposeBag)
viewModel.lineSpace.bindTo(rx_lineSpace)
.addDisposableTo(rx_disposeBag)
viewModel.image.bindTo(quoteImageView.rx_image)
.addDisposableTo(rx_disposeBag)
viewModel.width.bindTo(rx_width)
.addDisposableTo(rx_disposeBag)
viewModel.overlayLevel.bindTo(rx_overlayLevel)
.addDisposableTo(rx_disposeBag)
viewModel.authorVisible.bindTo(rx_authorVisible)
.addDisposableTo(rx_disposeBag)
viewModel.textAlignment.bindTo(rx_textAlignment)
.addDisposableTo(rx_disposeBag)
viewModel.facebookShare.subscribeNext {[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
Async.main(after: 1){
self?.showFacebookShare(self?.contentView.takeSnapshot())
}
}.addDisposableTo(rx_disposeBag)
viewModel.messengerShare.subscribeNext {[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
Async.main(after: 1){
self?.showMessengerShare(self?.contentView.takeSnapshot())
}
}.addDisposableTo(rx_disposeBag)
viewModel.whatsappShare.subscribeNext {[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
Async.main(after: 1){
self?.showWhatsappShare(self?.contentView.takeSnapshot())
}
}.addDisposableTo(rx_disposeBag)
viewModel.instagramShare.subscribeNext {[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
Async.main(after: 1){
self?.showInstagramShare(self?.contentView.takeSnapshot())
}
}.addDisposableTo(rx_disposeBag)
viewModel?.copyShare.subscribeNext{[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
}.addDisposableTo(rx_disposeBag)
viewModel?.moreShare.subscribeNext{[weak self] (quote, author) in
self?.copyQuote(quote:quote, author: author)
Async.main(after: 1){
self?.showMoreShare(self?.contentView.takeSnapshot())
}
}.addDisposableTo(rx_disposeBag)
viewModel?.saveShare.subscribeNext{[weak self] (quote, author) in
self?.requestAndSaveImage(self?.contentView.takeSnapshot())
}.addDisposableTo(rx_disposeBag)
super.bindToViewModel()
}
private func animateTools() {
UIView.animateWithDuration(0.3, delay: 0.3, options: [], animations: { [unowned self] in
self.toolsScrollView.contentOffset = CGPoint(x: 60, y: 0)
}, completion: { [unowned self] _ in
self.toolsScrollView.setContentOffset(CGPointZero, animated: true)
})
}
private func flashLabel() {
UIView.animateWithDuration(0.1, animations: { [unowned self] in
self.quoteLabel.layer.backgroundColor = UIColor(white: 1, alpha: 0.3).CGColor
}, completion: { _ in
UIView.animateWithDuration(0.5) { [unowned self] in
self.quoteLabel.layer.backgroundColor = UIColor(white: 1, alpha: 0.0).CGColor
}
})
}
private func showLabel() {
UIView.animateWithDuration(0.4) { [unowned self] in
self.quoteLabel.layer.backgroundColor = UIColor(white: 1, alpha: 0.3).CGColor
}
}
private func hideLabel() {
UIView.animateWithDuration(0.4) { [unowned self] in
self.quoteLabel.layer.backgroundColor = UIColor(white: 1, alpha: 0.0).CGColor
}
}
@objc private func editQuote() {
track(AppEvents.shareQuoteEdit)
self.editView.hidden = false
self.editView.startEditing()
}
private func showOverlay() {
if AppSession().didShowShareOverlay { return }
if let parent = UIApplication.topMostVC {
Async.main(after:1) {
Navigator.present(MainRoute.ShareOverlay, parent: parent, wrap: false, transitionStyle: .CrossDissolve)
}
}
}
private func toolbar(title title:String, icon:UIImage?) -> EditorToolbar {
let toolbar = EditorToolbar(frame: CGRect(x: 0, y: 0, width: self.view.w, height: 44))
toolbar.title = title
toolbar.icon = icon
return toolbar
}
}
// MARK: Share Extensions
extension ShareViewController {
private func copyQuote(quote quote:String, author:String) {
pasteboard.string = "\(quote) - \(author)"
SVProgressHUD.showSuccessWithStatus("Quote copied!")
}
private func showFacebookShare(image:UIImage?) {
let photo = FBSDKSharePhoto(image: image!, userGenerated: true)
let content = FBSDKSharePhotoContent()
content.photos = [photo]
let dialog = FBSDKShareDialog()
dialog.fromViewController = self
dialog.shareContent = content
dialog.mode = .Native
dialog.show()
}
private func showMessengerShare(image:UIImage?) {
let photo = FBSDKSharePhoto(image: image!, userGenerated: true)
let content = FBSDKSharePhotoContent()
content.photos = [photo]
FBSDKMessageDialog.showWithContent(content, delegate: nil)
}
private func showMoreShare(image:UIImage?) {
if
let imageURL = image?.saveAsJPEG(withName: "share.jpg") {
doc = UIDocumentInteractionController(URL: imageURL)
doc.presentOpenInMenuFromRect(self.view.bounds, inView: self.view, animated: true)
}
}
private func showWhatsappShare(image:UIImage?) {
if
let imageURL = image?.saveAsJPEG(withName: "share.wai"),
let appURL = NSURL(string:"whatsapp://app")
where UIApplication.sharedApplication().canOpenURL(appURL) {
doc = UIDocumentInteractionController(URL: imageURL)
doc.UTI = "net.whatsapp.image"
doc.presentOpenInMenuFromRect(self.view.bounds, inView: self.view, animated: true)
}
}
private func showInstagramShare(image:UIImage?) {
if
let imageURL = image?.saveAsJPEG(withName: "igo"),
let appURL = NSURL(string:"instagram://app")
where UIApplication.sharedApplication().canOpenURL(appURL) {
doc = UIDocumentInteractionController(URL: imageURL)
doc.UTI = "com.instagram.exclusivegram"
doc.presentOpenInMenuFromRect(self.view.bounds, inView: self.view, animated: true)
}
}
private func requestAndSaveImage(imageOrNil:UIImage?) {
if Permission.Photos.status == .Authorized {
if let image = imageOrNil {
self.saveImage(image)
}
} else {
Permission.Photos.request{[weak self] (status) in
if let image = imageOrNil where status == .Authorized {
self?.saveImage(image)
}
}
}
}
private func saveImage(image:UIImage) {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromImage(image)
}, completionHandler: { (success, error) in
if success {
SVProgressHUD.showSuccessWithStatus("Image saved!")
} else {
let alert = UIAlertController(title: "Could not save image to gallery",
message:"An error happened when we tried to save the image into your gallery. Try again later.",
preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Cancel, handler: nil))
self.navigationController?.presentViewController(alert, animated: true, completion: nil)
}
})
}
}
// MARK: Rx Extensions
extension ShareViewController {
var rx_quote:AnyObserver<String> {
return UIBindingObserver(UIElement: self) { (vc, text) in
vc.quoteLabel.text = text
vc.updateLabelSize()
}.asObserver()
}
var rx_quoteFont:AnyObserver<UIFont> {
return UIBindingObserver(UIElement: self){ (vc, font) in
vc.quoteLabel.font = font
vc.quoteLabel.text = vc.quoteLabel.text
vc.updateLabelSize(width: vc.quoteLabel.frame.w)
vc.flashLabel()
}.asObserver()
}
var rx_lineSpace:AnyObserver<CGFloat> {
return UIBindingObserver(UIElement: self){ (vc, space) in
vc.quoteLabel.lineSpacing = space
vc.quoteLabel.text = vc.quoteLabel.text
vc.updateLabelSize(width: vc.quoteLabel.frame.w)
vc.flashLabel()
}.asObserver()
}
var rx_width:AnyObserver<CGFloat> {
return UIBindingObserver(UIElement: self){ (vc, width) in
vc.updateLabelSize(width: width)
vc.flashLabel()
}.asObserver()
}
var rx_overlayLevel:AnyObserver<Int> {
return UIBindingObserver(UIElement: self) { (vc, level) in
let overlayIcon = { () -> UIImage? in
switch level {
case 1: return R.image.toggleOverlay25Button()
case 2: return R.image.toggleOverlay50Button()
case 3: return R.image.toggleOverlay75Button()
default: return R.image.toggleOverlay0Button()
}
}()
vc.toggleOverlayButton.setImage(overlayIcon, forState: .Normal)
UIView.animateWithDuration(0.3){
vc.overlayView.backgroundColor = UIColor.blackColor()
.colorWithAlphaComponent(0.25 * CGFloat(level))
}
}.asObserver()
}
var rx_authorVisible:AnyObserver<Bool> {
return UIBindingObserver(UIElement: self) { (cell, visible) in
cell.authorLabel.hidden = !visible
cell.toggleAuthorButton.tintColor = visible ? UIColor.flatSkyBlueColorDark() : UIColor.lightGrayColor()
}.asObserver()
}
var rx_textAlignment:AnyObserver<NSTextAlignment> {
return UIBindingObserver(UIElement: self) { (vc, alignment) in
vc.quoteLabel.textAlignment = alignment
let image = { Void -> UIImage? in
switch alignment {
case .Left: return R.image.leftAlignButton()
case .Center: return R.image.centerAlignButton()
case .Right: return R.image.rightAlignButton()
default: return nil
}
}()
vc.textAlignCenterButton.setImage(image, forState: .Normal)
}.asObserver()
}
}
class ShareViewModel:RxViewModel {
let inputs = (
quote:Variable(""),
font:Variable<UIFont?>(nil),
lineSpace:Variable<CGFloat>(0.0),
width:Variable<Int>(100),
textAlignment:Variable<NSTextAlignment>(.Center),
toggleAuthor:PublishSubject<Void>(),
toggleOverlay:PublishSubject<Void>(),
facebookShare:PublishSubject<Void>(),
instagramShare:PublishSubject<Void>(),
whastappShare:PublishSubject<Void>(),
messengerShare:PublishSubject<Void>(),
copyShare:PublishSubject<Void>(),
saveShare:PublishSubject<Void>(),
moreShare:PublishSubject<Void>()
)
var quote:Observable<String>!
var author:Observable<String>!
var authorVisible:Observable<Bool>!
var font:Observable<UIFont>!
var lineSpace:Observable<CGFloat>!
var width:Observable<CGFloat>!
var overlayLevel:Observable<Int>!
var image:Observable<UIImage?>!
var textAlignment:Observable<NSTextAlignment>!
var facebookShare:Observable<(quote:String, author:String)>!
var instagramShare:Observable<(quote:String, author:String)>!
var whatsappShare:Observable<(quote:String, author:String)>!
var messengerShare:Observable<(quote:String, author:String)>!
var copyShare:Observable<(quote:String, author:String)>!
var saveShare:Observable<(quote:String, author:String)>!
var moreShare:Observable<(quote:String, author:String)>!
private let authorVar = Variable("")
private let overlayLevelVar = Variable(1)
private let authorVisibleVar = Variable(true)
private let imageVar = Variable<UIImage?>(nil)
private var maxWidth:CGFloat = 0.0
private let fontIndex = Variable(0)
init(quote:String, author:String, font:UIFont, image:UIImage) {
super.init()
inputs.quote.value = quote
authorVar.value = author
inputs.font.value = font
imageVar.value = image
self.quote = inputs.quote.asObservable().distinctUntilChanged()
self.author = authorVar.asObservable()
self.font = inputs.font.asObservable().filterNil()
self.lineSpace = inputs.lineSpace.asObservable()
self.width = inputs.width.asObservable()
.map{[unowned self] w in self.maxWidth * CGFloat(w) / 100.0 }
self.overlayLevel = overlayLevelVar.asObservable()
self.authorVisible = authorVisibleVar.asObservable()
self.image = imageVar.asObservable()
self.textAlignment = inputs.textAlignment.asObservable()
let quoteAndAuthor$ = Observable.combineLatest(self.quote, self.author) { (quote:$0, author:$1) }
inputs.toggleAuthor.withLatestFrom(self.authorVisible)
.subscribeNext{ [unowned self] visible in
self.authorVisibleVar.value = !visible
}.addDisposableTo(rx_disposeBag)
inputs.toggleOverlay.withLatestFrom(self.overlayLevel)
.subscribeNext{ [unowned self] level in
self.overlayLevelVar.value = level == 3 ? 0 : level + 1
}.addDisposableTo(rx_disposeBag)
facebookShare = inputs.facebookShare.withLatestFrom(quoteAndAuthor$)
instagramShare = inputs.instagramShare.withLatestFrom(quoteAndAuthor$)
messengerShare = inputs.messengerShare.withLatestFrom(quoteAndAuthor$)
whatsappShare = inputs.whastappShare.withLatestFrom(quoteAndAuthor$)
copyShare = inputs.copyShare.withLatestFrom(quoteAndAuthor$)
saveShare = inputs.saveShare.withLatestFrom(quoteAndAuthor$)
moreShare = inputs.moreShare.withLatestFrom(quoteAndAuthor$)
}
func initWidth(width:CGFloat) {
self.maxWidth = width
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment