Acess uiviewController element from another class - ios

there is this project im working on, but there is a problem with the element in the viewcontroller of my storyboard which i want to change its property from another class!
my first approach was instantiating an object from the viewcontroller in my second class! which returns nil at runtime!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// let mainstampvc = MainStampVc().storyboard?.instantiateViewController(withIdentifier: "mainstampvc") as? MainStampVc
// mainstampvc?.setstampimage(imageURL: list_images[indexPath.row])
let msvc = mainstampvc()
mainstampvc?.setstampimage(imageURL: list_images[indexPath.row])
}
my second approache was instantiate the whole viewcontroller again in my second class which does nothing.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let mainstampvc = MainStampVc().storyboard?.instantiateViewController(withIdentifier: "mainstampvc") as? MainStampVc
mainstampvc?.setstampimage(imageURL: list_images[indexPath.row])
}
the whole thing i wanted is when i click on my uicollectionviewcell change the background of one of my MainViewcontroller views. here is all my classes
viewcontroller.swift
import Foundation
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var stampholder: UIView!
#IBAction func TextViewButton(_ sender: Any) {
removerSubViews()
addSubView(ViewName: "text")
}
#IBAction func AViewButton(_ sender: Any) {
removerSubViews()
addSubView(ViewName: "mohr")
}
#IBAction func BorderViewButton(_ sender: Any) {
}
#IBAction func DlViewButton(_ sender: Any) {
}
#IBOutlet weak var holderView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addSubView(ViewName: "mohr")
let mainstampvc = self.storyboard?.instantiateViewController(withIdentifier: "mainstampvc")
let mainstampview = mainstampvc?.view
mainstampview?.frame = stampholder.frame
stampholder.addSubview((mainstampview)!)
}
func removerSubViews(){
for view in self.holderView.subviews{
view.removeFromSuperview()
}
}
func addSubView(ViewName: String)
{
if let subview = Bundle.main.loadNibNamed(ViewName, owner: self, options: nil)?.first as? UIView {
self.holderView.addSubview(subview);
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
mohrcollectionview.swift
import Foundation
import UIKit
class MohrCollectionViewController: UIView,UICollectionViewDataSource,UICollectionViewDelegate{
var mohrPath: String = ""
var fileManager: FileManager!
var list_images : [String] = []
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fileManager = FileManager.default
let currentDir = Bundle.main.resourcePath
mohrPath = currentDir!
let mohrsPath = try? fileManager.contentsOfDirectory(atPath: mohrPath + "/mohr")
list_images = mohrsPath!
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return list_images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.register(UINib(nibName: "mohrcell", bundle: nil), forCellWithReuseIdentifier: "mohrcell")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mohrcell", for: indexPath) as! mohrCellController
let image = UIImage(named: list_images[indexPath.row])
cell.cellimage.image = image
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let mainstampvc = MainStampVc().storyboard?.instantiateViewController(withIdentifier: "mainstampvc") as? MainStampVc
mainstampvc?.setstampimage(imageURL: list_images[indexPath.row])
}
}
mainstampvc.swift
import Foundation
import UIKit
class MainStampVc: UIViewController{
#IBOutlet weak var stampimage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
setstampimage(imageURL: "golbanafsh.png")
}
public func setstampimage(imageURL: String)
{
stampimage.image = UIImage(named: imageURL)
}
}
any help would be appreciated
2
so here is my code with delegation but still nothing :(
//
// MohrCollectionViewController.swift
// Mohrem
//
// Created by shayan rahimian on 12/18/17.
// Copyright © 2017 shayan rahimian. All rights reserved.
//
import Foundation
import UIKit
class MohrCollectionViewController: UIView,UICollectionViewDataSource,UICollectionViewDelegate,UpdateBackgroundDelegate{
var updatedelegate:UpdateBackgroundDelegate? = nil
func updateBackground(imageURL: String) {
print("mohr update back ground e balaE")
if updatedelegate == nil {
print("no delegate")
}else{
updatedelegate?.updateBackground(imageURL: imageURL)
}
}
var mohrPath: String = ""
var fileManager: FileManager!
var list_images : [String] = []
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fileManager = FileManager.default
let currentDir = Bundle.main.resourcePath
mohrPath = currentDir!
let mohrsPath = try? fileManager.contentsOfDirectory(atPath: mohrPath + "/mohr")
list_images = mohrsPath!
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return list_images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.register(UINib(nibName: "mohrcell", bundle: nil), forCellWithReuseIdentifier: "mohrcell")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mohrcell", for: indexPath) as! mohrCellController
let image = UIImage(named: list_images[indexPath.row])
cell.cellimage.image = image
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.updateBackground(imageURL: list_images[indexPath.row])
}
}
//
// MainStampvc.swift
// Mohrem
//
// Created by shayan rahimian on 12/19/17.
// Copyright © 2017 shayan rahimian. All rights reserved.
//
import Foundation
import UIKit
protocol UpdateBackgroundDelegate : class {
func updateBackground(imageURL: String)
}
class MainStampVc: UIViewController{
#IBOutlet weak var stampimage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
updateBackground(imageURL: "biggolabi.png")
}
func updateBackground(imageURL: String) {
// update your background in this funcion
print("extension")
print(imageURL)
stampimage.image = UIImage(named: imageURL)
}
}
am i doing anything wrong?

