Start playling Lottie once collection view is changed - ios

I have problem with lottie animations, I have some kind of onboarding on my app and what I would like to achive is to everytime view in collectionview is changed, to start my animation, I have 4 pages and 4 different lottie animations. Currently if I call animation.play() function, once app is started, all of my animations are played at the same time, so once I get to my last page, animation is over. And I want my lottie to be played only once, when view is shown.
This is my cell
class IntroductionCollectionViewCell: UICollectionViewCell {
#IBOutlet var title: UILabel!
#IBOutlet var subtitleDescription: UILabel!
#IBOutlet var animationView: AnimationView!
override func awakeFromNib() {
super.awakeFromNib()
}
public func configure(with data: IntroInformations) {
let animation = Animation.named(data.animationName)
title.text = data.title
subtitleDescription.text = data.description
animationView.animation = animation
}
static func nib() -> UINib {
return UINib(nibName: "IntroductionCollectionViewCell", bundle: nil)
}
}
This is how my collection view is set up
extension IntroductionViewController {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
pages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "IntroductionCollectionViewCell", for: indexPath) as! IntroductionCollectionViewCell
cell.configure(with: pages[indexPath.row])
cell.animationView.loopMode = .loop
cell.animationView.play()
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let scrollPos = scrollView.contentOffset.x / view.frame.width
self.pageControl.currentPage = Int(floorf(Float(scrollPos)))
let n = pageControl.numberOfPages
if self.pageControl.currentPage == n - 1 {
continueButton.isHidden = false
} else {
continueButton.isHidden = true
}
}
}
Thanks in advance!!

You can use the collectionViewDelegate willDisplay and didEndDisplaying methods to start/stop the animations. And not when configuring the cell. - https://developer.apple.com/documentation/uikit/uicollectionviewdelegate
If you want the animation to run only once dont use the loop option.

if let cell = cell as? IntroductionCollectionViewCell {
cell.animationView.loopMode = .playOnce
cell.animationView.play()
}
this is the answer, I need to check is cell once is loaded my cell where lottie animation is

Related

Collection view auto resizing and setup repeating background

