Weird delete items animation when using basic UICollectionView with Flow Layout - ios

I ran into a problem when using the simplest UICollectionView and UICollectionViewFlowLayout.
The collection itself works fine, but when the cell is removed, there are problems with the animation.
Here is a code example that demonstrates the problem:
class Cell: UICollectionViewCell { }
class MyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var items = Array(repeating: [UIColor.red, .blue, .yellow, .cyan, .magenta, .green], count: 100).flatMap { $0 }
lazy var collectionView: UICollectionView = {
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.scrollDirection = .vertical
collectionViewLayout.minimumLineSpacing = 10
collectionViewLayout.minimumInteritemSpacing = 10
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell")
collectionView.delegate = self
collectionView.dataSource = self
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
NSLayoutConstraint.activate([
.init(item: collectionView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.backgroundColor = items[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
CGSize(width: UIScreen.main.bounds.width, height: 300)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
items.remove(at: indexPath.item)
collectionView.deleteItems(at: [indexPath])
}
}
The problem itself looks like this (in slow motion):
You can notice how the next cell disappears for the duration of the animation, and at the end of it it appears.
Question: what am I doing wrong? This is a very basic use of a collection and I'm confused by these issues.

From the rest of the answers, the reason for this glitch became clear - there are problems with the deletion animation if some of the new cells are off the screen.
I solved the problem by adding layout invalidation to the animation block:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.performBatchUpdates({
self.items.remove(at: indexPath.item)
collectionView.deleteItems(at: [indexPath])
collectionView.collectionViewLayout.invalidateLayout()
}, completion: nil)
}

It seems like there is a problem with your array.
It's created dynamically and has conflicts with UICollectionView's implementation.
Try replacing your array with the following static array.
var items = [UIColor.red, .blue, .yellow, .cyan, .magenta, .green, .red, .blue, .yellow, .cyan, .magenta, .green]
Here is what it looks like after changing it.

You have to subclass UICollectionViewFlowLayout and override these two methods
initialLayoutAttributesForAppearingItem
finalLayoutAttributesForDisappearingItem
by changing "attributes" properties you will get different effects just change the line 59 animation duration and check them out.
import UIKit
class Cell: UICollectionViewCell { }
class ViewController: UIViewController ,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var items = Array(repeating: [UIColor.red, .blue, .yellow, .cyan, .magenta, .green], count: 100).flatMap { $0 }
lazy var collectionView: UICollectionView = {
let collectionViewLayout = MyCollectionLayout()
collectionViewLayout.scrollDirection = .vertical
collectionViewLayout.minimumLineSpacing = 10
collectionViewLayout.minimumInteritemSpacing = 10
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell")
collectionView.delegate = self
collectionView.dataSource = self
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
NSLayoutConstraint.activate([
.init(item: collectionView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0),
.init(item: collectionView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0),
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.backgroundColor = items[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
CGSize(width: UIScreen.main.bounds.width, height: 300)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
UIView.animate(withDuration: 0.2) {
self.collectionView.performBatchUpdates({
self.items.remove(at: indexPath.item)
collectionView.deleteItems(at: [indexPath])
}, completion: nil)
}
}
}
class MyCollectionLayout : UICollectionViewFlowLayout {
var rowOffset : CGFloat = 0.0
var deleteIndexPaths : Array<IndexPath> = []
var insertIndexPaths : Array<IndexPath> = []
override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
super.prepare(forCollectionViewUpdates: updateItems)
self.deleteIndexPaths = Array<IndexPath>()
self.insertIndexPaths = Array<IndexPath>()
for updateItem in updateItems {
if (updateItem.updateAction == .delete){
self.deleteIndexPaths.append(updateItem.indexPathBeforeUpdate!)
}else if (updateItem.updateAction == .insert){
self.insertIndexPaths.append(updateItem.indexPathAfterUpdate!)
}
}
}
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
var attributes = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
if(attributes == nil){
attributes = self.layoutAttributesForItem(at: itemIndexPath)
}
attributes?.alpha = 1
return attributes
}
override func finalizeCollectionViewUpdates()
{
super.finalizeCollectionViewUpdates()
self.deleteIndexPaths = [];
self.insertIndexPaths = [];
}
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
var attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
if ((attributes == nil)){
attributes = self.layoutAttributesForItem(at: itemIndexPath)
}
attributes?.transform = CGAffineTransform(scaleX: 0, y: 0)
attributes?.zIndex = -1
attributes?.alpha = 1
return attributes
}
}

Related

UICollectionViewController footer not re-positioning itself properly after endEditing

I have a simple UICollectionViewController that is one section with a header and footer pinned to visible bounds. When I click a textField in the footer, the footer automatically animates above the keyboard and the collectionView scrolls to show the cells that otherwise would have been hidden by the keyboard as you would expect. However, when I click one outside of the keyboard, and dismiss it with a call to self.view.endEditing(true) the footer and collectionView do not react unless the collectionView is scrolled near the bottom. If the collectionView is scrolled near the bottom, the footer and collectionView animate as expected.
How can I force the footer and collectionView to animate properly every time?
In the images below, the header is orange, footer is green, and there are 10 cells that alternate red and blue.
Actual Behavior (occurs when you are not scrolled near the bottom of the collectionView when the keyboard dismisses)
Desired Behavior (occurs when you are scrolled at or near the bottom of the collectionView when the keyboard dismisses)
I know there are some methods to achieve this with Notifications when the keyboard appears and disappears but I would prefer to just use the default UICollectionViewController behavior if possible. Is there some configuration or setting I am missing?
Code:
New Single View App. Delete StoryBoard and ViewController.swift. Delete the main storyboard entry from the info plist file.
AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = CollectionViewController.init()
return true
}
}
CollectionViewController.swift
import UIKit
private let reuseIdentifier = "Cell"
private let collectionViewHeaderFooterReuseIdentifier = "MyHeaderFooterClass"
class CollectionViewController: UICollectionViewController {
init() {
let collectionViewFlowLayout = UICollectionViewFlowLayout.init()
collectionViewFlowLayout.sectionHeadersPinToVisibleBounds = true
collectionViewFlowLayout.sectionFootersPinToVisibleBounds = true
super.init(collectionViewLayout: collectionViewFlowLayout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView?.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)
self.collectionView?.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)
self.collectionView?.backgroundColor = UIColor.white
self.collectionView?.dataSource = self
self.collectionView?.delegate = self
}
}
// MARK: UICollectionViewDelegate
extension CollectionViewController {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
DispatchQueue.main.async {
self.view.endEditing(true)
self.collectionViewLayout.invalidateLayout()
self.collectionView?.layoutIfNeeded()
}
}
}
// MARK: - UICollectionViewDataSource
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 10
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
switch indexPath.row % 2 {
case 0:
cell.backgroundColor = UIColor.red
case 1:
cell.backgroundColor = UIColor.blue
default:
cell.backgroundColor = UIColor.gray
}
return cell
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier, for: indexPath)
if kind == UICollectionView.elementKindSectionHeader {
view.backgroundColor = UIColor.orange
}
else { //footer
view.backgroundColor = UIColor.green
}
return view
}
}
// MARK: - Collection View Flow Layout Delegate
extension CollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100)
}
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, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 100)
}
}
MyHeaderFooterClass
import UIKit
class MyHeaderFooterClass: UICollectionReusableView {
let textField: UITextField = {
let view = UITextField.init()
view.placeholder = "enter text here"
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(textField)
self.setNeedsUpdateConstraints()
}
override func updateConstraints() {
textFieldConstraints()
super.updateConstraints()
}
private func textFieldConstraints() {
NSLayoutConstraint(item: textField, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: textField, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: textField, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: textField, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
From documentation:
func layoutIfNeeded()
Use this method to force the view to update its layout immediately.
func invalidateLayout()
This method invalidates the layout of the collection view itself and
returns right away.
Solution:
Looking at your code, you are already updating collectionViewLayout. So, you don't need to force update collectionView again. Therefore, simply removing self.collectionView?.layoutIfNeeded() alone will solve your issue.
Change your code as below:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
DispatchQueue.main.async {
self.view.endEditing(true)
self.collectionViewLayout.invalidateLayout()
//self.collectionView?.layoutIfNeeded()
}
}

Sizing UICollectionView

So this is my first time working with CollectionView and I have collection of images and I want my collection hight to adjust to show all the images so I do not have to scroll to see the rest of them
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MycellCollectionViewCell
cell.myimage.image = UIImage(named: list[indexPath.row]+".jpg")
return cell
}
You can get the height of UICollectionView by using the performBatchUpdates(_:completion:) method after calling reloadData()
var height = CGFloat(0)
self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil) { (result) in
if result {
height = self.collectionView.contentSize.height
}
}
After that you can either set the frame height of the collection view:
collectionView.frame.size.height = height
or add a constraint:
let height = NSLayoutConstraint(item: collectionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height)
collectionView.addConstraint(height)
or if you have set the the collectionview's height from the storyboard or XIB, then you can connect the constraint to your code and simply change the constant:
collectionHeight.constant = height