You could pass a UIViewController reference to the MohrCollectionViewController(you should call this MohrCollectionView to avoid confusion) at the time you construct it. Then whenever you need to update the background you call the relevant function on the reference.
class ViewController : UIViewController {
...
override func viewDidLoad() {
...
let view = addSubView(ViewName: "mohr")
view?.vc = self
}
func addSubView(ViewName: String) -> UIView?
{
if let subview = Bundle.main.loadNibNamed(ViewName, owner: self, options: nil)?.first as? UIView {
self.holderView.addSubview(subview);
return subview
}
}
return nil
}
class MohrCollectionView {
func updateVcBackground() {
vc?.updateBackground()
}
var vc : ViewController? = nil
}
A cleaner way to do this is use a delegate. A delegate uses a protocol to define an interface between two classes.
protocol UpdateBackgroundDelegate : class {
func updateBackground()
}
class ViewController : UIViewController, UpdateBackgroundDelegate {
...
override func viewDidLoad() {
...
let view = addSubView(ViewName: "mohr")
view?.updateBackgroundDelegate = self
}
func updateBackground() {
// update your background in this funcion
}
}
class MohrCollectionView {
func updateVcBackground() {
updateBackgroundDelegate?.updateBackground()
}
var updateBackgroundDelegate : UpdateBackgroundDelegate? = nil
}

For making the delegate work do the following:
Declare the delegate first like in Collection View Class
protocol UpdateBackgroundDelegate : class {
func updateBackground(imageURL: String)
}
Create a variable like
var updateDelegate: UpdateBackgroundDelegate?
and paste it below your collectionView class from where you want to trigger changing background colour
In the collection view selection delegate, add this line of code
updateDelegate.updateBackground(imageUrl: yourUrl)
In the View, where colour change has to take place, create your collectionView instance and add this line of code
collectionView.updateDelegate = self
At last add this extension
class ViewController :UpdateBackgroundDelegate {
func updateBackground(imageUrl: yourUrl) {
//write code to load image from url
}
}

Related

How do you present images in a collection view in a table view?

