How to customize collection view flowlayout cell - swift3

I am creating a movie ticket app using collection view. I'm stuck arranging the seats in a collectionView. How to customise the custom flow layout? I want a particular amount of seats in specific rows of the collection view cells
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == leftCollectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "leftcellid", for: indexPath) as! LeftSideMovieCollectionViewCell
let selectedseat = leftsideArray[indexPath.row]
if selectedSeatArray.contains(selectedseat) {
cell.leftMovieSeatImageView.image = UIImage.init(named: "seatfulled")
} else {
cell.leftMovieSeatImageView.image = UIImage.init(named: "seatblank")
}
if indexPath.row == 16 || indexPath.row == 17 || indexPath.row == 12 || indexPath.row == 13 {
cell.leftMovieSeatImageView.isHidden = true
} else {
cell.leftMovieSeatImageView.isHidden = false
}
return cell
} else if collectionView == RightCollectionview {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "rightcellid", for: indexPath) as! RightSideMovieCollectionViewCell
let selectedseat = rightsideArray[indexPath.row]
if selectedSeatArray.contains(selectedseat) {
cell.RightMovieseatImageView.image = UIImage.init(named: "seatfulled")
} else {
cell.RightMovieseatImageView.image = UIImage.init(named: "seatblank")
}
if indexPath.row == 18 || indexPath.row == 19 || indexPath.row == 14 || indexPath.row == 15 {
cell.RightMovieseatImageView.isHidden = true
} else {
cell.RightMovieseatImageView.isHidden = false
}
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "maincellid", for: indexPath) as! MainCollectionViewCell
let selectedseat = vipSeatArray[indexPath.row]
if selectedSeatArray.contains(selectedseat) {
cell.mainSeatImageView.image = UIImage.init(named: "seatfulled")
} else {
cell.mainSeatImageView.image = UIImage.init(named: "seatblank")
}
if indexPath.row == 43 || indexPath.row == 42 || indexPath.row == 34 || indexPath.row == 33 {
cell.mainSeatImageView.isHidden = true
} else {
cell.mainSeatImageView.isHidden = false
}
return cell
}
}
Here I have taken three collection views. I have hidden the particular cells. I don't know how to modify this. Can it be done with a single collection view? Any suggestions will be appreciated.

Related

Treating adjacent tracking areas as one contiguous area

I'm trying to present a UI of a title/less window when a mouse leaves a window's title or contentview, but not when moving from one to the other; in essence have the two tracking areas function as one (I resorted to this as I couldn't figure out how to create a single area when the title view is hidden):
override func mouseEntered(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
if theEvent.modifierFlags.contains(.shift) {
NSApp.activate(ignoringOtherApps: true)
}
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close entered")
closeButton?.image = closeButtonImage
break
default:
Swift.print(String(format: "%# entered",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = true
updateTranslucency()
// view or title entered
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: !lastMouseOver)
}
}
}
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = false
updateTranslucency()
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: lastMouseOver)
}
}
}
Additionally, there's a tracking rect on the close button to appear only when over. Anyway, from my tracer output I see the issue - mouse over window from beneath to over its title:
view entered
updateTitleBar
**view entered**
view exited
updateTitleBar
title entered
updateTitleBar
title exited
Note sure why I'm getting a second view entered event (view entered), but the movement out of the view and onto the adjacent title each triggers an updateTilebar() call which is visually stuttering - not remedied so far with animation:
fileprivate func docIconToggle() {
let docIconButton = panel.standardWindowButton(.documentIconButton)
if settings.autoHideTitle.value == false || mouseOver {
if let doc = self.document {
docIconButton?.image = (doc as! Document).displayImage
}
else
{
docIconButton?.image = NSApp.applicationIconImage
}
docIconButton?.isHidden = false
self.synchronizeWindowTitleWithDocumentName()
}
else
{
docIconButton?.isHidden = true
}
}
#objc func updateTitleBar(didChange: Bool) {
if didChange {
Swift.print("updateTitleBar")
if settings.autoHideTitle.value == true && !mouseOver {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.hidden
panel.animator().titlebarAppearsTransparent = true
panel.animator().styleMask.formUnion(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
} else {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.visible
panel.animator().titlebarAppearsTransparent = false
panel.animator().styleMask.formSymmetricDifference(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
}
}
}
So my question is about how to defer the actions when areas are adjacent.
They (titlebar & content view) are not siblings of each other so didn't think a hitTest() was doable but basically if I could tell if I was moving into the adjacent tracking area, I'd like it to be a no-op.
Any help with why animation isn't working would be a plus.
Not a true answer, but if you know the adjacent view's rect you can use the event's location to probe whether you'd want to ignore movements among adjacent views:
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
let location : NSPoint = theEvent.locationInWindow
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
let vSize = self.window?.contentView?.bounds.size
// If we exit to the title bar area we're still "inside"
// and visa-versa, leaving title to content view.
if theEvent.trackingNumber == titleTrackingTag, let tSize = titleView?.bounds.size {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y < ((vSize?.height)! + tSize.height) {
Swift.print("title -> view")
return
}
}
else
if theEvent.trackingNumber == viewTrackingTag {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y > (vSize?.height)! {
Swift.print("view -> title")
return
}
}
mouseOver = false
updateTranslucency()
if hideTitle {
updateTitleBar(didChange: true)
}
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
}
}

