scrolling until last cel of uicollectionview - ios

I have a problem while scrolling my collectionView
after choosing a cell, the last cell is not available for choosing
func changeViewSize(from: CGFloat, to: CGFloat, indexPath: IndexPath) {
UIView.transition(with: myCollection, duration: 0.5, options: .beginFromCurrentState, animations: {() -> Void in
let cell = self.myCollection.cellForItem(at: indexPath)!
let cellCenter = CGPoint(x: cell.center.x, y: cell.center.y + 50)
if cell.bounds.height == from {
let sizee = CGRect(x: cell.center.x, y: cell.center.y, width: cell.frame.width, height: to)
self.myCollection.cellForItem(at: indexPath)?.frame = sizee
self.myCollection.cellForItem(at: indexPath)?.center = cellCenter
for x in self.indexPathss {
if x.row > indexPath.row {
print("this is cells")
print(self.myCollection.visibleCells)
let cell = self.myCollection.cellForItem(at: x)!
cell.center.y += 100
}
}
}
}, completion: {(_ finished: Bool) -> Void in
print("finished animating of cell!!!")
})
}

It looks like you're changing cell sizes and positions manually. The UICollectionView won't be aware of these changes and therefore its size is not changing. The last cells simply move out of the visible area of the collection view.
I haven't done something like this before, but have a look at performBatchUpdates(_:completion:). I'd try to reload the cell that you want to make bigger using that method. Your layout will have to supply the correct attributes, i.e. size.
From the screenshots it looks like maybe you could use a UITableView instead? If that's a possibility it might simplify things. It also has a performBatchUpdates(_:completion:) method, and the UITableViewDelegate would then have to provide the correct heights for the rows.

Because of spacing of cell we used collectionView and I think changing height of tableviewcell is easier than collectionviewcell if you have an idea for spacing of tablviewcell wi will be glad to have your idea.

Related

How can I automatically round the edges of a popup view?

I am working on a popup view for an app I am making. If you take a second to look at the image attached below, you will see that the top edges are rounded, but the bottom edges are not. This is because I only rounded the edges of the view (it is lowest in the hierarchy). I cannot round the edges of the images (the colorful boxes) because they are tables in a scrolling view. The only solution I can think of is a very ugly one where I mask the bottom edges with a UIImageView that appears once the popup has faded in. Does anyone have a better solution? If so, I would greatly appreciate your help. Also, my scrolling view is not yet functional, so that is not referenced here and the solution (if functional) should work regardless.
My code:
allSeenPopover.layer.cornerRadius = 5
userProfile.layer.cornerRadius = 15
colorBackground.layer.cornerRadius = 15
colorBackground.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
#IBAction func loadUserProfile(_ sender: Any) {
if darken.alpha == 0 {
darken.alpha = 1
self.view.addSubview(userProfile)
userProfile.center = self.view.center
userProfile.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
userProfile.alpha = 0
UIView.animate(withDuration: 0.3) {
self.largeDropShadow.alpha = 0.3
self.userProfile.alpha = 1
self.userProfile.transform = CGAffineTransform.identity
}
}
else {
UIView.animate(withDuration: 0.2, animations: {
self.userProfile.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
self.userProfile.alpha = 0
self.darken.alpha = 0
self.largeDropShadow.alpha = 0
}) { (success:Bool) in
self.userProfile.removeFromSuperview()
}
}
}
The image I was referring to:
Since the view you want rounded is inside a table view cell, you must take care that the view is created added only once per reused cell.
Each time a reused cell scrolls into view, check to see if it has had an imageView subview (using a tag that is unique within the cell is a quick way to make that check). If you don't have one, create it and then configure it for the particular row, otherwise just configure it for the particular row...
(warning, I'm not swift fluent, but the idea should be clear)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as UITableViewCell!
let imageView:UIImageView = cell.viewWithTag(99) as? UIImageView
if (!imageView) {
// this code runs just once per reused cell, so setup
// imageView properties here that are row-independent
imageView = UIImageView()
imageView.tag = 99 // so we'll find this when the cell gets reused
imageView.layer.cornerRadius = 15.0
imageView.clipsToBounds = true
// any other props that are the same for all rows
imageView.frame = // your framing code here
cell.addSubview(imageView)
}
// this code runs each time a row scrolls into view
// so setup properties here that are row-dependent
imageView.image = // some function of indexPath.row
return cell
}