I have a UICollectionView placed inside a UITableViewCell. The collection view has its scroll direction set to horizontal. I have set the collection view and the collection view cell in the table view right, but when I run it the images don't show up and as I see that data doesn't pass right from table view cell to collection view cell. I cannot find anything wrong. I hope someone can find the mistake with a clearer mind.
Imagine that I want the first cell to have a collection view of images horizontally and in the other cells rows of names for example.
You can see my project on this GitHub account: https://github.com/BenSeferidis/Nft-Assets/tree/v5 for better understanding.
Lobby View Controller (Main VC):
The AssetTableViewCell is another custom cell that generates the rest of the data from my models: NFT API
import UIKit
class LobbyViewController: UIViewController {
// MARK: - IBProperties
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var data: [DataEnum] = []
var likes:[Int] = []
var numlikes: Int = 0
var nfts: [Nft] = []
let creators : [Creator] = []
var icons: [Icon] = []
var loadData = APICaller()
// MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "AssetTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "AssetTableViewCell")
let nib2 = UINib(nibName: "CreatorsTableViewCell", bundle: nil)
tableView.register(nib2, forCellReuseIdentifier: "CreatorsTableViewCell")
tableView.dataSource = self //method to generate cells,header and footer before they are displaying
tableView.delegate = self //method to provide information about these cells, header and footer ....
downloadJSON {
self.tableView.reloadData()
print("success")
}
loadData.downloadData { (result) in
print(result)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentViewController {
destination.nft = nfts[tableView.indexPathForSelectedRow!.row]
destination.delegate = self
}
}
// MARK: - Methods
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://public.arx.net/~chris2/nfts.json")
URLSession.shared.dataTask(with: url!) { [self] data, response, error in
if error == nil {
do {
self.nfts = try JSONDecoder().decode([Nft].self, from: data!)
let creators = nfts.map { nft in
nft.creator
}
self.data.append(.type1(creators: creators))
self.nfts.forEach { nft in
self.data.append(.type2(nft: nft))
}
DispatchQueue.main.async {
completed()
}
}
catch {
print("error fetching data from api")
}
}
}.resume()
}
}
// MARK: - Extensions
extension LobbyViewController : UITableViewDelegate , UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
indexPath.row == 0 ? 200 : UITableView.automaticDimension
}
//gemizo ta rows tou table
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.data[indexPath.item] {
case .type1(let creators):
print("--->", creators)
let cell = tableView.dequeueReusableCell(withIdentifier: "CreatorsTableViewCell",
for: indexPath) as! CreatorsTableViewCell
cell.updateCreators(creators)
return cell
case .type2(let nft):
let cell = tableView.dequeueReusableCell(withIdentifier: "AssetTableViewCell",
for: indexPath) as! AssetTableViewCell
cell.nameLabel?.text = nft.name
cell.nameLabel.layer.cornerRadius = cell.nameLabel.frame.height/2
cell.likesLabel?.text = "\((numlikes))"
let imgUrl = (nft.image_url)
print(imgUrl)
cell.iconView.downloaded(from: imgUrl)
cell.iconView.layer.cornerRadius = cell.iconView.frame.height/2
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
}
extension LobbyViewController : TestDelegate{
func sendBackTheLikess(int: Int) {
numlikes = int
tableView.reloadData()
}
}
// MARK: - Enums
enum DataEnum {
case type1(creators: [Creator])
case type2(nft: Nft)
}
// MARK: - Struct
struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
Creators TableView Cell :
import UIKit
class CreatorsTableViewCell: UITableViewCell {
//MARK: - IBProtperties
#IBOutlet var creatorsCollectionView: UICollectionView!
//MARK: - Properties
var nft : Nft?
var creators : [Creator] = []
weak var delegate : CreatorsTableViewCellDelegate?
//MARK: - Life Cyrcle
override func awakeFromNib() {
super.awakeFromNib()
creatorsCollectionView.dataSource = self
creatorsCollectionView.delegate = self
let nibName = UINib(nibName: "CollectionViewCell", bundle: nil)
creatorsCollectionView.register(nibName, forCellWithReuseIdentifier: "CollectionViewCell")
}
func updateCreators( _ creators: [Creator]) {
self.creators = creators
}
required init?(coder aDecoder : NSCoder) {
super.init(coder: aDecoder)
}
}
//MARK: - Extensions
extension CreatorsTableViewCell : UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
CGSize(width: 30, height: 30)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return creators.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = creatorsCollectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell",
for: indexPath) as! CollectionViewCell
cell.renewCreators(creators)
cell.creatorName.text = creators[indexPath.row].user.username
cell.creatorName.layer.cornerRadius = cell.creatorName.frame.height/2
cell.creatorsImg.image = UIImage(named: creators[indexPath.row].profileImgURL )
cell.creatorsImg.layer.cornerRadius = cell.creatorsImg.frame.height/2
return cell
// cell.backgroundColor = .brown
// cell.creatorName?.text = creators[indexPath.row].user.username
// let imgUrl = (creators[indexPath.row].profileImgURL)
// print(imgUrl)
// cell.creatorsImg.downloaded(from: imgUrl)
// return cell
}
}
//MARK: - Protocols
protocol CreatorsTableViewCellDelegate: AnyObject {
func didSelectPhoto(index: Int)
}
CollectionViewCell:
import UIKit
class CollectionViewCell: UICollectionViewCell {
//MARK: - IBProperties
#IBOutlet var creatorsImg: UIImageView!{
didSet {
creatorsImg.contentMode = .scaleAspectFit
}
}
#IBOutlet var creatorName: UILabel!
//MARK: - Properties
var nft : Nft?
var creators : [Creator] = []
//MARK: - Life Cyrcle
override func awakeFromNib() {
super.awakeFromNib()
print(creators)
creatorName.backgroundColor = .systemCyan
creatorsImg.layoutIfNeeded()
creatorsImg.layer.cornerRadius = creatorsImg.frame.height / 2
}
func setUpCollectionViewCell(_ nft: Nft) {
}
func renewCreators( _ creators: [Creator]) {
self.creators = creators
}
}
//MARK: - Protocols
protocol CollectionViewCellDelegate: AnyObject {
func didSelectPhoto(index: Int)
}
Models:
import Foundation
// MARK: - Nft
struct Nft: Codable{
let id:Int
let image_url:String
let name:String
let creator: Creator
}
// MARK: - Icon
struct Icon:Codable{
let image_url:String
}
// MARK: - Creator
struct Creator: Codable {
let user: User
let profileImgURL: String
enum CodingKeys: String, CodingKey {
case user
case profileImgURL = "profile_img_url"
}
}
// MARK: - User
struct User: Codable {
let username: String?
}
APICaller :
import Foundation
final class APICaller {
static let shared = APICaller()
public struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
public func downloadData(completion:#escaping (Result<[Nft], Error>) -> Void )
{
guard let url = URL(string:Constants.url)else{
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
//print(response)
print("here")
guard let data = data , error == nil else{
print("something went wrong")
return
}
print("here4")
//mexri edo exoume parei ta data kai tora me to do-catch tha ta kanoume convert se object
do{
//Decode the response
let nfts = try JSONDecoder().decode([Nft].self, from: data)
completion(.success(nfts))
print(nfts)
}catch{
completion(.failure(error))
}
}
task.resume()
}
}

how in the numberOfItemsInSection method to return the number equal to the value that I set in the slider from another view controller in Swift?

I am still learning swift, and I am trying to create a UICollectionView that would return the number of items that I set in the initial view controller using the slider, but my code doesn't work, how would I do this? Here is my code below:
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet weak var firstLabel: UILabel! {
didSet {
firstLabel.text = "0"
}
}
#IBOutlet weak var firstSlider: UISlider! {
didSet {
firstSlider.value = 0
firstSlider.minimumValue = 0
firstSlider.maximumValue = 500
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func firstSliderAction(_ sender: UISlider) {
let firstSliderAction = Int(round(sender.value))
firstLabel.text = "\(firstSliderAction)"
}
}
// CollectionViewController
private let reuseIdentifier = "cell"
class CollectionViewController: UICollectionViewController {
var vc: ViewController!
override func viewDidLoad() {
super.viewDidLoad()
vc = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewController") as? ViewController
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(vc.firstSlider.value)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionCell
cell.backgroundColor = .green
return cell
}
}
First let me tell you why your code is not working as expected.
vc = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewController") as? ViewController
Every time when we initiate view controller from corresponding storyboard it will create new object of that view controller, so based on that vc object will not have those value which has been set by earlier user interaction, in-short for that object didSet haven't called yet.
Now how you can achieve the above,
Well first we need to observe how you are navigating from ViewController to CollectionViewController based on code it looks like you are using segue so you can do below stuff.
class ViewController: UIViewController {
#IBOutlet weak var firstLabel: UILabel!
#IBOutlet weak var firstSlider: UISlider!
var sliderValue:Int = 0 {
didSet {
firstLabel.text = "\(sliderValue)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
setUpInitialValues()
}
func setUpInitialValues() {
firstSlider.value = 0
firstSlider.minimumValue = 0
firstSlider.maximumValue = 500
}
// Value changed action
#IBAction func firstSliderAction(_ sender: UISlider) {
self.sliderValue = Int(round(sender.value))
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "navigateCollection" {
if let destinationVC = segue.destination as? CollectionViewController {
destinationVC.sliderValue = self.sliderValue
}
}
}
}
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
var sliderValue:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView.reloadData()
// Do any additional setup after loading the view.
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return sliderValue
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
cell.backgroundColor = .yellow
return cell
}
}