Creating UIcollectionView with Waterfall layout and self sizing cell

I am trying to implement waterfallLayout with a self sizing cell that should work with iOS >= 9.0
Something like pintrest collectionViewLayout not the transition but the difference is that i have a fixed ratio of image but fields below is dynamic which i used UIStackView.
The self sizing is working fine but my problem is in changing the cell origin to be always = 15 PX between cells.
so when i tried FlowLayout without any customisation everything is working fine except for the margins between cells is not correct but the scrolling is working well without any performance issue for iOS >= 9.0
After trying CustomizingFlowLayout the performance is so bad in iOS 10 things is not working while in iOS 9 works fine. i know that apple did some improvement with the UICollectionView but with me i don't see that improvement.
My Cell:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
self.setNeedsLayout()
self.layoutIfNeeded()
layoutAttributes.bounds.size.height = systemLayoutSizeFitting(layoutAttributes.size, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
return layoutAttributes
}
My controller:
ViewDidLoad {
layoutView = collectionView.collectionViewLayout as! CustomLayout
if #available(iOS 10.0, *) {
self.collectionView.isPrefetchingEnabled = false
}
layoutView.estimatedItemSize = CGSize(width: layoutView.cellWidth, height: 200)
}
extension RecipesCollectionWithSortBar: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell.reuseIdentifier, for: indexPath) as! customCell
return cell
}
extension RecipesCollectionWithSortBar : UICollectionViewDelegateFlowLayout {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.maxY == scrollView.contentSize.height && !isLoadingMoreRecipes {
//
LoadMore
}
}
My CustomFlowLayout
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attrs = super.layoutAttributesForElements(in: rect)
var attrsCopy = [UICollectionViewLayoutAttributes]()
for element in attrs! where (element.copy() as! UICollectionViewLayoutAttributes).representedElementCategory == .cell
&& element.frame.height != cacheCells[element.indexPath.item]?.frame.height {
if element.indexPath.item == 0 {
element.frame.origin.y = self.sectionInset.top
element.frame.origin.x = self.sectionInset.left
} else if element.indexPath.item == 1 {
element.frame.origin.y = self.sectionInset.top
element.frame.origin.x = cacheCells[0]!.frame.maxX + margin
} else {
let previous = cacheCells[element.indexPath.item - 2]
element.frame.origin.y = previous!.frame.maxY + self.minimumInteritemSpacing
element.frame.origin.x = previous!.frame.origin.x
}
cacheCells[element.indexPath.item] = element
attrsCopy.append(element)
}
let values = cacheCells.values.filter({ $0.frame.intersects(rect) })
return values.map({ $0 })
}
Here i have many problems :
1- i have a huge white space at the end of the collectionView because i changed the cell origin this problem only happens at iOS > 10.
i tried to override the content size
func getContentHeight() -> CGFloat {
if !cacheCells.values.isEmpty {
let attribute = cacheCells.values.max { $0.0.frame.maxY < $0.1.frame.maxY }
return attribute!.frame.maxY + sectionInset.bottom
}
return contHeight
}
override var collectionViewContentSize: CGSize {
return CGSize(width: self.cellWidth * CGFloat(columnCount), height: getContentHeight())
}
for some reason iOS 9 don't like this solution the scroll stops ..
2- Scrolling performance is so bad in iOS 10 and worst in iOS 9.0.
3- when i load more believe it or not it scroll 3 or 4 times and doesn't have more items to show when i print the log (i have 100 item but the collection view show only 70) and sometimes when hard scrolling it remembers that there's some more items and show them. i am not sure what i am doing wrong.
It's been 2 weeks now trying to figure out what's wrong may be having some fresh eye and mind can point on the problem i am having.

Index out of range (Table with expandable rows) in Swift 3