I am using collection view to load data from API. Here i want to extend size of collection view height instead of scrolling inside collection view. And also need to repeat the background image according to collection view height.
Here is the android layout and i want to develop similar to this.Tap here
import UIKit
import Nuke
import SVProgressHUD
import JSONJoy
class HomeViewController: UIViewController {
#IBOutlet weak var categoryCollection: UICollectionView!
#IBOutlet weak var tabbar: UITabBar!
var sectors:[Sector] = []
var timer = Timer()
var counter = 0
var selectedSector = ""
var selectedSectorName = ""
var webService = ApiService()
let plist = PlistHelper()
override func viewDidLoad() {
super.viewDidLoad()
self.categoryCollection.dataSource = self
self.categoryCollection.delegate = self
for item in tabbar.items ?? []{
item.image = item.image?.withRenderingMode(.alwaysOriginal)
}
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .selected)
listSectors()
self.categoryCollection.backgroundColor = UIColor(patternImage: UIImage(named: "bg")!)
}
override func viewWillAppear(_ animated: Bool) {
listbanners()
}
override func viewWillDisappear(_ animated: Bool) {
self.timer.invalidate()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "sectors"){
let vc = segue.destination as! SectorsViewController
vc.sectorCode = selectedSector
vc.sectorName = selectedSectorName
}
}
func listSectors(){
webService.listSectors({ (sectors, message, status) in
if(status){
if let resData = sectors.arrayObject {
do{
for data in resData{
self.sectors.append(try Sector(JSONLoader(data)))
}
DispatchQueue.main.async {
self.categoryCollection.reloadData()
}
}
catch let error {
print("JSonJoyError:\(error)")
}
}
}
})
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(collectionView == bannerCollection){
return banners.count
}
else {
return sectors.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let options = ImageLoadingOptions(placeholder: UIImage(named: "bannerPlaceholder"),transition: .fadeIn(duration: 0.33))
if(collectionView == bannerCollection){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! DataCollectionViewCell
Nuke.loadImage(with: URL(string: banners[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.img)
return cell
}
else{
let cell = categoryCollection.dequeueReusableCell(withReuseIdentifier: "catCell", for: indexPath) as! catogeryCollectionViewCell
Nuke.loadImage(with: URL(string: sectors[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.photoImageView)
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if(collectionView == categoryCollection){
selectedSector = sectors[indexPath.row].Code ?? "FOOD"
selectedSectorName = sectors[indexPath.row].Name ?? "FOOD"
self.performSegue(withIdentifier: "sectors", sender: self)
}
}
}
extension HomeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if(collectionView == bannerCollection){
let size = bannerCollection.frame.size
return CGSize(width: size.width, height: size.height - 10)
}
else{
let size = categoryCollection.frame.size
print("size\(size)")
return CGSize(width: (size.width / 2) - 8, height:120)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 30
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
Following steps can help you to increase your collection View height according to data.
Create Heightconstraint outlet.
After loading data in collection view with delay of 0.2 sec in main thread,
Set Height Constraint constant = collection view content size height.

Scrolling issue with UITableViewController hosted in UICollectionViewCell

I have the following setup in my app:
UITabBarController
UINavigationController
UIViewController
The UIViewController has a UICollectionView with horizontal scrolling.
In the cells, I want to "host" a view from another ViewController. This works pretty well, but I have scrolling issues. The first UICollectionViewCell hosts a view that comes from a UITableViewController. I can scroll the UITableViewController but it does not really scroll to the end - it seems like the UITableViewController starts to bounce way too early.
When I used the UITableViewController as the Root View Controller, everything worked fine, so I don't think there is something wrong with this ViewController.
The height of the CollectionView is pretty small, I just wanted to show the "bouncing" behaviour.
Here is the code for the collectionView:
import Foundation
import UIKit
class FeedSplitViewController : UIViewController, Controllable
{
#IBOutlet weak var menuBar: MenuBar!
#IBOutlet weak var collectionView: UICollectionView!
private var currentIndex = 0
private var dragStart: CGFloat = 0.0
private var feedActivities: FeedViewController!
var controller: Controller!
override func viewDidLoad()
{
super.viewDidLoad()
self.initControls()
self.initMenuBar()
self.initCollectionView()
self.initActivitiesViewController()
}
fileprivate func initActivitiesViewController()
{
self.feedActivities = UIStoryboard.instantiate("Main", "feedActivities")
self.feedActivities.controller = self.controller
}
fileprivate func initControls()
{
self.navigationController?.navigationBar.setValue(false, forKey: "hidesShadow")
}
fileprivate func initMenuBar()
{
self.menuBar.showLine = true
self.menuBar.enlargeIndicator = true
self.menuBar.texts = [Resources.get("FEED_ACTIVITIES"), Resources.get("DASHBOARD")]
self.menuBar.selectionChanged =
{
index in
self.collectionView.scrollToItem(at: IndexPath(item: index, section: 0), at: UICollectionView.ScrollPosition.right, animated: true)
}
}
fileprivate func initCollectionView()
{
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
let menuBarFrame = self.menuBar.frame.origin
let collectionView = self.collectionView.frame.origin
Swift.print(menuBarFrame)
Swift.print(collectionView)
}
}
extension FeedSplitViewController : UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
if indexPath.item == 0, let feedActivities = self.feedActivities
{
cell.contentView.addSubview(feedActivities.view)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat
{
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: self.view.bounds.width, height: self.view.bounds.height)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView)
{
self.dragStart = scrollView.contentOffset.x
}
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let oldIndex = self.currentIndex
let page = scrollView.contentOffset.x / scrollView.frame.size.width
let currentPage = Int(round(page))
if oldIndex != currentPage
{
if Settings.useHapticFeedback
{
Utilities.haptic(.medium)
}
self.menuBar.selectedIndex = currentPage
}
self.currentIndex = currentPage
}
}
I have attached a small video: https://imgur.com/a/pj7l3Hd
I solved it by doing the following:
I no longer host the view of an ViewController directly in the
Every UICollectionView cell hosts an UITableView.
The UITableViewCell contains the data model that was previously implemented in the ViewController. The logic is still outside of the UITableViewCell.

I want create this Sliding UIVies in xcode using swift, what sholud i used ? an collecntionView or TableView?

i want to create an exact screen in my ios application with xcode & swift im not able to create it, what shall i used? an collectionView or ScroolView?
import UIKit
class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
#IBOutlet weak var newCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
newCollectionView.delegate = self
newCollectionView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
It is my class. It works completely. Also i added an image of storyboard. Link to my github project : https://github.com/Agisight/scroller.git
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scroll: UIScrollView!
#IBOutlet var btns: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
scroll.delegate = self
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
methodAnimation()
}
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
methodAnimation()
}
func methodAnimation() {
if btns.count == 0 {return}
var i = 0
// i – calculate it, it is your visible/desirable view.
// here is variant of code
let dragOffset = view.frame.width * 0.5 // callibrate it for you
let offX = scroll.contentOffset.x
i = Int(offX + dragOffset) / Int(view.frame.width)
i = max(0, min(i, btns.count - 1))
let yourX = btns[i].frame.width * CGFloat(i) // ()
let p = CGPoint(x: yourX, y: 0);
scroll.setContentOffset(p, animated: true)
}
}

Call collectionView(_ :layout:sizeForItemAt:) in Swift 4

I made the gallery using UICollectionView.
If you lower the screen, the image should continue to come out and stop at 20.
The code was constantly updated.
However, the function collectionView (_: layout: sizeForItemAt :) that sets the size of the cell did not work.
Therefore, only 20 images are displayed continuously.
On viewDidLoad()
if let layoutt = artCollectionView?.collectionViewLayout as? PinterestLayout
{
layoutt.delegate = self
}
cell setting
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as? HomeCollectionViewCell,
let arts = self.artList else { return HomeCollectionViewCell() }
if arts.count > indexPath.row
{
let model = arts[indexPath.row]
cell.imgView.sd_setImage(with: URLHelper.createEncodedURL(url: model.thumburl), completed: nil)
}
return cell
}
return cell size
extension HomeViewController : PinterestLayoutDelegate
{
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
{
return CGFloat(300.0)
}
}
Currently, the function only executes once when the app starts. Do you know how to run that function every time I want?
You need to implement UICollectionViewDelegateFlowLayout.
You can read more about the protocol here:
UICollectionViewDelegateFlowLayout protocol
Example:
extension viewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 50, height: 300)
}
}
If you look at the prepare() method of PinterestLayout class, you can see the below code
guard
cache.isEmpty == true,
let collectionView = collectionView
else {
return
}
When you reload the colectionView the cache is no longer empty, so it returns back without altering the layout.
So just remove the cache value when prepare is called.
Add removeAll() before guard
cache.removeAll()
Please follow the steps to install PinterestLayout in UICollectionView below.
Set PinterestLayout to your collection view.
let layout = PinterestLayout()
collectionView.collectionViewLayout = layout
Setup layout
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
Implement methods of PinterestLayoutDelegate
extension CustomCollectionVC: PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let image = images[indexPath.item]
return image.height(forWidth: withWidth)
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let textFont = UIFont(name: "Arial-ItalicMT", size: 11)!
return "Some text".heightForWidth(width: withWidth, font: textFont)
}
}
When data is loaded invalidate layout as well as reload data on collection view.
collectionView.collectionViewLayout.invalidateLayout()
collectionView.reloadData()
Observation:
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
try this instead of above:
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat

