Two Swift custom cells

Asked 2 years ago, Updated 2 years ago, 301 views

I would like to add two custom cells from different VCs on Swift.

Tab-Navi-List (UIVC) and two other Post Fols, both of which are UIVCs

How should I write it on the list that displays custom cells?

 functableView(_tableView:UITableView, numberOfRowsInSection section:Int) - > Int {
        return mainArray.count
    }
    
    functableView(_tableView:UITableView, cellForRowAtindexPath:IndexPath) - >UITableViewCell{
        let postcell=tableView.dequeueReusableCell(withIdentifier: "postcell")!
        postcell.textLabel?.text=mainArray [indexPath.row]
        return postcell
    } 

If so, I think I can only register one

add

Original VC

class List:UIViewController, UITableViewDataSource, UITableViewDelegate{
    
    varmainArray: String = [ ]
    let initArray: String = [ ]
    
    override func viewDidLoad(){
        super.viewDidLoad()
        tableView.dataSource=self
        tableView.delegate=self
        
        // Do any additional setup after the view.
    }
    
    @IBOutlet weak variableView:UITableView!
    
    functableView(_tableView:UITableView, numberOfRowsInSection section:Int) - > Int{
        return mainArray.count
    }
    
    functableView(_tableView:UITableView, cellForRowAtindexPath:IndexPath) - >UITableViewCell{
        let postcell=tableView.dequeueReusableCell(withIdentifier: "postcell")!
        postcell.textLabel?.text=mainArray [indexPath.row]
        return postcell
    }
    
    functableView(_tableView:UITableView, didSelectRowAtclassName:UITableViewCell){
        
        switchclassName{
        case is PostCell:
            
            guardlet viewControlr=storyboard?instantiateViewController(withIdentifier: "Post") as? Postelse{
                return
            }; navigationController?pushViewController(viewControlr, animated:true)
        
            
        case is FolderCell:
            
            guard let list = storyboard ?.instantiateViewController(withIdentifier: "List") as ? Listelse{
                return
            }; navigationController?pushViewController(list, animated:true)
            
        default:
            return
        }
        
    }

}