How can I execute the collectionView methods of a class from another one?

I have my class CardSensors which is has a collectionView which is filled with another XIB
class CardSensors: UIView {
#IBOutlet weak var botName: UILabel!
#IBOutlet weak var sensorsCollectionView: UICollectionView!
var sensors = [[String: Any]]()
var viewModel: NewsFeedViewModel! {
didSet {
setUpView()
}
}
func setSensors(sensors: [[String: Any]]){
self.sensors = sensors
}
static func loadFromNib() -> CardSensors {
return Bundle.main.loadNibNamed("CardSensor", owner: nil, options: nil)?.first as! CardSensors
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func setupCollectionView(){
let nibName = UINib(nibName: "SensorCollectionViewCell", bundle: Bundle.main)
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
}
func setUpView() {
botName.text = viewModel.botName
}
}
extension CardSensors: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SensorCollectionViewCell", for: indexPath) as? SensorCell else {
return UICollectionViewCell()
}
cell.dateLabel.text = sensors[indexPath.row]["created_at"] as? String
cell.sensorType.text = sensors[indexPath.row]["type"] as? String
cell.sensorValue.text = sensors[indexPath.row]["value"] as? String
cell.sensorImage.image = UIImage(named: (sensors[indexPath.row]["type"] as? String)!)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sensors.count
}
}
Im creating an object in another class like this but I want this to call the methods of the collectionView for it to load the info.
let sensorView = CardSensors.loadFromNib()
sensorView.sensors = sensores
sensorView.setupCollectionView()
The problem is that the collectionView methods are never being called. What can I do to call them from my other class?
You need to set the data souce
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
sensorsCollectionView.dataSource = self
sensorsCollectionView.reloadData()
Then inside your vc , make it an instance variable
var sensorView:CardSensors!
sensorView = CardSensors.loadFromNib()
sensorView.sensors = sensores
sensorView.setupCollectionView()