embedding custom keyboard into uiview

I've been researching on how to embed my own custom decimal keyboard into a UIView. i've found below image, which i would like to recreate. However i'm not sure on how and what is the best way to get started with such? is it just to create multiple UIButton and then handle it in a method or is there a smart way to recreate such ?
A collectionView is a great way to do this. Create a new storyboard, put a UICollectionViewController in there. Then create a UICollectionViewCell with an UILabel (for digits and a dot) and an UIImage (for the delete button).
Here's my UICollectionViewController code for this:
import UIKit
protocol KeypadDelegate: class {
func did(tapOnKeypad key: KeypadKey)
}
enum KeypadKey {
case number (value: Int)
case backspace
}
class KeypadViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
// MARK: - Properties.
weak var delegate: KeypadDelegate?
// MARK: - Lifecycle.
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(cellReuseID: KeypadCVCell.reuseID)
}
// MARK: - UICollectionView DataSource.
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "KeypadCVCell", for: indexPath) as! KeypadCVCell
switch indexPath.row {
case 0...8:
cell.configure(number: String(indexPath.row + 1))
case 9:
cell.setBlank()
case 10:
cell.configure(number: "0")
case 11:
let image = UIImage(named: "btn_keypad_backspace")
cell.configure(image: image)
default:
break
}
return cell
}
// MARK: - UICollectionView Delegate.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
switch indexPath.row {
case 0...8:
let key: KeypadKey = .number(value: indexPath.row + 1)
delegate?.did(tapOnKeypad: key)
case 9:
break
case 10:
let key: KeypadKey = .number(value: 0)
delegate?.did(tapOnKeypad: key)
case 11:
delegate?.did(tapOnKeypad: .backspace)
default:
break
}
}
// MARK: - UICollectionView Delegate FlowLayout.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.bounds.width / 3
let height = collectionView.bounds.height / 4
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class KeypadCVCell: UICollectionViewCell {
// MARK: - Outlets.
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var backspaceImageView: UIImageView!
// MARK: - Configuration.
func configure(number: String) {
self.numberLabel.text = number
self.backspaceImageView.isHidden = true
}
func configure(image: UIImage?) {
self.numberLabel.isHidden = true
self.backspaceImageView.image = image
}
func setBlank() {
self.numberLabel.isHidden = true
self.backspaceImageView.isHidden = true
}
}
Create a single UIButton action outlet, connect all the buttons to it, check the senders title value, convert it to Int, if not possible it's the comma or if title text is empty it's the backspace.Or create a separate function for the backspace. That would be a relatively easy, clutter free way to do it and wouldn't take much more than a dozen lines of code.

Resources