Pass data from CollectionView to TabBarController, than to his childs in Swift - ios

I am new to Swift. Unable to find solution for below problem.
Below is a ViewController with CollectionView and When you click on Cell in CollectionView, data from cell(even this who isn't in label and image view, but are in Book array row) must be send to TabBarCollection, than from TabBarCollection I need to send this data to all of child's, like in this image.
Later in childs of TabBar I will set value of Labels in View Controllers from data from choosed Cell.
Book.swift
import UIKit
struct Book {
let title: String
let image: UIImage?
//Here soon will be more "let", and this data will also have to be send to TabBar but it don't will be show I CollectionViewCell
}
extension Book {
static let books: [Book] = [
Book(title: "Antygona", image: UIImage(named: "imgantygona")!),
//etc
]
}
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var bookImageView: UIImageView!
#IBOutlet weak var bookTitle: UILabel!
func setup(with book: Book) {
bookTitle.text = book.title
bookImageView.image = book.image
}
}
ViewController
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
let books = Book.books
override func viewDidLoad() {
super.viewDidLoad()
let fontAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16.0)]
UITabBarItem.appearance().setTitleTextAttributes(fontAttributes, for: .normal)
collectionView.dataSource = self
collectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return books.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "bookCell", for: indexPath) as! CollectionViewCell
let book = books[indexPath.item]
cell.setup(with: book)
return cell
}
}
I saw many solutions but I can't perfectly adapt it to my problem. :(
Thanks for help !
BookInsideViewController.swift
import UIKit
class BookInsideViewController: UIViewController {
#IBOutlet weak var testImageView: UIImageView!
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}

You can use collection view DidSelectItemAt function
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourtabbarIdentifier") as! UITabBarController
// You should access the `imageController` through your `tabBarController`,
// not instantiate it from storyboard.
if let viewControllers = tabBarController.viewControllers,
let imageController = viewControllers.first as? ImageController {
BookInsideViewController.recivedData1 = Books[indexPath.row]
}
navigationController?.pushViewController(tabBarController, animated: true)
}

Related

Cannot see my second collectionView on viewController

