Drag & Drop between two UICollectionViews - ios

I need to drag a Cell from CollectionView One and drop it to
CollectionView Two.
The Drag & Drop inside one CollectionView is no Problem, but how
can i get the Cell out of CollectionView One to CollectionView Two?
Any ideas? Any projects or frameworks that have already solved this problem?
Thanks for helping!

https://github.com/Ice3SteveFortune/i3-dragndrop Check this out - its a helper I'm working on to achieve just that. It also supports tableviews
UPDATE
I've recently released the second version of this codebase, called BetweenKit. Its now a fully-fledged drag-and-drop framework.
Hope it proves useful !

When you select the cell from the first collection view, remove it from this collection view, create a new view as copy of that cell place it as subview of the superview on top of all views. Make that view movable using pan gestures. As soon as you "drop" this intermediary cell, detect its position and add it to the current collection view.

Ok, here is the simplest flow ever for the following example:
Add UIGestureRecognizer for every of UICollectionView.
Connect every gesture recognizer with one method:
#IBAction func longPressGestureChanged(recognizer: UILongPressGestureRecognizer) { ... }
Within UIViewController add #IBOutlet for each of UICollectionView:
#IBOutlet var collectionViewGreen: UICollectionView!
#IBOutlet var collectionViewYellow: UICollectionView!
Implement gesture recognizer method to detect changes:
#IBAction func longPressGestureChanged(recognizer: UILongPressGestureRecognizer) {
let globalLocation = recognizer.locationInView(view)
if CGRectContainsPoint(collectionViewGreen.frame, globalLocation) {
//you cover green collection view
let point = view.convertPoint(globalLocation, toView: collectionViewGreen)
if let indexPath = collectionViewGreen.indexPathForItemAtPoint(point) {
//you cover cell in green collection view
} else {
//you do not cover any of cells in green collection view
}
} else if CGRectContainsPoint(collectionViewYellow.frame, globalLocation) {
//you cover yellow collection view
let point = view.convertPoint(globalLocation, toView: collectionViewYellow)
if let indexPath = collectionViewYellow.indexPathForItemAtPoint(point) {
//you cover cell in yellow collection view
} else {
//you do not cover any of cells in yellow collection view
}
} else {
//you do not cover any of collection views
}
}

Related

How to reset UICollectionView in Swift

I am developing a card game using UICollectionView. I override the draw() function of the UICollectionViewCell to arrange my images when load the cells. Every new game, i change the number of cards in the game. So the number and size of the cells change. I have to do all operations for UICollectionView in the viewWillAppear() method because of the third part library i use. My question is that how to reset all the works which done over the UICollectionView before. I want clear UICollectionView during each game without loading UIViewController again. Wanna do it in viewWillAppear() method. I want to clear UICollectionView because my images overlap if don't have clear UICollectionView in new game.
Note : I removed all the subviews from UICollectionView like below, but it didn't work
let subViews = gameCollectionView.subviews
if subViews != nil {
for view in subViews {
view.removeFromSuperview()
}
}
Clearing the collection is from clearing it's dataSource array like
arr = []
gameCollectionView.reloadData()
if you need to clear the cell then do it inside cellForRowAt
let cell = ///
cell.contentView.subviews//// clear here
or override
override func prepareForReuse() {
super.prepareForReuse()
// clear any subview here
}
if you correctly assign values for all properties of the collection cell every run of cellForRowAt without adding subviews , then you'll have no overlappings
You need to remove every subview from each cell's contentView, not from your collection view
gameCollectionView.visibleCells.forEach { $0.contentView.subviews.forEach { $0.removeFromSuperview() } }
But, I'm not sure for what you need to override draw method. You should rather create what you need just once, for example in awakeFromNib and then you should just change properties of your image views, etc. depending on content, for this you purpose you can use collection view's data source method cellForItemAt.

UITableViewCell - drag whole row to move?

I'm trying to use a UITableViewController and allow the user to reorganize my data by dragging the rows. This is working fine, except that the user has to tap on the relatively small UITableViewCellReorderControl on the right quarter of the cell to drag the row.
Here's an example:
As far as I can tell there's no way to get ahold of the UITableViewCellReorderControl or access its size or behavior through the UITableViewDelegate. Is there a way to customize drag behavior so that you can drag anywhere on the cell to move it up or down?
just expand the reorderControl to the size of cell.
extension UITableViewCell {
var reoderControl: UIView? {
for view in self.subviews {
if view.description.contains("UITableViewCellReorderControl") {
return view
}
}
return nil
}
}
class Cell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
reoderControl?.frame = bounds
}
}

Swift: UICollectionView Paging Enabled