Perform Segue from UICollectionViewCell

So I'm creating a blog app, and on the home news feed collection view (imageCollection, loaded from firebase database) I have a button. This button title depends on the Category of the image. What i'm having an issue with is performing the segue in the UICollectionViewCell class. I ran the button action with the print statement, and it worked. But when i try to add performSegue, well it doesn't let me. (Use of unresolved identifier 'performSegue')
Any tips? thank you!
P.S. i'm still fairly new to swift, so if i come off a little ignorant, i apologize
My ViewController
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var popImageCollection: UICollectionView!
#IBOutlet weak var imageCollection: UICollectionView!
var customImageFlowLayout = CustomImageFlowLayout()
var popImageFlowLayout = PopImagesFlowLayout()
var images = [BlogInsta]()
var popImageArray = [UIImage]()
var homePageTextArray = [NewsTextModel]()
var dbRef: DatabaseReference!
var dbPopularRef: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
dbRef = Database.database().reference().child("images")
dbPopularRef = Database.database().reference().child("popular")
loadDB()
loadImages()
loadText()
customImageFlowLayout = CustomImageFlowLayout()
popImageFlowLayout = PopImagesFlowLayout()
imageCollection.backgroundColor = .white
popImageCollection.backgroundColor = .white
// Do any additional setup after loading the view, typically from a nib.
}
func loadText() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.homePageTextArray.removeAll()
for homeText in snapshot.children.allObjects as! [DataSnapshot] {
let homeTextObject = homeText.value as? [String: AnyObject]
let titleHome = homeTextObject?["title"]
let categoryButtonText = homeTextObject?["category"]
self.imageCollection.reloadData()
let homeLabels = NewsTextModel(title: titleHome as! String?, buttonText: categoryButtonText as! String?)
self.homePageTextArray.append(homeLabels)
}
}
})
}
func loadImages() {
popImageArray.append(UIImage(named: "2")!)
popImageArray.append(UIImage(named: "3")!)
popImageArray.append(UIImage(named: "4")!)
self.popImageCollection.reloadData()
}
func loadDB() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
var newImages = [BlogInsta]()
for BlogInstaSnapshot in snapshot.children {
let blogInstaObject = BlogInsta(snapshot: BlogInstaSnapshot as! DataSnapshot)
newImages.append(blogInstaObject)
}
self.images = newImages
self.imageCollection.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.imageCollection {
return images.count
} else {
return popImageArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
let image = images[indexPath.row]
let text: NewsTextModel
text = homePageTextArray[indexPath.row]
cell.categoryButton.setTitle(text.buttonText, for: .normal)
cell.newTitleLabel.text = text.title
cell.imageView.sd_setImage(with: URL(string: image.url), placeholderImage: UIImage(named: "1"))
return cell
} else {
let cellB = popImageCollection.dequeueReusableCell(withReuseIdentifier: "popCell", for: indexPath) as! PopularCollectionViewCell
let popPhotos = popImageArray[indexPath.row]
cellB.popularImageView.image = popPhotos
cellB.popularImageView.frame.size.width = view.frame.size.width
return cellB
}
}
}
My ImageCollectionViewCell.swift
import UIKit
import Foundation
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var categoryButton: UIButton!
#IBOutlet weak var newTitleLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.imageView.image = nil
}
}
You need a custom delegate. Do this:
protocol MyCellDelegate {
func cellWasPressed()
}
// Your cell
class ImageCollectionViewCell: UICollectionViewCell {
var delegate: MyCellDelegate?
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
self.delegate?.cellWasPressed()
}
}
}
// Your viewcontroller must conform to the delegate
class ViewController: MyCellDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
// set the delegate
cell.delegate = self
// ...... rest of your cellForRowAtIndexPath
}
// Still in your VC, implement the delegate method
func cellWasPressed() {
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
You should use your own delegate. It is already described here
performSegue(withIdentifier:sender:) won't work from cell because it is UIViewController metod.
also you can make use of closure

Using core data, I got an error 'nib but didn’t get a UICollectionView.’

When I use core data to save some images which are downloaded from internet in collectionView cell, I got an error ‘NSInternalInconsistencyException’, reason: ‘-[UICollectionViewController loadView] loaded the “BYZ-38-t0r-view-8bC-Xf-vdC” nib but didn’t get a UICollectionView.’
There is my code of albumViewController:
class albumViewController: coreDataCollectionViewController, MKMapViewDelegate {
// Properties
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var albumCollection: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.albumCollection.dataSource = self
self.albumCollection.delegate = self
// Show the pin
let spanLevel: CLLocationDistance = 2000
self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(pinLocation.pinCoordinate, spanLevel, spanLevel), animated: true)
self.mapView.addAnnotation(pinLocation.pinAnnotation)
// Set the title
title = "Photo Album"
//Get the stack
let delegate = UIApplication.shared.delegate as! AppDelegate
let stack = delegate.stack
// Create a fetchrequest
let fr = NSFetchRequest<NSFetchRequestResult>(entityName: "Album")
fr.sortDescriptors = [NSSortDescriptor(key: "imageData", ascending: false),NSSortDescriptor(key: "creationDate", ascending: false)]
// Create the FetchedResultsController
fetchedResultsController = NSFetchedResultsController(fetchRequest: fr, managedObjectContext: stack.context, sectionNameKeyPath: nil, cacheName: nil)
// Put photos in core data
let photoURLs = Constants.photosUrl
for photoUrLString in photoURLs {
let photoURL = URL(string: photoUrLString)
if let photoData = try? Data(contentsOf: photoURL!) {
let photo = Album(imageData: photoData, context: fetchedResultsController!.managedObjectContext)
} else {
print("Image does not exist at \(photoURL)")
}
}
}
// Find number of items
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCollectionViewCell", for: indexPath) as! photoCollectionViewCell
let album = fetchedResultsController!.object(at: indexPath) as! Album
performUIUpdatesOnMain {
cell.photoImageView?.image = UIImage(data: album.imageData as! Data)
}
return cell
}
// Reload photos album
#IBAction func loadNewPhotos(_ sender: AnyObject) {
}
}
There is my code of
class coreDataCollectionViewController: UICollectionViewController {
// Mark: Properties
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>? {
didSet {
// Whenever the frc changes, we execute the search and
// reload the table
fetchedResultsController?.delegate = self
executeSearch()
collectionView?.reloadData()
}
}
// Mark: Initializers
init(fetchedResultsController fc: NSFetchedResultsController<NSFetchRequestResult>, collectionViewLayout: UICollectionViewFlowLayout) {
fetchedResultsController = fc
super.init(collectionViewLayout: collectionViewLayout)
}
// Do not worry about this initializer. I has to be implemented because of the way Swift interfaces with an Objective C protocol called NSArchiving. It's not relevant.
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
// Mark: CoreDataTableViewController (Subclass Must Implement)
extension coreDataCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
fatalError("This method MUST be implemented by a subclass of CoreDataTableViewController")
}
}
// Mark: CoreDataTableViewController (Table Data Source)
extension coreDataCollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
if let fc = fetchedResultsController {
return (fc.sections?.count)!
} else {
return 0
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let fc = fetchedResultsController {
return fc.sections![section].numberOfObjects
} else {
return 0
}
}
}
// Mark: CoreDataTableViewController (Fetches)
extension coreDataCollectionViewController {
func executeSearch() {
if let fc = fetchedResultsController {
do {
try fc.performFetch()
} catch let error as NSError {
print("Error while trying to perform a search: \n\(error)\n\(fetchedResultsController)")
}
}
}
}
// MARK: - CoreDataTableViewController: NSFetchedResultsControllerDelegate
extension coreDataCollectionViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
}
}
But if I don't use core data, I succeed. Is there any difference?
class albumViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate {
// Properties
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var albumCollection: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let spanLevel: CLLocationDistance = 2000
self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(location.pinCoordinate, spanLevel, spanLevel), animated: true)
self.mapView.addAnnotation(location.pinAnnotation)
}
// Find number of items
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Constants.photosUrl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCollectionViewCell", for: indexPath) as! photoCollectionViewCell
let photoURL = URL(string: Constants.photosUrl[(indexPath as NSIndexPath).item])
if let photoData = try? Data(contentsOf: photoURL!) {
performUIUpdatesOnMain {
cell.photoImageView?.image = UIImage(data: photoData)
}
} else {
print("Image does not exist at \(photoURL)")
}
return cell
}
// Reload photos album
#IBAction func loadNewPhotos(_ sender: AnyObject) {
}
}
By the way, there is my code link
https://github.com/MartinSnow/MyVirtualTourist.git
and my project is on the "storeLocation" branch.
The key difference between those two implementations, apart from the use of CoreData, is that the "working" version is a subclass of UIViewController:
class albumViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate { .... }
whereas the version throwing the error is a subclass of UICollectionViewController:
class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UICollectionViewController { .... }
UICollectionViewControllers require that their root view is a UICollectionView, or a subclass thereof. In your storyboard, the albumVC has a collectionView, but is not the root view.
The solution is to change the class definition for coreDataCollectionViewController to subclass UIViewController and adopt the collection view delegate and datasource protocols:
class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { .... }
Note also that the error is actually thrown by your mapViewController scene, which is also a subclass of coreDataCollectionViewController and has no collectionView in the storyboard. The above fix should work for this too.

Resources