Hello i am trying to make a shop page on my application. The shop page contains a table with a title that is clickable so it expands into subcategories of that title. Etc click coffee and get some different kind of coffee to buy from.
Title = Drinks, Sandwiches & Drinks in the picture below.
subcategory from title = Ham & cheese, Chicken, bacon & curry .... and so on.
Now the expanding and closing the titles work fine. But when i press + (add iteem to cart) i get a out out of index if it's clicked in a specific order.
if i expand sandwiches (like it is in the picture) and then expand drinks (the one below sandwiches) and then click on the plus on any subcategory in sandwiches and then close sandwiches again and press on plus on any subcategory in Drinks (the one below the now closed sandwiches) the application will crash with a out of index error. Only this combination gives this error.
How the code works:
/* Create Cells */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -
> UITableViewCell {
// Row is DefaultCell
if let rowData = destinationData?[indexPath.row] {
let defaultCell = Bundle.main.loadNibNamed("TableViewCellMenuItems", owner: self, options: nil)?.first as! TableViewCellMenuItems
// tableView.dequeueReusableCell(withIdentifier: "TableViewCellMenuItems", for: indexPath).first as! TableViewCellMenuItems
defaultCell.menuName.text = rowData.name
defaultCell.menuPicture.image = rowData.image
defaultCell.menuFoldPic.image = #imageLiteral(resourceName: "ic_keyboard_arrow_down")
defaultCell.selectionStyle = .none
return defaultCell
}
// Row is ExpansionCell
else {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: indexPath.row)] {
// print("her åbner vi")
// // Create an ExpansionCell
let expansionCell =
Bundle.main.loadNibNamed("TableViewCellMenuItemExpanded", owner: self, options: nil)?.first as! TableViewCellMenuItemExpanded
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: indexPath.row)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = indexPath.row - parentCellIndex - 1
// Set the cell's data
expansionCell.itemPrice.text = String (describing: "\(rowData.menuItems![flightIndex].price) kr")
expansionCell.itemName.text = rowData.menuItems?[flightIndex].name
expansionCell.itemCount.text = String (describing: rowData.menuItems![flightIndex].count)
// expansionCell.itemCount.text = String (describing: indexPath.row)
expansionCell.itemAdd.tag = Int(indexPath.row)
expansionCell.itemMinus.tag = Int(indexPath.row)
expansionCell.itemAdd.addTarget(self, action: #selector(HomeSelectedShopViewController.plus(_:)), for: .touchUpInside)
expansionCell.itemMinus.addTarget(self, action: #selector(HomeSelectedShopViewController.minus(_:)), for: .touchUpInside)
expansionCell.selectionStyle = .none
return expansionCell
}
}
return UITableViewCell()
}
as you can see the expansionCells get a tag for my function plus and minus (so i can know which indexPath i should add up or down)
Plus function:
func plus(_ sender: AnyObject?) {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: sender!.tag)] {
self.table.reloadData()
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: sender!.tag)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = sender!.tag - parentCellIndex - 1
print(sender!.tag)
print(flightIndex)
print(rowData.menuItems!.count)
var con = 0;
for a in rowData.menuItems! {
print("her er navn : \(a.name) og her er id \(con)")
con += 1
}
let data = rowData.menuItems![flightIndex]
let item = Items(name: data.name, price: data.price, count: data.count)
var counter = 0;
for x in self.basket {
if(x.name == item.name || x.price == item.price)
{
counter = counter+1;
}
}
if(counter >= 99)
{
}
else
{
DispatchQueue.main.async {
self.basket.append(item)
self.updateBasket()
rowData.menuItems![flightIndex].count = rowData.menuItems![flightIndex].count+1;
self.table.reloadData()
}
}
}
}
Rest of the functions:
/* Expand cell at given index */
private func expandCell(tableView: UITableView, index: Int) {
// print("when is this run 1")
// Expand Cell (add ExpansionCells
if let flights = destinationData?[index]?.menuItems {
for i in 1...flights.count {
destinationData?.insert(nil, at: index + i)
tableView.insertRows(at: [NSIndexPath(row: index + i, section: 0) as IndexPath] , with: .top)
}
}
}
/* Contract cell at given index */
private func contractCell(tableView: UITableView, index: Int) {
// print("when is this run 4")
if let flights = destinationData?[index]?.menuItems {
for i in 1...flights.count {
destinationData?.remove(at: index+1)
tableView.deleteRows(at: [NSIndexPath(row: index+1, section: 0) as IndexPath], with: .top)
}
}
}
/* Get parent cell index for selected ExpansionCell */
private func getParentCellIndex(expansionIndex: Int) -> Int {
// print("when is this run 5")
var selectedCell: Menu?
var selectedCellIndex = expansionIndex
while(selectedCell == nil && selectedCellIndex >= 0) {
selectedCellIndex -= 1
selectedCell = destinationData?[selectedCellIndex]
}
return selectedCellIndex
}
func minus(_ sender: AnyObject?) {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: sender!.tag)] {
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: sender!.tag)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = sender!.tag - parentCellIndex - 1
let data = rowData.menuItems![flightIndex]
let item = Items(name: data.name, price: data.price, count: data.count)
DispatchQueue.main.async {
var counter = 0;
for x in self.basket {
if(x.name == item.name || x.price == item.price)
{
if(item.count == 0)
{
break
}
else
{
self.basket.remove(at: counter)
self.updateBasket()
rowData.menuItems![flightIndex].count = rowData.menuItems![flightIndex].count-1;
self.table.reloadData()
break
}
}
counter = counter+1;
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let data = destinationData?[indexPath.row] {
// If user clicked last cell, do not try to access cell+1 (out of range)
if(indexPath.row + 1 >= (destinationData?.count)!) {
expandCell(tableView: tableView, index: indexPath.row)
}
else {
// If next cell is not nil, then cell is not expanded
if(destinationData?[indexPath.row+1] != nil) {
expandCell(tableView: tableView, index: indexPath.row)
} else {
contractCell(tableView: tableView, index: indexPath.row)
}
}
}
}
I dont really know how to solve this, all i know is that the plus function gets out of range on this bit "let data = rowData.menuItems![flightIndex]".

Why is this not saving?

I am trying to save a simple piece of information using NSUserdefaults. I am trying to save a SKSprite to have an alpha of 1. Here is how I am doing it.
First scene: Level select (sprite alpha is 0.2)
When user completes Level: (edit sprite in Level Select to equal one)
GameViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = levelselectscene {
// Set the scale mode to scale to fit the window
scene.scaleMode = .fill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
Level Select:
override func didMove(to view: SKView) {
if unlockLevelTwoButton == true {
levelselectscene?.childNode(withName: "LevelTwoButton")?.alpha = 1
UserDefaults.standard.set(unlockLevelTwoButton, forKey: "LevelTwoUnlocked")
print("I got this far")
}
}
Level One:
func didBegin(_ contact: SKPhysicsContact) {
var bodyA = contact.bodyA
var bodyB = contact.bodyB
let threeStars = SKScene(fileNamed: "LevelCompleted3Star")
let fadeAction = SKAction.fadeAlpha(by: 1, duration: 0.45)
if bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 2 || bodyA.categoryBitMask == 2 && bodyB.categoryBitMask == 1{
print("TEST")
levelOneCompleted() //islevelonecompleted
unlockLevelTwoButton = true
//3 stars
threeStars?.scaleMode = .fill
self.view?.presentScene(threeStars!, transition: .fade(withDuration: 0.3))
}
3 Stars:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isLevelOneCompleted == true{
unlockLevelTwoButton = true
UserDefaults.standard.set(isLevelOneCompleted, forKey: "LevelOne")
UserDefaults.standard.synchronize()
levelselectscene?.scaleMode = .fill
levelselectscene?.childNode(withName: "levelTwoButton")?.alpha = 1
self.view?.presentScene(levelselectscene)
}
To me, it looks like the information should save. What am I doing wrong? I also have the keys set to retrieve:
if let z = UserDefaults.standard.object(forKey: "LevelTwoButton")
{
unlockLevelTwoButton = z as! Bool
}
Can't figure out why it's not saving!
Based on the code you've shown, you are saving it with one name, and retrieving it with a different name (LevelTwoUnlocked) vs (LevelTwoButton)

Swift 3 - Swipe left to change label by using Array

I beginner learning Swift 3 - UISwipe gesture but cannot work if using Array.
This my code.
How can I code so the Label only change from "hello1" to "hello2" then swipe left again to "hello3". Also reverse swipe to right back from "hello3" to "hello2" and "hello1". or loop back to first one.
Thanks.
class ChangeLabelViewController: UIViewController {
var helloArray = ["Hello1", "Hello2", "Hello3"]
var currentArrayIndex = 0
#IBOutlet weak var helloLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ChangeLabelViewController.handleSwipes(sender:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ChangeLabelViewController.handleSwipes(sender:)))
leftSwipe.direction = .left
rightSwipe.direction = .right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
helloLabel.text = helloArray[currentArrayIndex]
}
func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
helloLabel.text = helloArray[currentArrayIndex + 1]
}
if sender.direction == .right {
}
}
Try this:
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
helloLabel.text = helloArray[currentArrayIndex]
}
if sender.direction == .right {
currentArrayIndex = (currentArrayIndex + 3 - 1) % 3 //if uInt
helloLabel.text = helloArray[currentArrayIndex]
}
you can also do it with below code,
func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
if(currentArrayIndex < helloArray.count - 1)
{
currentArrayIndex += 1
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
}
if sender.direction == .right {
if(currentArrayIndex > 0)
{
currentArrayIndex -= 1
if currentArrayIndex == -1
{
currentArrayIndex = 0
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
else {
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
}
}