I have a UICollectionView with 3 full-screen cells and want to know when a cell is focused full-screen upon swipe. The cellForItemAtIndexPath.isFocused method isn't working for me. Is this the correct use of isFocused method or should I do it another way?
What ever, I can get from your question. I think you want to know on which page it is. So, Here is how i did it:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
for cell in colViewSol.visibleCells {
let indexPath = colViewSol.indexPath(for: cell)
// here add the code whatever you want to do
}
}

Use UICollectionViews to create dynamic and multiple features

I'm interested in creating a view that contains multiple features that users can scroll down and see i.e. pictures, description, comments, carousels etc. I am aware that the UICollectionView is able to provide this type of layout. I initially thought UITableViews would be the best approach.
I have looked at several tutorials and GitHub repos but majority of them just use a UICollectionView in a standard grid layout. I've also looked at IGListKit used by Instagram and some the tutorials linked to it.
I'm aiming to get something like the KitchenStories app:
I was wondering if someone could advice me in terms of the direction and approach best for this.
Don't try to do too much with any single view, even a UICollectionView.
The screen you've shown has a UITabBarController manage its top-level arrangement. The currently selected tab (“Home”) has a UINavigationController managing its content.
On the top of the navigation stack is, probably, a collection view or a table view. Either could be used here because the elements are visually laid out as screen-width rows in a stack. A table view is simpler because then you don't have to worry about setting up the layout.
The table view has several visible rows, each different:
The title/image row (“Easy seafood paella”)
The ratings row
The export row (hearts / save / share)
The comments row
The creator row (I assume, since it looks like it's probably a headshot and a name)
And there are probably even more unique rows out of view.
In your storyboard, you can design each of these rows as a prototype row in the table view controller's scene. Or you can design the table view with static content rows, which is easier if you won't need to change the order of the rows or duplicate any rows at run time.
“But Rob,” you say, “I can't fit all those rows into the table view in my storyboard!” Make the storyboard scene taller. UIKit will resize it at run time to fit the device screen.
In each row, drag in and arrange whatever subviews you need for that row's data. For example, the title/image row needs a UIImageView and a UILabel. The ratings row needs a label, probably a custom view to display and edit the stars, and perhaps a stack view for layout.
For each row, you'll need a separate subclass of UITableViewCell with outlets to that row's views. To pass the data to each cell for display, make each cell conform to a protocol:
protocol RecipeUsing {
var recipe: Recipe? { get set }
}
Then, in your table view controller, you set it like this if you're using static content:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath)
if let user = cell as? RecipeUsing {
user.recipe = recipe
}
return cell
}
You'll need a RecipeTitleImageCell with outlets to the UIImageView and UILabel. Something like this:
class RecipeTitleImageCell: UITableViewCell, RecipeUsing {
#IBOutlet var label: UILabel!
// UITableViewCell has an imageView property that's not an outlet 😭
#IBOutlet var myImageView: UIImageView!
var recipe: Recipe? {
didSet {
guard let recipe = recipe else { return }
label.text = recipe.title
myImageView.image = recipe.image
}
}
}
And for the ratings row, you'll want something like this:
class RecipeRatingsCell: UITableViewCell, RecipeUsing {
#IBOutlet var ratingControl: RatingControl!
#IBOutlet var label: UILabel!
var recipe: Recipe? {
didSet {
guard let recipe = recipe else { return }
ratingControl.rating = recipe.ratings.reduce(0, +) / recipe.Double(ratings.count)
if ratings.count < 5 { label.text = "Too few ratings" }
else { label.text = "\(ratings.count) ratings" }
}
}
}

UITableView & UICollectionView showing same data in 2 different situations

