MultipleCollectionViews with two arrays in Swift - arrays

At the moment I'm parsing data from a Json and saving it local. Then I fill up a CollectionView with the data from the first Array. But now I want to add a second CollectionView that depends on the selection from the first CollectionView.
My Json structure:
struct Posts: Codable {
let id: Int
let name: String
let ImageURL: String
let Rarity: [PostType]
}
struct PostType: Codable {
let type: String
let test: int
let test2: String
}
What I tried:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.PostsCollectionView{
return getPostsFromDisk().count
}else
{
return feedWrapper.count //not working
}
}
var feedWrapper:[PostType] = []
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.PostsCollectionView{
let cell = PostsCollectionView.dequeueReusableCell(withReuseIdentifier: "PostsUICollectionViewCell", for: indexPath) as! PostsUICollectionViewCell
cell.PostsNameLbl.text = getPostsFromDisk()[indexPath.row].name
cell.PostsImage.sd_setImage(with: URL(string: getPostsFromDisk()[indexPath.row].ImageURL))
return cell
}else
{
let cell = PostsRareCollectionView.dequeueReusableCell(withReuseIdentifier: "PostsRareCollectionViewCell", for: indexPath) as! PostsRareCollectionViewCell
if(feedWrapper[indexPath.row].type == "normal"){
cell.PostsRareImageView.image = [imageFromAssets]
}
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
PostsName.text = getPostsFromDisk()[indexPath.row].name
//thougt i could save the selecte array to feedWrapper but its not working like expected
feedWrapper = getPostsFromDisk()[indexPath.row].Rarity
}
I think my var feedWrapper isn't filled up at the beginning but I'm not sure how to do this
I hope someone can help me with this
Thanks in advance

You need to reload the collectionView. You can use this code:
Declare your variable with didset (Assumed that your array is inside view controller)
var feedWrapper:[PostType] = [] {
didSet {
self.yourCollectionView.reloadData()
}
}

Related

Drag and drop in collectionView with data