UIGraphicsGetImageFromCurrentImageContext() returning blank image in UITableViewCell

I've been successfully using the function below to take snapshots of UIViews, however, now that I'm using it inside a UITableViewCell, it no longer works.
static func pictureTaker(_ rawView: UIView) -> UIImageView {
UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0)
rawView.drawHierarchy(in: rawView.bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext();
let viewImage = UIImageView(image: screenshot)
UIGraphicsEndImageContext();
viewImage.frame = rawView.frame
return viewImage
}
Here is a trimmed down version of the function I'm using to populate the UITableViewCell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = gamesHolder.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath) as! StandardGameCell
let row = indexPath.row
let boardPreviewTemplate = UIView(frame: CGRect(x: 0, y: 0, width: cell.previewBoard.frame.width, height: cell.previewBoard.frame.height))
GameManagement.setupBoard(boardPreviewTemplate, boardItems: gamesArray[row].board)
let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)
//Removing preview board items and replacing with picture
cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
cell.previewBoard.addSubview(gamePicture)
return cell
}
In the above tableView function, I'm programmatically creating a new view to replicate cell.previewBoard, but I also tried directly snapshotting cell.previewBoard and that didn't work either. I think the issue has something to do with the timing of the UITableViewCell loading.
I'm also 100% positive that the function is returning an image (but a blank one) and that it's where I'm intending it to be. If I change UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0) to UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, true, 0), the returned image shows up as entirely black.
As one final note, I discovered that boardPreviewPicture.snapshotView(afterScreenUpdates: true) would return an image, but it made scrolling through the UITableView very slow. Maybe there is another workaround where I can use snapshotView() instead.
I ran into the same problem. What I noticed is that when UIGraphicsBeginImageContextWithOptions is called inside tableView(_:cellForRowAt:), the result from UIGraphicsGetImageFromCurrentImageContext() is blank. Take note that in the documentation it is stated that UIGraphicsGetImageFromCurrentImageContext:
This function may be called from any thread of your app.
However, the solution that I discovered is to call it inside the main thread. In your case, maybe you can do something like:
DispatchQueue.main.async {
let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)
//Removing preview board items and replacing with picture
cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
cell.previewBoard.addSubview(gamePicture)
}

UITableView.visibleCells not capturing all visible cells

I'm trying to animate a table view cells on the initial load. The animation works fine for all the cells, except for the last one at the bottom of the table which refuses to animate.
This is because the UITableView.visibleCells does not return this cell at the end. (it returns cells 0-12, the last cell is at index 13 and is clearly visible)
Here is the code. Is there anything I can do to ensure all the cells get animated?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
let cells = tableView.visibleCells
let tableHeight: CGFloat = clubsTable.bounds.size.height
for i in cells {
let cell: UITableViewCell = i as UITableViewCell
cell.transform = CGAffineTransformMakeTranslation(0, tableHeight)
}
var index = 0
for a in cells {
let cell: UITableViewCell = a as UITableViewCell
UIView.animateWithDuration(1.5, delay: 0.05 * Double(index), usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
cell.transform = CGAffineTransformMakeTranslation(0, 0);
}, completion: { (complete) in
})
index += 1
}
}
That is because the height of your table is less than the height of 13 cells. Thus it animates 12 cells. What you can do is to make the table height bigger in 30px (or any px until it contains 13 cells), and after the animation is done, change the height of the tableView back to normal.
after you call let cells = tableView.visibleCells can't you just do something like
let indexPath = NSIndexPath(forRow: cells.count, inSection: 0)
cells.append(tableView.cellForRowAtIndexPath(indexPath))
Going off memory on those function signatures but you get the point.

Populating UICollectionView in reverse order