Designing a Crossword Grid for iOS in Swift

I've developed a crossword app for android and managed to create the grid using the GridView class provided in the android support library and buttons (This one).
I've tried to use UICollectionView with a custom layout but it seems to be designed for other purposes. Does anyone know what can I use to create the same (or a similar) thing in iOS?
Is there a library that makes easier to crate a fixed table like this one?
Note: the number of columns and rows is fixed 10x10
I'd prefer to use Swift over Obj-C and i'm not caring about the keyboard and the menu yet.
Thank you very much
I attach a screenshot of the android app.
Sorry for my english, it is still bad :)
UIViewController should work fine for your needs. Try something like this:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var collectionView: UICollectionView!
var gridSize: CGFloat = 10
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
view.addConstraints([
NSLayoutConstraint(item: collectionView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: collectionView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: collectionView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: collectionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.width) ])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(pow(Double(gridSize), 2))
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
// Set up your cells here
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = view.frame.width / gridSize - 1
return CGSize(width: size, height: size)
}
}
Setting the backgroundColor of your cells will give you this:

hide NavigationBar when scrolling at down CollectionView

i made NavigationBar and TabBar programmatically, but hide navigationBar when i scroll at down collectionView. here is code
import Foundation
import Foundation
import UIKit
class HomeMyBazar: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
let productCellId = "productCellId"
let dealsCellId = "dealsCellId"
let titles = ["Hello world", "Product", "Deals", "Store world"]
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isTranslucent = false
setupNavBarButtons()
setupMenuBar()
setupCollectionView()
}
func setupNavBarButtons() {
lazy var settingsLauncher: SettingsLauncher = {
let launcher = SettingsLauncher()
launcher.homeController = self
return launcher
}()
func targetMyBazar() {
let secondViewController = ViewController1()
self.navigationController?.pushViewController(secondViewController, animated: true)
}
func targetSearch() {
let viewController = ViewController4()
self.navigationController?.pushViewController(viewController, animated: true)
}
func targetUser() {
let viewController = ViewController3()
self.navigationController?.pushViewController(viewController, animated: true)
}
func targetCart() {
let viewController = ViewController5()
self.navigationController?.pushViewController(viewController, animated: true)
}
func handleMore() {
settingsLauncher.showSettings()
}
func showControllerForSetting(_ setting: Setting) {
let dummySettingsViewController = UIViewController()
dummySettingsViewController.view.backgroundColor = UIColor.white
dummySettingsViewController.navigationItem.title = setting.name.rawValue
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
navigationController?.pushViewController(dummySettingsViewController, animated: true)
}
func scrollToMenuIndex(_ menuIndex: Int) {
let indexPath = IndexPath(item: menuIndex, section: 0)
collectionView?.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition(), animated: true)
}
fileprivate func setTitleForIndex(_ index: Int) {
if let titleLabel = navigationItem.titleView as? UILabel {
titleLabel.text = " \(titles[index])"
}
}
lazy var menuBar: MenuBar = {
let mb = MenuBar()
mb.homeController = self
return mb
}()
fileprivate func setupMenuBar() {
navigationController?.hidesBarsOnSwipe = true
let blackView = UIView()
blackView.backgroundColor = .lightGray
view.addSubview(blackView)
view.addConstraintsWithFormat("H:|[v0]|", views: blackView)
view.addConstraintsWithFormat("V:[v0(50)]", views: blackView)
view.addSubview(menuBar)
view.addConstraintsWithFormat("H:|[v0]|", views: menuBar)
view.addConstraintsWithFormat("V:[v0(80)]", views: menuBar)
menuBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
menuBar.horizontalBarLeftAnchorConstraint?.constant = scrollView.contentOffset.x / 4
}
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let index = targetContentOffset.pointee.x / view.frame.width
let indexPath = IndexPath(item: Int(index), section: 0)
menuBar.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition())
setTitleForIndex(Int(index))
}
func setupCollectionView() {
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
collectionView?.backgroundColor = UIColor.blue
collectionView?.register(MyBazarCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.register(ProductCell.self, forCellWithReuseIdentifier: productCellId)
collectionView?.register(DealsCell.self, forCellWithReuseIdentifier: dealsCellId)
collectionView?.contentInset = UIEdgeInsetsMake(110, 0, 0, 0)
collectionView?.isPagingEnabled = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
if indexPath.item == 1 {
identifier = productCellId
} else if indexPath.item == 2 {
identifier = dealsCellId
} else {
identifier = cellId
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, 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 - 50)
}
}
import Foundation
import UIKit
struct Item {
var imageName: String
var text: String
}
class MenuBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.black
cv.dataSource = self
cv.delegate = self
return cv
}()
let data: [Item] = [
Item(imageName: "service", text: "Service"),
Item(imageName: "product", text: "Product "),
Item(imageName: "deals", text: " Deals"),
Item(imageName: "store", text: "Store")
]
let cellId = "cellId"
let imageNames = ["service", "product", "deals", "store"]
let imageTitle = ["Servic", "Product", "Deals", "Store"]
var homeController: HomeMyBazar?
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(MenuCell.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
addConstraintsWithFormat("H:|[v0]|", views: collectionView)
addConstraintsWithFormat("V:|[v0]|", views: collectionView)
let selectedIndexPath = IndexPath(item: 0, section: 0)
collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: UICollectionViewScrollPosition())
setupHorizontalBar()
}
var horizontalBarLeftAnchorConstraint: NSLayoutConstraint?
func setupHorizontalBar() {
let horizontalBarView = UIView()
horizontalBarView.backgroundColor = UIColor.red
horizontalBarView.translatesAutoresizingMaskIntoConstraints = false
addSubview(horizontalBarView)
horizontalBarLeftAnchorConstraint = horizontalBarView.leftAnchor.constraint(equalTo: self.leftAnchor)
horizontalBarLeftAnchorConstraint?.isActive = true
horizontalBarView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
horizontalBarView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1/4).isActive = true
horizontalBarView.heightAnchor.constraint(equalToConstant: 4).isActive = true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
homeController?.scrollToMenuIndex(indexPath.item)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MenuCell
let item = data[indexPath.item]
cell.imageView.image = UIImage(named: item.imageName)
cell.imageTitle.text = item.text
cell.tintColor = UIColor.white
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width / 4, height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MenuCell: BaseCell {
let imageTitle: UILabel = {
let label = UILabel()
label.text = "hellow world label"
label.textColor = .white
return label
}()
let imageView: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "service")?.withRenderingMode(.alwaysTemplate)
iv.tintColor = .white
return iv
}()
override var isHighlighted: Bool {
didSet {
imageView.tintColor = isHighlighted ? UIColor.white : UIColor.white
}
}
override var isSelected: Bool {
didSet {
imageView.tintColor = isSelected ? UIColor.white : UIColor.white
}
}
override func setupViews() {
super.setupViews()
addSubview(imageTitle)
addSubview(imageView)
addConstraintsWithFormat("H:|-25-[v0(80)]|", views: imageTitle)
addConstraintsWithFormat("V:|-45-[v0(20)]|", views: imageTitle)
addConstraintsWithFormat("H:[v0(28)]", views: imageView)
addConstraintsWithFormat("V:|-15-[v0(28)]|", views: imageView)
addConstraint(NSLayoutConstraint(item: imageTitle, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: imageTitle, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: imageView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
}
}
after scrolling
You can try this
self.navigationController.hidesBarsOnSwipe = true

UICollectionView is not working in custom keyboard extension

I am trying to implement UICollectionView in custom keyboard extension but it is not working.
This is what I am doing inside viewDidLoad method.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 90, height: 120)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView)
I have added UICollectionViewDelegateFlowLayout and UICollectionViewDataSource protocols to my class and then implemented dataSource methods as shown below
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
cell.backgroundColor = UIColor.orangeColor()
return cell
}
The same code is working as expected in app bundle. I don't understand why it has a different behaviour in keyboard extension.
Is there anything that I am missing here, What do I need to do to get it working in keyboard extension?
The collection view will be visible when you provide constraints for the collectionView.
Code :
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(collectionView)
collectionView.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor,constant: 0.0).active = true
collectionView.bottomAnchor.constraintEqualToAnchor(bottomLayoutGuide.topAnchor, constant: 0.0).active = true
collectionView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
collectionView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
Try this once bro:
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 10, height: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView)
// Perform custom UI setup here
self.nextKeyboardButton = UIButton(type: .System)
self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
self.nextKeyboardButton.sizeToFit()
self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false
self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)
self.view.addSubview(self.nextKeyboardButton)
let nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0)
let nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
cell.backgroundColor = UIColor.orangeColor()
return cell
}
override func textDidChange(textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal)`}`

Resources