I'm trying to add drag and drop to my collectionView, and I have some issues;
When I drag my cell, the data disappear (UITextField). I think I don't save the data in my dataArray correctly...
And the second issue: the drag and drop "works" (without data lol) in the collectionView where I create the cell, but I can't drag this cell to another collection view. The animation is play, but I can't perform a drop.
this I my code:
fist, my class Task, my dataArray is of this type:
class Task: NSObject, Codable, NSItemProviderReading, NSItemProviderWriting {
var task: String?
var button: Bool?
init(task: String, button: Bool) {
self.task = task
self.button = button
}
static var writableTypeIdentifiersForItemProvider: [String] {
return [(kUTTypeData) as String]
}
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: #escaping (Data?, Error?) -> Void) -> Progress? {
let progress = Progress(totalUnitCount: 100)
do {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(self)
let json = String(data: data, encoding: String.Encoding.utf8)
progress.completedUnitCount = 100
completionHandler(data, nil)
} catch {
completionHandler(nil, error)
}
return progress
}
static var readableTypeIdentifiersForItemProvider: [String] {
return [(kUTTypeData) as String]
}
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Self {
let decoder = JSONDecoder()
do {
let myJSON = try decoder.decode(Task.self, from: data)
return myJSON as! Self
} catch {
fatalError("Err")
}
}
Here I try to save my data and perform drag and drop:
extension LTHM_Todo: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier, for: indexPath as IndexPath) as! MyCollectionViewCell
cell.myText.text = dataArray[indexPath.row].task
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 300, height: 40)
}
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
return true
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArray.count
}
private func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier, for: indexPath as IndexPath) as! MyCollectionViewCell
return cell
}
extension LTHM_Todo: UICollectionViewDragDelegate, UICollectionViewDropDelegate {
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
{
if session.localDragSession != nil
{
if collectionView.hasActiveDrag
{
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
else
{
return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
}
}
else
{
return UICollectionViewDropProposal(operation: .forbidden)
}
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
{
let destinationIndexPath: IndexPath
if let indexPath = coordinator.destinationIndexPath
{
destinationIndexPath = indexPath
}
else
{
let section = collectionView.numberOfSections - 1
let row = collectionView.numberOfItems(inSection: section)
destinationIndexPath = IndexPath(row: row, section: section)
}
switch coordinator.proposal.operation
{
case .move:
reorderItem(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
break
default:
return
}
}
fileprivate func reorderItem(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
let items = coordinator.items
if items.count == 1, let item = items.first, let sourceIndexPath = item.sourceIndexPath
{
var dIndexPath = destinationIndexPath
if dIndexPath.row >= collectionView.numberOfItems(inSection: 0)
{
dIndexPath.row = collectionView.numberOfItems(inSection: 0) - 1
}
collectionView.performBatchUpdates({
self.dataArray.remove(at: sourceIndexPath.row)
self.dataArray.insert(item.dragItem.localObject as! Task, at: dIndexPath.row)
collectionView.deleteItems(at: [sourceIndexPath])
collectionView.insertItems(at: [dIndexPath])
})
coordinator.drop(item.dragItem, toItemAt: dIndexPath)
}
}
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let item = self.dataArray[indexPath.row]
let itemProvider = NSItemProvider(object: item)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = item
return [dragItem]
}

How to make cell index path.row == an items index in array?

I have an array of 100 items called cellNames of type String. I have a collection view made up of 1 section with 100 customCollectionViewCell rows in it. I'm looking for something like this-
for cellName in cellNames {
if cellName.index == cellNames[indexpath.row] {
let cellName = cell.nameTextField
}
}
So in summation I need the cellName for index 0...100 == cellForRowAt 0...100
It looks like you are creating 100 static cells and trying to populate them with cellNames. A correct approach would be to conform to UICollectionViewDataSource and set the number of items to cellNames's count and use the indexPath provided in cellForItemAt to access each element of your array.
class ViewController: UIViewController {
let cellNames = ["1", "2", "3"] // ....
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellNames.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCellId", for: indexPath) as! CustomCell
let cellName = cellNames[indexPath.item]
cell.nameTextField.text = cellName
return cell
}
}

append 2 arrays in a collection view

So I have 2 arrays, 1 being a photoPost array and the other a videoPost array. Now is it possible to display both these arrays in the one collection view at the same time? I tried to use the method below but does not work out. I'm not sure if I'm even doing it right.Much help would be appreciated. Thanks in advance
var photoPosts = [photoPost]()
var videoPosts = [videoPost]()
func retrieveData(){
let ref = Database.database().reference().child("videoPost").child(uid)
ref.observe(.childAdded, with: { (snapshot) in
let dictionary = videoPost(snapshot: snapshot)
self.videoPosts.append(dictionary)
})
let ref = Database.database().reference().child(“photoPost").child(uid)
ref.observe(.childAdded, with: { (snapshot) in
let dictionary = photoPost(snapshot: snapshot)
self. photoPosts.append(dictionary)
})
self.newsfeedCollectionView?.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return videoPosts.count + photoPosts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! newsfeedCollectionViewCell
cell. photoPost = photoPosts[indexPath.item]
cell.videoPost = videoPosts[indexPath.item] // fatal error:index out of range
return cell
}
Please Try this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! newsfeedCollectionViewCell
if indexPath.item < photoPosts.count {
cell. photoPost = photoPosts[indexPath.item]
} else {
cell.videoPost = videoPosts[indexPath.item-photoPosts.count]
}
return cell
}

Swift: How to Display Parsed JSON Data in CollectionView inside a TableView?

I'm trying to display my data as this image.
My problem is that data displayed inside table view rows are all the same thing, while it should display all data of the array.
This is the code I used to display the collectionView inside the tableView:
var onlineNews = ["local", "Economy", "Variety", "international", "sport"]
var storedOffsets = [Int: CGFloat]()
var tableIndexPath: IndexPath!
#IBOutlet var listTableView: UITableView!
var tableIndex: Int = 0
var categoryResults = [JSON]() {
didSet {
listTableView.reloadData()
}
}
let requestManager = RequestManager()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for i in onlineNews {
requestManager.categoryList(sectionName: i)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return onlineNews.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! NewsTableViewCell
tableIndex = indexPath.row
return cell
}
func tableView(_ tableView: UITableView,
willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? NewsTableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: (indexPath as NSIndexPath).row)
tableViewCell.collectionViewOffset = storedOffsets[(indexPath as NSIndexPath).row] ?? 0
}
func tableView(_ tableView: UITableView,
didEndDisplaying cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? NewsTableViewCell else { return }
storedOffsets[(indexPath as NSIndexPath).row] = tableViewCell.collectionViewOffset
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return categoryResults.count
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColCell",
for: indexPath) as! NewsCollectionViewCell
cell.contentType.text = categoryResults[indexPath.row]["ContentType"].stringValue **// This is where I get the same values for all table view rows**
cell.sectionName.text = onlineNews[tableIndex]
return cell
}
I'm sure someone can absolutely help me with this as I know that it takes only a small tweak to make it work, but not sure where.
Update:
I have followed a way that I believe should work, which is to declare the JSON array to be like this [[JSON]], and then use categoryResults[collection.tag][indexPath.item]["ContentType"].stringValue to get to the value. However, it gives me "index out of range" message. Do you have any clue how can I solve the issue?
var onlineNews = ["local", "Economy", "Variety", "international", "sport"]
var storedOffsets = [Int: CGFloat]()
#IBOutlet var listTableView: UITableView!
var tableIndex: Int = 0
var categoryResults = [[JSON]]() { // updated
didSet {
listTableView.reloadData()
}
}
let requestManager = RequestManager()
override func viewDidLoad() {
super.viewDidLoad()
requestManager.resetCategory()
updateSearchResults()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.updateSearchResults), name: NSNotification.Name(rawValue: "categoryResultsUpdated"), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for i in 0..<onlineNews.count {
requestManager.categoryList(sectionName: onlineNews[i])
}
}
func updateSearchResults() {
categoryResults = [requestManager.categoryResults] // updated
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return onlineNews.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! NewsTableViewCell
tableIndex = indexPath.row
return cell
}
func tableView(_ tableView: UITableView,
willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? NewsTableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: (indexPath as NSIndexPath).row)
tableViewCell.collectionViewOffset = storedOffsets[(indexPath as NSIndexPath).row] ?? 0
}
func tableView(_ tableView: UITableView,
didEndDisplaying cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? NewsTableViewCell else { return }
storedOffsets[(indexPath as NSIndexPath).row] = tableViewCell.collectionViewOffset
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return categoryResults[collectionView.tag].count // updated
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColCell",
for: indexPath) as! NewsCollectionViewCell
cell.contentType.text = categoryResults[collectionView.tag][indexPath.row]["ContentType"].stringValue // updated
return cell
}
This the content of RequestManager class (where I call the API):
var categoryResults = [JSON]()
func categoryList(sectionName: String) {
let url = "http://mobile.example.com/api/Content?MobileRequest=GetCategoryNews&PageNo=1&RowsPerPage=10&Category=\(sectionName)&IssueID=0&Type=online"
print("this is the url \(url)")
Alamofire.request(url, method: .get).responseJSON{ response in
if let results = response.result.value as? [String:AnyObject] {
let items = JSON(results["Data"]?["OnlineCategoryNews"]! as Any).arrayValue
self.categoryResults += items
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "categoryResultsUpdated"), object: nil)
}
}
}
func resetCategory() {
categoryResults = []
}
deinit {
NotificationCenter.default.removeObserver(self)
}
Update 2:
And here is the method where the collectionView.tag is assigned. This is added to the tableViewCell class:
func setCollectionViewDataSourceDelegate
<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>
(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.bounds.size.width = self.bounds.size.width
collectionView.reloadData()
}
Collection view delegate methods don't know context of their collection view. You should calculate onlineNews index depending on the collectionView instance instead of using indexPath.row, which is internal collection view index path.
Edit: better option (to avoid scrolling and layout issues) is to use single collection view, where cells are grouped in rows. If you don't want to make layout manager, you can achieve such layout by adding small, but very wide separator views between sections
Edit2:
cell.contentType.text = categoryResults[indexPath.row]["ContentType"].stringValue
uses local indexPath of this collection view. You could assign tag to tableViewCell.collectionView with a value of desired categoryResults index. Then you can use this tag as in categoryResults[collectionView.tag]