I would like to populate UICollectionView in reverse order so that the last item of the UICollectionView fills first and then the second last and so on. Actually I'm applying animation and items are showing up one by one. Therefore, I want the last item to show up first.
Swift 4.2
I found a simple solution and worked for me to show last item first of a collection view:
Inside viewDidLoad() method:
collectionView.transform = CGAffineTransform.init(rotationAngle: (-(CGFloat)(Double.pi)))
and inside collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) method before returning the cell:
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
(optional) Below lines will be necessary to auto scroll and show new item with smooth scroll.
Add below lines after loading new data:
if self.dataCollection.count > 0 {
self.collectionView.scrollToItem(at: //scroll collection view to indexpath
NSIndexPath.init(row:(self.collectionView?.numberOfItems(inSection: 0))!-1, //get last item of self collectionview (number of items -1)
section: 0) as IndexPath //scroll to bottom of current section
, at: UICollectionView.ScrollPosition.bottom, //right, left, top, bottom, centeredHorizontally, centeredVertically
animated: true)
}
I'm surprised that Apple scares people away from writing their own UICollectionViewLayout in the documentation. It's really very straightforward. Here's an implementation that I just used in an app that will do exactly what are asking. New items appear at the bottom, and the while there is not enough content to fill up the screen the the items are bottom justified, like you see in message apps. In other words item zero in your data source is the lowest item in the stack.
This code assumes that you have multiple sections, each with items of a fixed height and no spaces between items, and the full width of the collection view. If your layout is more complicated, such as different spacing between sections and items, or variable height items, Apple's intention is that you use the prepare() callback to do the heavy lifting and cache size information for later use.
This code uses Swift 3.0.
//
// Created by John Lyon-Smith on 1/7/17.
// Copyright © 2017 John Lyon-Smith. All rights reserved.
//
import Foundation
import UIKit
class InvertedStackLayout: UICollectionViewLayout {
let cellHeight: CGFloat = 100.00 // Your cell height here...
override func prepare() {
super.prepare()
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttrs = [UICollectionViewLayoutAttributes]()
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numberOfSectionItems = numberOfItemsInSection(section) {
for item in 0 ..< numberOfSectionItems {
let indexPath = IndexPath(item: item, section: section)
let layoutAttr = layoutAttributesForItem(at: indexPath)
if let layoutAttr = layoutAttr, layoutAttr.frame.intersects(rect) {
layoutAttrs.append(layoutAttr)
}
}
}
}
}
return layoutAttrs
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let contentSize = self.collectionViewContentSize
layoutAttr.frame = CGRect(
x: 0, y: contentSize.height - CGFloat(indexPath.item + 1) * cellHeight,
width: contentSize.width, height: cellHeight)
return layoutAttr
}
func numberOfItemsInSection(_ section: Int) -> Int? {
if let collectionView = self.collectionView,
let numSectionItems = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section)
{
return numSectionItems
}
return 0
}
override var collectionViewContentSize: CGSize {
get {
var height: CGFloat = 0.0
var bounds = CGRect.zero
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numItems = numberOfItemsInSection(section) {
height += CGFloat(numItems) * cellHeight
}
}
bounds = collectionView.bounds
}
return CGSize(width: bounds.width, height: max(height, bounds.height))
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if let oldBounds = self.collectionView?.bounds,
oldBounds.width != newBounds.width || oldBounds.height != newBounds.height
{
return true
}
return false
}
}
Just click on UICollectionView in storyboard,
in inspector menu under view section change semantic to Force Right-to-Left
I have attach an image to show how to do it in the inspector menu:
I'm assuming you are using UICollectionViewFlawLayout, and this doesn't have logic to do that, it only works in a TOP-LEFT BOTTOM-RIGHT order. To do that you have to build your own layout, which you can do creating a new object that inherits from UICollectionViewLayout.
It seems like a lot of work but is not really that much, you have to implement 4 methods, and since your layout is just bottom-up should be easy to know the frames of each cell.
Check the apple tutorial here: https://developer.apple.com/library/prerelease/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html
The data collection does not actually have to be modified but that will produce the expected result. Since you control the following method:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
Simply return cells created from inverting the requested index. The index path is the cell's index in the collection, not necessarily the index in the source data set. I used this for a reversed display from a CoreData set.
let desiredIndex = dataProfile!.itemEntries!.count - indexPath[1] - 1;
Don't know if this still would be useful but I guess it might be quite useful for others.
If your collection view's cells are of the same height there is actually a much less complicated solution for your problem than building a custom UICollectionViewLayout.
Firstly, just make an outlet of your collection view's top constraint and add this code to the view controller:
-(void)viewWillAppear:(BOOL)animated {
[self.view layoutIfNeeded]; //for letting the compiler know the actual height and width of your collection view before we start to operate with it
if (self.collectionView.frame.size.height > self.collectionView.collectionViewLayout.collectionViewContentSize.height) {
self.collectionViewTopConstraint.constant = self.collectionView.frame.size.height - self.collectionView.collectionViewLayout.collectionViewContentSize.height;
}
So basically you calculate the difference between collection view's height and its content only if the view's height is bigger. Then you adjust it to the constraint's constant. Pretty simple. But if you need to implement cell resizing as well, this code won't be enough. But I guess this approach may be quite useful. Hope this helps.
A simple working solution is here!
// Change the collection view layer transform.
collectionView.transform3D = CATransform3DMakeScale(1, -1, 1)
// Change the cell layer transform.
cell.transform3D = CATransform3DMakeScale(1, -1, 1)
It is as simple as:
yourCollectionView.inverted = true
PS : Same for Texture/IGListKit..

Custom UIViewController transition where UITableViewCell grows to full screen and back?

I haven't been able to find any examples of this online. How can I achieve the following effect? Instead of the standard slide-to-left effect when tapping a table row, I'd like the view controller transition animation to look like the following:
User taps a cell in the table
The cell starts growing to fill the screen, pushing other rows above and below it "offscreen".
As the cell grows, cells elements (text, images, etc.) cross-fade into the new view's contents until the new view completely fills the screen.
I'd like to also be able to interactively transition back into the table view by dragging up from the bottom edge, such that the reverse of the above is achieved. i.e. view starts shrinking back into a normal table view cell as the "offscreen" cells animate back into position.
I've thought about taking a snapshot of the table and splitting it up at the points above and below the cell, and animating these snapshots offscreen as part of a custom view controller transition. Is there a better way? Ideally I'd like to not take snapshots, as I may want to have animations, etc., still happening in the table view rows as they fade offscreen.
First thing first, i have seen your post today and his is written in swift programming language.
the follwing code is to expand the selected cell to the full screen, where the subviews will fade out and background image will expands.
First complete cellForRowAtIndexPath, then in didSelectRowAtIndexPath,
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
isSelected = true
selectedCellIndex = indexPath.row
tableView.beginUpdates()
var cellSelected: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
cellSelected.frame = tableCities.rectForRowAtIndexPath(indexPath)
println("cell frame: \(cellSelected) and center: \(cellSelected.center)")
let newCell = cellSelected.frame.origin.y - tableOffset
println("new cell origin: \(newCell)")
var tempFrame: CGRect = cellSelected.frame
variableHeight = cellSelected.frame.origin.y - tableOffset
tempFrame.size.height = self.view.frame.height
println("contentoffset: \(tableView.contentOffset)")
let offset = tableView.contentOffset.y
println("cell tag: \(cellSelected.contentView.tag)")
let viewCell: UIView? = cellSelected.contentView.viewWithTag(cellSelected.contentView.tag)
println("label: \(viewCell)")
viewCell!.alpha = 1
UIView.animateWithDuration(5.0, delay: 0.1, options: UIViewAnimationOptions.BeginFromCurrentState, animations: {
tableView.setContentOffset(CGPointMake(0, offset + self.variableHeight), animated: false)
tableView.contentInset = UIEdgeInsetsMake(0, 0, self.variableHeight, 0)
tableView.endUpdates()
cellSelected.frame = tempFrame
viewCell!.alpha = 0
println("contentoffset: \(tableView.contentOffset)")
}, completion: nil)
}
then update your heightForRowAtIndexPath, as
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if isSelected && (selectedCellIndex == indexPath.row) {
return self.view.frame.height
}else {
return 100
}
}
Ignore this if already solved. and this might be helpful to others.

Resources