Posting VC Post

 class Post:UIViewController {

    var postString: String=""
    
    @IBOutlet weak var postTextField: UITextField!
    
    override func viewDidLoad(){
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
    
    @ IBAction func postBack(_sender:Any){
    self.navigationController?popToRootViewController(animated:true)
        
        postTextField.text=postString
        
        guard let list = tabBarController ?.viewControllers ?[0] as ? Listelse{
            return
        }
        
        let postString=postTextField.text???"
        
        list.tableView?.beginUpdates()
        list.tableView?.insertRows(at: IndexPath(row:0, section:0), with:.automatic)
        list.tableView?.endUpdates()
    } 

} 

Additional VC Fol

Cells for Folder

I don't have the code yet, but it's almost the same as Post, and the only custom cell I'm adding is FolderCell

add

Adding protocol to the list

Protocol 'ListEntryDelegate' cannot be nested inside another declaration

and for extension

Declaration is only valid at file scope 

Use of undeclared type 'ListEntryDelegate' 

The error appears

Is there any difference?

Note 1

structure ListEntry{
    enum CellType: String {
        case post="postcell"
        case folder="foldercell"
    }
    /// type
    varcellType —CellType
    
    /// text
    var text:String
    
    variable array —String
    
    // Increase properties as more information is displayed in one cell
    //...
}

Post and Fol to

listView.mainArray.insert(folString, at:0) 

I tried to

Cannot convert value of type 'String' to expected argument type 'ListEntry' 

will appear

Additional 2

When I tap PostCell, I want to put postString in Post, and when I tap FolderCell, I want to put FolCell array folArray in mainArray of List and transition to Navigation Controller

add

If I press Button on Post, what should I add to the Post Insart when I press Button on Fol?

swift

2022-09-30 21:50

1 Answers

After reading the updated questions, I understood as follows.

  • You want to view two custom cells, the UITableView has only one in the List (ViewController)
  • That UITableView uses section 0 only
  • In that section 0, a mix of cells for post and folder appears

Please let me know as soon as possible if there is any misunderstanding.

(It doesn't interfere with the operation, but naming the viewController List is hard to read because iOS apps don't have it. I think it's better to set it to ListViewController or at least ListVC.If you change the class name of the application you are creating, you may have trouble in many non-essential places, so the following is List (or Post).

Now, if you want to "show two custom cells" in one UITableView, you're saying, "I need information somewhere to distinguish between cell types."

mainArray I will stop preparing a separate array and putting information into it as it becomes more and more complicated and eventually breaks down as the app gets more complicated.

The information corresponding to "one cell" should be represented by "one type."

ListEntry.swift

import Foundation

structureListEntry{
    enum CellType: String {
        case post="postcell"
        case folder="foldercell"
    }
    /// type
    varcellType —CellType
    
    /// text
    var text:String
    
    // Increase properties as more information is displayed in one cell
    //...
}

The mainArray in List should be this ListEntry array, not the String array.

class List:UIViewController, UITableViewDataSource, UITableViewDelegate{
    
    varmainArray: ListEntry = [ ]
    // let initArray: [String] = [ ]
    
    override func viewDidLoad(){
        super.viewDidLoad()
        tableView.dataSource=self
        tableView.delegate=self
        
        // Do any additional setup after the view.
        // The following is not required if configured on the storyboard.
        tableView.register (PostCell.self, forCellReuseIdentifier: ListEntry.CellType.post.rawValue)
        tableView.register (FolderCell.self, forCellReuseIdentifier: ListEntry.CellType.folder.rawValue)
    }

    //...
}    

register(_:forCellReuseIdentifier:) calls are not required if you are using the storyboard, but if you are not, be sure to call all cell types

Now, if you really need to separate the processing by cell type, you can separate it based on the value of the cellType property in ListEntry.

For example, tableView(_:cellForRowAt:) should look like this.

 functableView(_tableView:UITableView, cellForRowAtindexPath:IndexPath)->UITableViewCell{
        letlistEntry=mainArray [indexPath.row]
        switchlistEntry.cellType{
        case.post:
            let postcell=tableView.dequeueReusableCell(withIdentifier:listEntry.cellType.rawValue, for:indexPath) as!PostCell
            // It seems that you need to use the special properties of `PostCell` instead of `textLabel`
            postcell.textLabel?.text=listEntry.text
            return postcell
        case.folder:
            let foldercell=tableView.dequeueReusableCell(withIdentifier:listEntry.cellType.rawValue, for:indexPath) as!FolderCell
            // It seems that you should use the 'FolderCell' exclusive property instead of 'textLabel'
            foldercell.textLabel?.text=listEntry.text
            return foldercell
        }
    }

(By the way, using as!, which is considered dangerous, was intentionally intended to "crash me if I set it wrong.")

Now, regarding tableView(_:didSelectRowAt:), the "Depending on Type" is written incorrectly, so if you don't rewrite it significantly, you'll have unpredictable problems.

If you want to rewrite it significantly anyway, I will rewrite the part of Post (ViewController) that accesses List.With the current code, if there is a design change such as which tab and which VC to display, you will have to rewrite the code.It can't be helped to some extent, but it's a good design to separate the code from the design as much as possible.

At the moment, Post seems to be only adding elements, so we have the following protocols in place to implement them in the List class:

(Actually, editing elements is also necessary, but I will not go any further in this answer because it is too far from the subject of using two different cells.)

(Assume to be added to the same file as List)

protocol ListEntryDelegate:class{
    funcaddEntry(_entry:ListEntry, at index:Int)
}

extension List:ListEntryDelegate{
    funcaddEntry(_entry:ListEntry, at index:Int){
        mainArray.insert (entry, at:index)
        tableView.insertRows(at: IndexPath(row:index, section:0), with:.automatic)
    }
}

The Post prerequisite for using the above delivery will look like this:

 class Post:UIViewController {
    
    weak vardelegate —ListEntryDelegate?//<- Added
    
    var postString: String=""
    
    @IBOutlet weak var postTextField: UITextField!
    
    override func viewDidLoad(){
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
    }
    
    @ IBAction func postBack(_sender:Any){
        self.navigationController?popToRootViewController(animated:true)
        
        let postString=postTextField.text???"
        
        // ↓ Invoke the Deligate method without calling the `List` method yourself
        let newEntry=ListEntry (cellType: .post, text:postString)
        delete?.addEntry(newEntry, at:0)//<- Can I add it to the top anytime?
    }
}

Now, if you make a similar change to the Fol (ViewController) side (the VC class name doesn't need to be written so many times, I think it's better to use a more descriptive name), the tableView(_:didSelectRowAt:) in the List class will look like this:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let listEntry = mainArray[indexPath.row]
        switch listEntry.cellType {
        case .post:
            guard let postVC = storyboard?.instantiateViewController(withIdentifier: "Post") as? Post else {
                return
            }
            postVC.delegate = self
            postVC.postString = listEntry.text
            / character/or other and ` Status information set here if there is any information that would deliver a C '
            //postVC.〜 = 〜
            navigationController?.pushViewController(postVC, animated: true)
        case .folder:
            guard let folderVC = storyboard?.instantiateViewController(withIdentifier: "Folder") as? Fol else {
                return
            }
            folderVC.delegate=self
            // If you have any other information to tell `folderVC`, set it up here
            // folderVC.~=~
            navigationController?pushViewController (folderVC, animated:true)
        }
    }

In tableView(_:didSelectRowAt:), didSelectRowAt: is passed to the value of type IndexPath.Declaring it in the UITableViewCell type can cause unpredictable failures during execution.

(Actually, the Swift compiler determines, "This is not tableView(_:didSelectRowAt:) in UITableViewDelegate!")

There are a lot of changes and it's a little ugly, but please read carefully which part of the class you want to modify and try it out.

As it has become somewhat difficult to read, there will be some things you don't understand or don't understand, but

  • "I want to view two types of custom cells" → Comments to this question
  • Other things → Ask new questions

If you use it differently, I think it would be better if you read this thread later.

About the "add" question → List class summary.

I think I confused you because I took advantage of the explanation and cut the code apart. If you apply all of the above modifications to a file (List.swift?) that contains the List class, it looks like this:

import UIKit

classList:UIViewController, UITableViewDataSource, UITableViewDelegate{
    
    varmainArray: ListEntry = [ ]
    // let initArray: [String] = [ ]
    
    override func viewDidLoad(){
        super.viewDidLoad()
        tableView.dataSource=self
        tableView.delegate=self
        
        // Do any additional setup after the view.
        // The following is not required if configured on the storyboard.
        tableView.register (PostCell.self, forCellReuseIdentifier: ListEntry.CellType.post.rawValue)
        tableView.register (FolderCell.self, forCellReuseIdentifier: ListEntry.CellType.folder.rawValue)
    }
    
    @IBOutlet weak variableView:UITableView!
    
    functableView(_tableView:UITableView, numberOfRowsInSection section:Int) - > Int{
        return mainArray.count
    }
    
    functableView(_tableView:UITableView, cellForRowAtindexPath:IndexPath) - >UITableViewCell{
        let listEntry = mainArray[indexPath.row]
        switch listEntry.cellType {
        case .post:
            let postcell=tableView.dequeueReusableCell(withIdentifier:listEntry.cellType.rawValue, for:indexPath) as!PostCell
            // It seems that you need to use the special properties of `PostCell` instead of `textLabel`
            postcell.textLabel?.text=listEntry.text
            return postcell
        case .folder:
            let foldercell=tableView.dequeueReusableCell(withIdentifier:listEntry.cellType.rawValue, for:indexPath) as!FolderCell
            // It seems that you should use the 'FolderCell' exclusive property instead of 'textLabel'
            foldercell.textLabel?.text=listEntry.text
            return foldercell
        }
    }
    
    functableView(_tableView:UITableView, didSelectRowAtindexPath:IndexPath){
        let listEntry = mainArray[indexPath.row]
        switch listEntry.cellType {
        case .post:
            guard let postVC = storyboard?.instantiateViewController(withIdentifier: "Post") as? Post else {
                return
            }
            postVC.delegate = self
            postVC.postString = listEntry.text
            / character/or other and ` Status information set here if there is any information that would deliver a C '
            //postVC.〜 = 〜
            navigationController?.pushViewController(postVC, animated: true)
        case .folder:
            guard let folderVC = storyboard?.instantiateViewController(withIdentifier: "Folder") as? Fol else {
                return
            }
            folderVC.delegate=self
            // If you have any other information to tell `folderVC`, set it up here
            // folderVC.~=~
            navigationController?pushViewController (folderVC, animated:true)
        }
    }
} //<- where `List` class definition is over

protocolListEntryDelegate:class{
    funcaddEntry(_entry:ListEntry, at index:Int)
}

extension List:ListEntryDelegate{
    funcaddEntry(_entry:ListEntry, at index:Int){
        mainArray.insert (entry, at:index)
        tableView.insertRows(at: IndexPath(row:index, section:0), with:.automatic)
    }
}

Isn't the place where ListEntryDelegate and ListEntryDelegate are listed differently from what I tried? (I'm sorry it's hard to understand.) Please try again with this content.


2022-09-30 21:50

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.