Swift: Array of audio files

Using Swift 2 how would I create an array of audio files to be used on a collection view?
There are 4 options "Ocean", "Birds", "Nature", "Waves" and they are displayed as cells.
When a cell is clicked, I need it to play the sound. Can you please help?
var imageArray = [UIImage(named: "ocean"),UIImage(named: "birds"),UIImage(named: "nature"),UIImage(named: "wave")]
var nameArray = ["Ocean","Birds","Nature","Waves"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! homeCollectionViewCell
cell.iconImage?.image = imageArray[indexPath.item!]
cell.label.text = nameArray[indexPath.item!]
return cell
}
Do you know how to play the sound? Have you done it yet?
If so, you need to use the following function and do something like this:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
//grab the sound from the array
let sound = your_array_with_sounds[indexPath.row]
//if you want to play the sound in this view:
<code to play the sound>
//if you want to play the sound in another view:
<perform the segue and pass the sound to the view you want>
}
I hope this help. If not, add a bit more of information to your question and we will help you.
You need to use the AVAudioPlayer Class for Play the Audio
For that you need to import the below class
import AVFoundation
you need to create the function with Following code
let audioName:String!
func playAudio(){
let url = NSURL.fileURLWithPath(
NSBundle.mainBundle().pathForResource(audioName,
ofType: "mp3")!)
var error: NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
if let err = error {
println("audioPlayer error \(err.localizedDescription)")
} else {
audioPlayer?.delegate = self
audioPlayer?.prepareToPlay()
}
if let player = audioPlayer {
player.play()
}
}
in collection View
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
audioName = arrayName[indexPath.row]
}
Swift 4.0
let audioName:String!
func playAudio(){
let url = NSURL.fileURL(
withPath: Bundle.main.path(forResource: audioName,
ofType: "mp3")!)
var error: NSError?
let audioPlayer = try? AVAudioPlayer(contentsOf: url)
audioPlayer?.delegate = self
audioPlayer?.prepareToPlay()
if let player = audioPlayer {
player.play()
}
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
audioName = arrayName[indexPath.row]
}

Resources