I ask for help with determining the right approach
when I click on the picture I need to pass text and her to new VC, but the problem is that the project has a great depth struct
the cell lies in the collectionView, which is in the tableViewCell
How do I properly implement the transfer?
(the number of cells is generated automatically)
Another complication is that the code of the cell with which I work lies in another cell.
those. the transfer of information must also be written in it.
import UIKit
import moa
class RealPhotoInfoTableViewCell: UITableViewCell {
#IBOutlet private weak var MyCollection: UICollectionView!
var model: RealPhoto!
func setCollectionViewDataSourceDelegate(dataSourceDelegate: UICollectionViewDataSource & UICollectionViewDelegate, forRow row: Int) {
MyCollection.delegate = self
MyCollection.dataSource = self
MyCollection.reloadData()
}
}
extension RealPhotoInfoTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(model?.data.count as Any)
return model?.data.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RPhotoCell", for: indexPath) as! RealPhotoCollectionViewCell
cell.realPhotoImage.moa.url = model?.data[indexPath.row].photo
return cell
}
}
Add a var
class RealPhotoInfoTableViewCell: UITableViewCell {
weak var delegate:VCName?
Inside cellForRowAt of the table do
cell.delegate = self
Now you can do this inside didSelectItemAt of the collection
delegate?.sendData(image)
You have two options:
One is Sh_Khan answer using delegation.
Or you can use Notification observers.
This question has been asked before...
You can use delegate protocol in that case. You can implement that protocol. Please find following sample code for that.
protocol ImageSelectionDelegate {
func imageClicked(imageURL: String)
}
//Class A
class A : ImageSelectionDelegate{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//your existing code
cell.delegate = self
}
//Implement that protocol
func imageClicked(imageURL: String){
//You will get url for tapped image
}
}
class RealPhotoInfoTableViewCell{
weak var delegate : ImageSelectionDelegate? = nil
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate.imageClicked(model?.data[indexPath.item].photo)
}
}
Related
I have two View controllers(VC-1, VC-2).
View controller 1 is having a table view and further that table view is consists of 4 collection views. I want to navigate to new VC(VC-2). But unable to get "self.storyboard.instantiateViewController" in didSelect method of collection View.
So now how I can navigate to VC-2
You should use delegate. you can create a protocol and define a method for routing and implement it into VC1
You can use the below sample code.
This code is using closure syntax
class FirstVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell_Identifier") as! TableCell
cell.configureCell(data: ["Your", "Data"])
cell.didSelectRow = { data in
// Goto second view controller
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC")
self.navigationController?.pushViewController(vc!, animated: true)
}
return cell
}
}
class TableCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var colView: UICollectionView!
var didSelectRow: ((_ data: String) -> Void)? = nil // Closure
var arrData = [String]()
func configureCell(data: [String]) {
// Setup CollectionView data
self.arrData = data
self.colView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.arrData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL_IDENTIFIER", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let data = self.arrData[indexPath.row]
didSelectRow?(data)
}
}
I am new to Swift and I am trying to understand how could a class get a value inside an extension.
I have two problems but they are similar to each other, but first I will explain my project. I am using 2 controllers (MainViewController and FactsViewController) that we could say first controller and second controller. The third file is a CollectionViewCell because I am using a collection view on my project.
So, for the first problem, I am trying to call prepareForSegue method to send a value to FactsViewController. I kinda have all the method done, but I don't know how could a I collect the indexPath from the extension to send the right information to my second screen. This is the method on my MainViewController:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let destinationVC = segue.destination as! FactViewController
// getting the value associated with a variable here on this line
destinationVC.receivedValue = // a possible variable with the right result
}
This is how my CollectionViewCell file looks like:
class FactsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var lbFactsText: UILabel!
#IBOutlet weak var lbCategories: UILabel!
func setup(with facts: FactsData?, index: Int) {
let result = facts!.result[index]
lbFactsText.text = result.value
print(result.value)
if let category = result.categories.first {
lbCategories.text = category!.uppercased()
lbCategories.sizeToFit()
} else {
lbCategories.text = "UNCATEGORIZED"
lbCategories.sizeToFit()
}
}
}
I don't if StackOverFlow will let I list all the code to be precise but here is the list of the extensions I am using:
extension MainViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
}
extension MainViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
}
}
extension MainViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}
}
extension MainViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
(Note that there are code inside those extension methods, it just listed empty just to show methods I am using).
How could I for example get a fact[result].value to transfer there on my prepareForSegue method? Also, as I mentioned two problems, how could I sum all heights of my lbFactsText and lbCategories to return on UICollectionViewDelegateFlowLayout. I am really confused, I heard that objc_setAssociatedObject or objc_getAssociatedObject could do this but I don't understand how to use it, it is really the correct way use this two methods? I got from this site: https://marcosantadev.com/stored-properties-swift-extensions/
Please let me know if you need me to list full details of the extensions.
Based on #Joakim Danielson, it worked. I had to write a property on my controller class to store the value inside the extension.
I know this has been asked many times, but I am still learning, I have tried all possible solutions here in S.O. and I haven't had luck a single time. So this is the issue:
I am using Xcode 11 beta 3 (hopefully that ISN'T the issue!)
I have a simple collection view in a view controller
Inside the cell I have put a tvOS' TVPosterView - which inherits from UIControl and testing it by itself it does behave like a button (IBAction gets called).
On the collection view there is the animation when I push the poster
the poster view has a default image and I just change the title
I dragged an IBAction from the poster view to the cell class in IB
IBAction apart, the collection view shows and scrolls just fine, changing the titles of the posters
If I delegate the collection view to the view controller, collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) does get called.
Here is the code:
View Controller
import UIKit
import TVUIKit
class SecondViewController: UIViewController {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var myCollection: MyCollection!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
extension SecondViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
cell.myPoster.title! = "Header " + String(indexPath.row + 1)
cell.myPoster.tag = indexPath.row + 1
cell.posterTapAction = { cell in
print("Header is: \(cell.myPoster.title!)")
}
return cell
}
}
extension SecondViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// This one works - but that's not what I am looking for
print("From didSelectItemAt indexPath: \(indexPath.item + 1)")
}
}
Collection View
import UIKit
class MyCollection: UICollectionView {
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
Cell
import UIKit
import TVUIKit
class MyCell: UICollectionViewCell {
var posterTapAction: ((MyCell) -> Void)?
#IBOutlet weak var myPoster: TVPosterView!
#IBAction func myAction(_ sender: TVPosterView) {
posterTapAction?(self)
print("Poster pressed \(sender.tag)")
}
}
Any idea about what I am missing? I would just be happy to manage to print a dummy string after pressing a poster.
I have also tried the solutions with selectors and delegates, none worked. So, please let's concentrate on this particular solution with closures. Thanks!
I have finally found it. As usual it's due to tvOS focus. Basically the cell has to be not focusable, so that the UIControl element inside the cell (TVPosterView in my case, but it could be any UIControl) will be the focused one. It's tricking because graphically it does appear as if the poster had focus (one can rotate the poster a bit around).
The solution is to add collectionView(_:canFocusItemAt:) to the UICollectionViewDelegate
extension SecondViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView,
canFocusItemAt indexPath: IndexPath) -> Bool {
return false
}
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
// Now, thanks to the function above, this is disabled
print("From didSelectItemAt indexPath: \(indexPath.item + 1)")
}
}
I have tested it in the code of this question and added it also to my project, which finally works!
I have UITableViewCell and I want to send the indexPath.row number to UIViewController using didSet, but xcode gives me an error when i use the value for other things (in UIViewController) says that the value is nil error: unexpectedly found null when unwrapping an optional value.
But if I print in the variable in UIViewController the value appears.
What i do? Thanks.
class TableViewCellCentral: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var CollectionData: UICollectionView!
var send = Int() {
didSet{
ViewController().reload = send
}
}
override func awakeFromNib() {
super.awakeFromNib()
CollectionData.dataSource = self
CollectionData.delegate = self
CollectionData.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
cell.LabelData.text! = "number \(indexPath.row)"
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
send = indexPath.row
CollectionData.reloadData()
}
}
ViewController() is just an instance of your ViewController. What you're doing when you say ViewController() is you're creating a new ViewController, rather than accessing the one you're looking for. If you want the indexPath.row, you need to send it to the actual VC, not just an instance of it.
You need to keep in mind that not all data is loaded instantly and at the same time, also that it does not always load in the order that you expect it to. It is possible that you are asking for the data before it is even assigned. Try
import UIKit
class TableViewCellCentral: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var CollectionData: UICollectionView!
var send = Int() {
didSet {
ViewController().reload = send
}
}
override func awakeFromNib() {
super.awakeFromNib()
CollectionData.dataSource = self
CollectionData.delegate = self
CollectionData.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
if indexPath.row != nil {
cell.LabelData.text! = "number \(self.indexPath.row)"
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = CollectionData.dequeueReusableCell(withReuseIdentifier: "CellData", for: indexPath) as! CollectionViewCellData
send = indexPath.row
CollectionData.reloadData()
}
}
I am only printing if the value is not nil! Let me know if you have more questions, or let us know if my solution is not working. =)
I made a simple app add uibutton to collectionviewcell but when i give action an error occurs. Here is a screenshoot.
Error 1
Error 2
and my code of class: UIbuton {}
import Foundation
import UIKit
class KeypadUIButton: UIButton {
var qIndex : NSIndexPath?
}
and my cell class is :
import UIKit
class ConverterCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var keypadButton: KeypadUIButton!
}
and error in :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = padCollectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! ConverterCollectionViewCell
let index = indexPath.row
// -> cell.keypadButton.qIndex = indexPath as NSIndexPath
return cell
}
Where is my mistake. can you help me ?
Oh! I've got your error, it seems you missed this:
Type in your class name: KeypadUIButton
Also you don't need to use NSIndexPath in swift. Just use IndexPath instead. So you won't need to down cast it.
I have try with your code its work fine , please check that you give action to your button as well as custom class name to your button
class KeypadUIButton: UIButton {
var qIndex : NSIndexPath?
}
class ConverterCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var keypadButton: KeypadUIButton!
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from
a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : UICollectionViewDataSource{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 16
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier : String = "test"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! ConverterCollectionViewCell
cell.keypadButton.qIndex = indexPath as NSIndexPath?
cell.keypadButton.setTitle("\(indexPath.row + 1)", for: .normal)
return cell
}
}
You had taken
qIndex as? NSIndexPath
you can take as
qIndex as? Indexpath
It avoid unnecessary downcasting in cellforitem.