UICollectionView is not working in custom keyboard extension - ios

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)`}`

Related

Weird delete items animation when using basic UICollectionView with Flow Layout

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
}
}

UICollectionView in navigation title view is unresponsive to selection

I have a UICollectionView inside the UINavigationItem's title view acting as a menu. I am unable to select items within the collection view. If I place the UICollectionView outside of the navigation item and into my view controller, everything works as intended.
UIViewController
let menuView = HomeMenuView()
override func viewDidLoad() {
navigationItem.titleView = menuView
menuView.delegate = self
}
HomeMenuView (UICollectionView)
class HomeMenuView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var sections = ["Recent", "Following"]
var delegate: HomeMenuDelegate!
var selectedIndex = 0
var collectionView: UICollectionView!
override init(frame: CGRect) {
super.init(frame: frame)
create()
}
func create() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
addSubview(collectionView)
collectionView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: UIScreen.main.bounds.width/2, height: 30)
collectionView.center(x: centerXAnchor, y: centerYAnchor)
collectionView.backgroundColor = .clear
collectionView.isUserInteractionEnabled = true
collectionView.clipsToBounds = true
collectionView.allowsSelection = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(HomeMenuCell.self, forCellWithReuseIdentifier: "cellID")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sections.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! HomeMenuCell
cell.title = sections[indexPath.row]
cell.isSelected = selectedIndex == indexPath.row
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let currentCell = collectionView.cellForItem(at: IndexPath(row: selectedIndex, section: 0)) as? HomeMenuCell else { return }
guard let newCell = collectionView.cellForItem(at: indexPath) as? HomeMenuCell else { return }
currentCell.isSelected = false
newCell.isSelected = true
selectedIndex = indexPath.row
delegate.didSelectSection(at: indexPath.row)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (collectionView.frame.width)/CGFloat(sections.count), height: collectionView.frame.height)
}
override func layoutSubviews() {
super.layoutSubviews()
collectionView.layer.cornerRadius = collectionView.bounds.height/2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Your collection view cells cannot be selected because your collection view is outside the bounds of its super view.
Change the middle part of your create() func as follows (you didn't include your "constraint helpers" so I used standard constraint syntax):
addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
collectionView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width * 0.5),
collectionView.heightAnchor.constraint(equalToConstant: 30.0),
])
//collectionView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: UIScreen.main.bounds.width/2, height: 30)
//collectionView.center(x: centerXAnchor, y: centerYAnchor)
collectionView.backgroundColor = .clear

Select collection view cell on load - Custom UICollectionView class

I have a custom collection view (tabBarCollectionView), which should load with the first cell being selected, as per the code below. However, this is not working. I know this as the cell should be a different colour when selected, but isn't.
override init(frame: CGRect) {
super.init(frame: frame)
tabBarCollectionView.register(TabBarCell.self, forCellWithReuseIdentifier: cellIdentifier)
addSubview(tabBarCollectionView)
tabBarCollectionView.translatesAutoresizingMaskIntoConstraints = false
addConstraintsWithFormat("H:|[v0]|", views: tabBarCollectionView)
addConstraintsWithFormat("V:|[v0]|", views: tabBarCollectionView)
tabBarCollectionView.selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: [])
}
Full code of custom UIView class is inserted below:
import Foundation
import UIKit
class TabBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellIdentifier = "Cell"
let imageNames = ["homeIcon", "exploreIcon", "addIcon", "inboxIcon", "profileIcon"]
lazy var tabBarCollectionView: UICollectionView = {
// All collection view implementations in here
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor.clear
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
tabBarCollectionView.register(TabBarCell.self, forCellWithReuseIdentifier: cellIdentifier)
addSubview(tabBarCollectionView)
tabBarCollectionView.translatesAutoresizingMaskIntoConstraints = false
addConstraintsWithFormat("H:|[v0]|", views: tabBarCollectionView)
addConstraintsWithFormat("V:|[v0]|", views: tabBarCollectionView)
self.tabBarCollectionView.allowsSelection = true
self.tabBarCollectionView.selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: [])
}
override func layoutSubviews() {
super.layoutSubviews()
tabBarCollectionView.allowsSelection = true
tabBarCollectionView.selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: [])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! TabBarCell
cell.backgroundColor = UIColor.clear
cell.tabBarImageView.image = UIImage(named: imageNames[indexPath.item])?.withRenderingMode(.alwaysTemplate)
cell.tabBarImageView.tintColor = UIColor.pinpointGrey
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width / 5, 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 TabBarCell: UICollectionViewCell {
let tabBarImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "homeIcon")?.withRenderingMode(.alwaysTemplate)
imageView.tintColor = UIColor.pinpointGrey
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
backgroundColor = UIColor.clear
addSubview(tabBarImageView)
addConstraintsWithFormat("H:[v0(42)]", views: tabBarImageView)
addConstraintsWithFormat("V:[v0(42)]", views: tabBarImageView)
addConstraint(NSLayoutConstraint(item: tabBarImageView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: tabBarImageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
}
override var isHighlighted: Bool {
didSet {
if tabBarImageView.isHighlighted == true {
self.tabBarImageView.tintColor = UIColor.pinpointBlue
} else {
self.tabBarImageView.tintColor = UIColor.pinpointGrey
}
}
}
override var isSelected: Bool {
didSet {
tabBarImageView.tintColor = isSelected ? UIColor.pinpointBlue : UIColor.pinpointGrey
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Where is your datasource set? Do you load it separately and then do a reloadData()?
If yes, then you should select the cell then and not during your viewDidLoad method as during load the collection view might not have been populated yet.
You could also add to a viewWillAppear/viewDidAppear method as this gets called after the collection view has been populated

cellForItemAt is not calling in collectionView

I have a inputView where a collectionView will be displayed. I used auto layout for that inputView. When running the app numberOfItemsInSection is called but cellForItemAt is not called. So no cell is displaying. Did I do anything wrong using constraint?
I've also created project https://drive.google.com/file/d/0B5UHWsK1E6dSRDA5bmtSY2ZZbGs/view
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
textField.inputView = InputPhotoView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 265))
textField.becomeFirstResponder()
}
InputPhotoView.swift
class InputPhotoView: UIView {
let CellIdentifier = "Cell"
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.dataSource = self
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: CellIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("Not yet implented")
}
func setupViews() {
self.backgroundColor = UIColor.brown
self.addSubview(collectionView)
setupConstraints()
}
func setupConstraints() {
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: self.trailingAnchor, constant: 50),
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
collectionView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
])
}
public lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 100, height: 100)
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.contentInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
return collectionView
}()
}
DataSource
extension InputPhotoView : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("number of items")
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CustomCell
return cell
}
}
Your code has constraint conflicts so the collectionView is not visible. Try changing this line:
collectionView.leadingAnchor.constraint(equalTo: self.trailingAnchor, constant: 50)
To something like this:
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 50)

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

Resources