So I have my program where I have two collection views on the same ViewController. However when I run the app I can only see one. I have set the constraints on the image the same way on both.
the arrays productsImages and StoresImages are names of images I have in my assets folder. Firthermore pillImage and compStoreImage I have as outlet on my ColectionViewCell files.
HomeViewController.swift
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet weak var productCollectionView: UICollectionView!
#IBOutlet weak var storesCollectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(collectionView == storesCollectionView) {
return storesImages.count
}
return productsImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = productCollectionView.dequeueReusableCell(withReuseIdentifier: "productsCell", for: indexPath) as! productCollectionViewCell
cell.pillImage.image = UIImage(named: productsImages[indexPath.row])
if(collectionView == storesCollectionView) {
let cell2 = storesCollectionView.dequeueReusableCell(withReuseIdentifier: "storesCell", for: indexPath) as! StoreCollectionViewCell
cell2.compstoreImage.image = UIImage(named: storesImages[indexPath.row])
return cell2
}
return cell
}
var productsImages:[String] = ["pillImage", "pillyImage", "pillsImage"]
var storesImages:[String] = ["compstoreImage", "compeImage", "compeStore"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
StoreCollectionViewCell.swift
import UIKit
class StoreCollectionViewCell: UICollectionViewCell {
#IBOutlet var compstoreImage: UIImageView!
}
productCollectionViewCell.swift
import UIKit
class productCollectionViewCell: UICollectionViewCell {
#IBOutlet var pillImage: UIImageView!
}
Screenshot

Unrecognized selector sent to instance for collection view cell as a button which takes data from the button to another view controller

I have a button in main view controller named ViewController.swift and need to take the value of the button to ToDoListViewController.
But it's showing an error :
[ToDoCalender.ViewController.DatebtnTapped:]: unrecognized selector sent to instance 0x7fdb6b808270.
I've tried removing all outlets of the button and set connections again. But it still not working. After that i've tried to use delegate in DateCollectionViewCell and call the DatebtnTapped in the DateCollectionViewCell. Within DatebtnTapped another function dtbtntapped which is defined in my ViewController is called via protocol delegate. But it didn't resolve the problem.
I'm doing this for a project under a course of my university. I'm a beginner.
My original code:
ViewController.swift:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Calendar", for: indexPath) as! DateCollectionViewCell
...
}
...
#IBAction func DatebtnTapped(cell: DateCollectionViewCell) {
let daydata = cell.Datebtn.currentTitle!
let mdata = currentMonth
let ydata = year
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ToDoListViewController") as! ToDoListViewController
vc.day = daydata
vc.month = mdata
vc.year = ydata
self.navigationController?.pushViewController(vc, animated: true)
}
}
DateCollectionViewCell.swift:
import UIKit
class DateCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var Datebtn: UIButton!
}
ToDoListViewController.swift:
import UIKit
class ToDoListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var ListTableView: UITableView!
#IBOutlet weak var date: UILabel!
var day = ""
var month = ""
var year:Int = 0
...
override func viewDidLoad() {
super.viewDidLoad()
date.text = " \(day) - \(month) - \(year)"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Here, Datebtn and Datebtntapped indicating the same button. I'm using xcode10 and macOS Catalina.
How to fix this problem?
Replace
#IBAction func DatebtnTapped(cell: DateCollectionViewCell) {
with
#objc func DatebtnTapped(_ btn : UIButton) {
let clickedItem = arr[btn.tag]
the parameter should be of type UIButton use it's tag to access the data source of the table
and
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Calendar", for: indexPath) as! DateCollectionViewCell
cell.Datebtn.tag = indexPath.row
cell.Datebtn.addTarget(self,#selector(DatebtnTapped),for:.touchUpInside)

UICollectionView not loading Image from SDWebImage

I created a UICollectionView Inside a ViewController but the UICollectionView is not loading the data I appended to the array. I tried to print from the UIcollectionView datasource and delegate but it also not printing
import UIKit
import Alamofire
import SwiftyJSON
import SDWebImage
class SelectCarViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var selectedcarManufacturalLebel: UILabel!
#IBOutlet weak var selectedcarPlateNumber: UILabel!
#IBOutlet weak var selectCarCarouselCollectionView: UICollectionView!
var selecrCarsURL = "https://my.api.mockaroo.com/cars.json?key=86f86980"
var selectCarArray : [SelectCarDataModel] = [SelectCarDataModel]()
let selectCarFromJSON = SelectCarDataModel()
override func viewDidLoad() {
super.viewDidLoad()
selectCarCarouselCollectionView.delegate = self
selectCarCarouselCollectionView.dataSource = self
//register custom sell nib file
selectCarCarouselCollectionView.register(UINib.init(nibName: "SelectCarViewCell", bundle: nil), forCellWithReuseIdentifier: "selectIdentifier")
//Add flowyout function
addFlowlayOut()
getselectCarData(url: selecrCarsURL)
selectCarCarouselCollectionView.reloadData()
}
//MARK:- Add carousel flowlayout for viewcontroller collectionview
func addFlowlayOut() {
let flowlayout = UPCarouselFlowLayout()
flowlayout.itemSize = CGSize(width: UIScreen.main.bounds.size.width - 60, height: selectCarCarouselCollectionView.frame.size.height)
flowlayout.scrollDirection = .horizontal
flowlayout.sideItemScale = 0.8
flowlayout.sideItemAlpha = 1.0
flowlayout.spacingMode = .fixed(spacing: 5.0)
selectCarCarouselCollectionView.collectionViewLayout = flowlayout
}
//MARK:- Select Car Newtworking
func getselectCarData(url: String) {
Alamofire.request(url, method: .get).responseJSON {
response in
if response.result.isSuccess {
print("Sucess Got the Selected Cars Data")
let selelectCarJSON : JSON = JSON(response.result.value!)
print(selelectCarJSON)
self.updateSelectedCar(json: selelectCarJSON)
}else {
print("error)")
}
}
}
//MARK:- Select Car Update JSON Parsing
func updateSelectedCar(json : JSON) {
//for i in 0...json.count-1 {
selectCarFromJSON.image = json["car"][0]["img"].stringValue
selectCarFromJSON.carType = json["car"][0] ["manufacturer"].stringValue
selectCarFromJSON.carModel = json["car"][0]["model"].stringValue
print("this is it \(selectCarFromJSON.image)")
//}
}
//MARK: -- UICollection Delegate and Datasourse Manipulation
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return selectCarArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = selectCarCarouselCollectionView.dequeueReusableCell(withReuseIdentifier: "selectIdentifier", for: indexPath) as! SelectCarViewCell
let selectCar = SelectCarDataModel()
selectCar.image = selectCarFromJSON.image
selectCar.carType = selectCarFromJSON.carType
selectCar.carModel = selectCarFromJSON.carModel
selectCarArray.append(selectCar)
print(selectCarArray)
cell.selectCarImage.sd_setImage(with: URL(string: selectCarArray[indexPath.row].image))
selectedcarManufacturalLebel.text = selectCarArray[indexPath.row].carType
selectedcarPlateNumber.text = selectCarArray[indexPath.row].carModel
print("this is cell\(selectCarArray[indexPath.row].image)")
selectCarCarouselCollectionView.reloadData()
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
}
I expected the SDWebImage to get the URL from the JSON and populate the UICollectionView
SDWebImage show URL image in imageView not a local image.
your selectCarArray store an image not url. so it's not working.
try this:-
cell.selectCarImage.sd_setImage(with: URL(string: "https://image.shutterstock.com/image-photo/beautiful-abstract-grunge-decorative-navy-260nw-539880832.jpg"))

Reach CollectionViewCell into Xib with Programatically

I created a Xib File and call in ViewController.Also I create a CollectionView into Xib File.And now I want to reach CollectionViewCell for showing cells.
class ProductVC: UIViewController {
var collection:UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let productView : ProductDetailView = UIView.fromNib()
productView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(productView)
collection = productView.collectionView
collection.register(UINib(nibName: "TagCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "TagCollectionViewCell")
productView.topAnchor.constraint(equalTo: view.safeTopAnchor).isActive = true
productView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
productView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height).isActive = true
}
}
class ProductDetailView: UIView {
#IBOutlet var productTitle: UILabel!
#IBOutlet var dateLabel: UILabel!
#IBOutlet var productImage: UIImageView!
#IBOutlet var productDescriptionLabel: UILabel!
#IBOutlet var collectionView: UICollectionView!
}
class TagCollectionViewCell: UICollectionViewCell {
#IBOutlet var tagName: UILabel!
}
Also I added some code like below . But has no sense!. Where is my mistake?
extension ProductVC : UICollectionViewDelegate , UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell: TagCollectionViewCell! = collectionView.dequeueReusableCell(withReuseIdentifier: "TagCollectionViewCell", for: indexPath) as? TagCollectionViewCell
if cell == nil {
collectionView.register(UINib(nibName: "TagCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "TagCollectionViewCell")
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TagCollectionViewCell", for: indexPath) as? TagCollectionViewCell
}
cell.tagName.text = "aa"
return cell
}
}
You did not conform to delegate and dataSource protocols. I think this the problem. Put below lines after collection.register
collection.delegate = self
collection.dataSource = self
Hope this will work.

How to call collectionView.reloadData() from reusable header button?

I have a button in my reusable header view that deletes information in the array that supplies the data for the collection view and header. I want it to also be able to perform collectionView.reloadData().
The issue is that I can't call collectionView.reloadData() from the header button because it doesn't recognize collectionView as a variable. If I call Builds().collectionView.reloadData() (Builds is the View controller) the app crashes because it says that it found nil while unwrapping optional. I know that simply calling collectionView.reloadData() isn't the problem because i have collectionView.reloadData() called in viewDidAppear() and that gives me no crashes.
Whats going on here? How can I get my collectionView to reload data after the button removes the data?
For reference:
Reusable Header:
import UIKit
class BuildsHeader: UICollectionReusableView {
#IBOutlet weak var headerLabel: UILabel!
#IBOutlet weak var deleteBuildButton: UIButton!
#IBAction func deleteBuildButton(sender: UIButton) {
for hero in heroes{ if hero.hero.name == heroForDetails.hero.name {
hero.builds.removeAtIndex(sender.tag)
heroForDetails = hero
}}
saveToDefaults(userProfile)
//Builds().collectionBuild.reloadData() runs just fine without this line
}
}
ViewController:
import UIKit
class Builds: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionBuild: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionBuild.delegate = self
collectionBuild.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
collectionBuild.reloadData()
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("BuildCell", forIndexPath: indexPath) as? TalentsCell {
let build = heroForDetails.builds[indexPath.section]
switch (indexPath.row) {
case 0:
cell.configureCell(build["1"]! as! Talent)
case 1:
cell.configureCell(build["4"]! as! Talent)
case 2:
cell.configureCell(build["7"]! as! Talent)
case 3:
cell.configureCell(build["10"]! as! Talent)
case 4:
cell.configureCell(build["13"]! as! Talent)
case 5:
cell.configureCell(build["16"]! as! Talent)
case 6:
cell.configureCell(build["20"]! as! Talent)
default:
cell.configureCell(build["1"]! as! Talent)
}
return cell
}
return UICollectionViewCell()
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return heroForDetails.builds.count
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(50,50)
}
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "BuildsHeader", forIndexPath: indexPath) as! BuildsHeader
var buildDict = heroForDetails.builds[indexPath.section]
header.headerLabel.text = buildDict["name"] as! String
header.deleteBuildButton.tag = indexPath.section
return header
}
}
You can achieve that with delegation.
Create a protocol
Create a variable inside BuildsHeader for delegate
Call the delegate method in deleteBuildButton function
Now the code for BuildsHeader should look like this:
import UIKit
//**** The Protocol ****
protocol BuildsHeaderDelegate {
func updateCollectionView()
}
class BuildsHeader: UICollectionReusableView {
//**** Variable for the delegate ****
var delegate: BuildsHeaderDelegate?
#IBOutlet weak var headerLabel: UILabel!
#IBOutlet weak var deleteBuildButton: UIButton!
#IBAction func deleteBuildButton(sender: UIButton) {
for hero in heroes{ if hero.hero.name == heroForDetails.hero.name {
hero.builds.removeAtIndex(sender.tag)
heroForDetails = hero
}}
saveToDefaults(userProfile)
//**** Call the delegate method ****
self.delegate?.updateCollectionView()
}
}
In the viewForSupplementaryElementOfKind method of your collection view configure the delegate property of the header, like this:
let header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "BuildsHeader", forIndexPath: indexPath) as! BuildsHeader
//**** Set this view controller to be the header's delegate ****
header.delegate = self
// rest of header setup
Make the Builds ViewController confirm to BuildsHeaderDelegate :
class Builds: UIViewController, BuildsHeaderDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
Implement BuildsHeaderDelegate's delegate method in the Builds view controller. you can put this right after your viewForSupplementaryElementOfKind method:
func updateCollectionView () {
//**** reload the collectionView ****
self.collectionBuild.reloadData()
}
Use Delegate to call the method:
protocol BuildsHeaderDelegate(){
func reloadCollectionView()
}
class BuildsHeader: UICollectionReusableView {
var delegate:BuildsHeaderDelegate!
#IBOutlet weak var headerLabel: UILabel!
#IBOutlet weak var deleteBuildButton: UIButton!
#IBAction func deleteBuildButton(sender: UIButton) {
for hero in heroes{ if hero.hero.name == heroForDetails.hero.name {
hero.builds.removeAtIndex(sender.tag)
heroForDetails = hero
}}
saveToDefaults(userProfile)
//##Call delegate method##
delegate.reloadCollectionView()
}
}
Conform delegate in View Controller and Assign it:
class Builds: UIViewController,delegate:BuildsHeaderDelegate , UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionBuild: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionBuild.delegate = self
collectionBuild.dataSource = self
}
func reloadCollectionView() {
collectionBuild.reloadData()
}
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "BuildsHeader", forIndexPath: indexPath) as! BuildsHeader
//##Assign Delegate##
header.delegate = self
var buildDict = heroForDetails.builds[indexPath.section]
header.headerLabel.text = buildDict["name"] as! String
header.deleteBuildButton.tag = indexPath.section
return header
}
}

Resources