I have a scenario in which I require to display some data in UITableView when user clicks on a particular UIButton and display the same data in UICollectionView when clicked on another UIbutton . But in the same view controller .
First when table view is selected we are going for data in table view -
it would show data in table view format
Second when collection view button is clicked we are going for displaying data in collection view.
I think we can achieve this by the following ways -
1)Creating 2 views in scene dock one having table view and the other having collection view and using them in our code.
2)Creating 2 .xib files of UIView class having TableView & CollectionView and loading them on selection of particular button.
So how could I achieve this functionality? Please suggest some ways which could be quite reliable in such cases and if possible share any source codes or links or give detailed explanation which could help iOS novices like me.
You could use UIPageViewController. Just remember to disable scrolling of pageviewcontroller.
It seems like to make custom view container.
Google with keywords iOS custom container, there are many resources.
Custom container view controller transitions
Container View controller quick start
Apple document, about custom container
You can also add both the UItableView and UICollectionView in the same view and make one of them hidden in each state. So by default the first button is selected where the collectionView will be hidden. When user taps on the second button you can hide the table view and unhide the collection view. You can also add animation for transitioning two state to make it look good.
If we simply think about this problem then we can use only one collectionView with two different kind of Layouts and cellViews of different sizes. But I am not sure, how much will be the complexity with Aujtolayout (if you using).
First create two class file with UITableView and UICollectionView baseClass for more detail see below screenshot.
And add datasource and delegate method for both class in implementation file.
Now add following code in .h(interface) file where you want to show table/collection view.
#import "collectionData.h"
#import "tableData.h"
#interface ViewController : UIViewController {
collectionData *collectionObj;
tableData *tableObj;
IBOutlet UIView *viewData;
}
#end
And in .m(implementation) file:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *arrCollection = [[NSBundle mainBundle] loadNibNamed:#"collectionData" owner:self options:nil];
collectionObj = (collectionData *)[arrCollection objectAtIndex:0];
NSArray *arrTable = [[NSBundle mainBundle] loadNibNamed:#"tableData" owner:self options:nil];
tableObj = (tableData *)[arrTable objectAtIndex:0];
[self displayView:tableObj];
}
- (IBAction)btnTableClick:(id)sender {
[self displayView:tableObj];
}
- (IBAction)btnCollectionClick:(id)sender {
[self displayView:collectionObj];
}
- (void)displayView: (UIView *)viewObj {
for(UIView *view in viewData.subviews) {
[view removeFromSuperview];
}
[viewData addSubview:viewObj];
}
Note that its not everything yet that you want it's only basic idea.
Your initial view controller can house the buttons and a "Container View Controller" that will segue between a UITableViewController and a UICollectionViewController based on the button that was pressed. When a button is tapped, the initial view controller will pass along the product data and make itself the child view controller's delegate.
When a cell is selected in either of your child view controllers, they can call methods on the delegate (your initial view controller) and it can act accordingly.
To pass data into whatever child view controller you want to display first (e.g. the UITableViewController), perform a segue when the initial view controller loads.
I would personally suggest any one facing such type of situation to go for Scene Dock which many don't care to use. It requires quite less coding and is very easy. I think it can be considered as an alternative for .xib .
I followed some details about scene dock in the link - http://www.raywenderlich.com/115697/ios-9-storyboards-tutorial-whats-new-in-storyboards
Add a table view and a collection view in the scene dock of your view controller and go for the coding. Here is an example of hoe it can be done.
Main story board -
Here goes the coding with all explannations -
import UIKit
class CategoriesViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UICollectionViewDataSource,UICollectionViewDelegate
{
var mainarray : NSMutableArray!
var cararray : NSMutableArray!
var bikearray : NSMutableArray!
#IBOutlet var TableVieww: UITableView!//table view outlet in scene dock
#IBOutlet var collctnvieww: UICollectionView!//scene dock collection view outlet
#IBOutlet weak var rqdview: UIView!// view on which you would be adding the collection/table view
override func viewDidLoad()
{
super.viewDidLoad()
cararray = ["audii","duster"]
bikearray = ["honda","bajaj"]
TableVieww.dataSource = self
TableVieww.delegate = self
collctnvieww.delegate = self
collctnvieww.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Car(sender: AnyObject)//outlet of car button
{
mainarray = cararray as NSMutableArray
self.TableVieww.reloadData()
self.collctnvieww.reloadData()
}
#IBAction func bike(sender: AnyObject)//outlet of bike button
{
mainarray = bikearray as NSMutableArray
self.TableVieww.reloadData()
self.collctnvieww.reloadData()
}
#IBAction func showcllctnview(sender: AnyObject)//when you want to show collection view for cars/bikes
{
print("collection view called")
collctnvieww.frame = CGRectMake(0, 0, rqdview.frame.width, rqdview.frame.height)//you change the frame size of your views as per your requirement.
self.rqdview.addSubview(collctnvieww)
}
#IBAction func showtableview(sender: AnyObject)//when you want to show table view for cars/bikes
{
print("table view called")
TableVieww.frame = CGRectMake(0, 0, rqdview.frame.width, rqdview.frame.height)
self.rqdview.addSubview(TableVieww)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return mainarray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = TableVieww.dequeueReusableCellWithIdentifier("cellreuse", forIndexPath: indexPath)
cell.textLabel!.text = mainarray[indexPath.row] as? String
return cell
}
//coding for collection view
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return mainarray.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collctnvieww.dequeueReusableCellWithReuseIdentifier("collectioncell", forIndexPath: indexPath) as! CategoriesCollectionViewCell
cell.nmlbl.text = mainarray[indexPath.row] as? String
return cell
}
}
But still I think the code can be shortened if I can use only one data source for table view and collection view. If any one can suggest some tips, it might be helpful.

Resources