I have a plain FriendsController class which is UICollectionViewController subclass, so when I implement UICollectionViewDataSource protocol required functions why do I need to put override key word in front of func declaration it is even do not implemented in UICollectionViewController parent class.
class FriendsController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
fileprivate let cellId = "cellId"
var messages: [Message]?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Recent"
collectionView?.backgroundColor = UIColor.white
collectionView?.register(MessageCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.alwaysBounceVertical = true
setupData()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = messages?.count {
return count
}
return 0
}
I've seen that UIViewCollectionController did not implement protocol required functions:
open class UICollectionViewController : UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
public init(collectionViewLayout layout: UICollectionViewLayout)
public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
public init?(coder aDecoder: NSCoder)
open var collectionView: UICollectionView?
// Defaults to YES, and if YES, any selection is cleared in viewWillAppear:
// This property has no effect if the useLayoutToLayoutNavigationTransitions property is set to YES
open var clearsSelectionOnViewWillAppear: Bool
// Set to YES before pushing a a UICollectionViewController onto a
// UINavigationController. The top view controller of the navigation controller
// must be a UICollectionViewController that was pushed with this property set
// to NO. This property should NOT be changed on a UICollectionViewController that
// has already been pushed onto a UINavigationController.
#available(iOS 7.0, *)
open var useLayoutToLayoutNavigationTransitions: Bool
// The layout object is needed when defining interactive layout to layout transitions.
#available(iOS 7.0, *)
open var collectionViewLayout: UICollectionViewLayout { get }
// Defaults to YES, and if YES, a system standard reordering gesture is used to drive collection view reordering
#available(iOS 9.0, *)
open var installsStandardGestureForInteractiveMovement: Bool
}
UICollectionViewController conforms to UICollectionViewDataSource and UICollectionViewDelegate so it has to implement the required methods of those protocols, including numberOfItemsIn:, which is why you need to use override
It's not a superclass, much rather a protocol, which your class needs to conform to.
Related
I am currently following a video tutorial course about test driven development of iOS in Swift, but when testing Table View in View Controller, I get stuck, since I don't understand why we need NSObject in Interface builder like the picture below:
Movie Library Data Service is inheritted NSObject class:
the class of MovieLibraryDataService is like this:
import UIKit
class MovieLibraryDataService: NSObject, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
and the MovieLibraryDataService class will be used in XCTestCase is like this :
#testable import FilmFest
class LibraryViewControllerTests: XCTestCase {
var sut: LibraryViewController!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LibraryViewControllerID") as! LibraryViewController
_ = sut.view
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
// MARK: Nil Checks
func testLibraryVC_TableViewShouldNotBeNil() {
XCTAssertNotNil(sut.libraryTableView)
}
// MARK: Data Source
func testDataSource_ViewDidLoad_SetsTableViewDataSource() {
XCTAssertNotNil(sut.libraryTableView.dataSource)
XCTAssertTrue(sut.libraryTableView.dataSource is MovieLibraryDataService)
}
// MARK: Delegate
func testDelegate_ViewDidLoad_SetsTableViewDelegate() {
XCTAssertNotNil(sut.libraryTableView.delegate)
XCTAssertTrue(sut.libraryTableView.delegate is MovieLibraryDataService)
}
// MARK: Data Service Assumptions
func testDataService_ViewDidLoad_SingleDataServiceObject() {
XCTAssertEqual(sut.libraryTableView.dataSource as! MovieLibraryDataService, sut.libraryTableView.delegate as! MovieLibraryDataService)
}
}
and the definition of LibraryViewController:
import UIKit
class LibraryViewController: UIViewController {
#IBOutlet weak var libraryTableView: UITableView!
#IBOutlet var dataService: MovieLibraryDataService!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.libraryTableView.dataSource = dataService
self.libraryTableView.delegate = dataService
}
}
I really don't understand why I need to make that MovieLibraryDataService class
I usually use:
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
but why do I need to write :
self.libraryTableView.dataSource = dataService
self.libraryTableView.delegate = dataService
MovieLibraryDataService is just another class which implements UITableViewDataSource and UITableViewDelegate, with the difference that the storyboard instantiates it and that storyboard-created instance is bound via the IBOutlet #IBOutlet var dataService: MovieLibraryDataService!. All objects in the storyboard are storyboard-created, that’s why one has to bind them to variables to use if they are not bound to other variables you use already.
Naming the variable dataService is just a fancy way of saying that it is supposed to serve, in this case as dataSource and delegate for a tableView, since it implements those delegate-protocols.
Since the dataService is instantiated by the storyboard, you could try if you can bind the dataService inside the storyboard to tableView. This is possible because you have the dataService referencable in the storyboard. This would replace setting dataSource and delegate in the viewController.
Another example
AppDelegate is also an NSObject inside the Storyboard so the UIApplication/NSApplication inside the storyboard is able to reference that to use, avoids having to set up AppDelegate yourself outside a storyboard. (Would otherwise be ugly, maybe that’s macOS only though, since mac apps have to show a Menu even if its empty.)
Edit
You can use NSObject on the Storyboard for different purposes, one of them could be also the delegation. Instead of setting it programatically like:
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
you can hold control and then set the corresponding delegates like below:
Original Answer
Because MovieLibraryDataService that conforms to UITableViewDataSource and UITableViewDelegate and not your LibraryViewController.
If you want to change it as you are always used to, change your code to:
class LibraryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var libraryTableView: UITableView!
var dataService = MovieLibraryDataService()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
Personally I would suggest to keep it as you have it, as View Controllers tend to grow and become like we call it Massive View Controller
I created a UICollectionView in an .xib file like so:
Based on this post: Can´t add items to UICollectionView inside UIView xib
I understand that you cannot directly add a UICollectionViewCell within a UICollectionView that is contained in a .xib, but instead have to create another .xib with only the UICollectionViewCell, which is what I did:
I create a GridViewCell class and add it as the Custom Class for the UICollectionViewCell:
class GridViewCell: UICollectionViewCell
{
#IBOutlet var clothingImageView: UIImageView!
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
}
I also created a GridViewGallery class and associated with my UICollectionView .xib file:
extension GridViewGallery: UICollectionViewDelegate
{
}
extension GridViewGallery: UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return clothingImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridViewCell", for: indexPath) as! GridViewCell
cell.clothingImageView.image = clothingImages[indexPath.row].image
return cell
}
}
class GridViewGallery: UIView
{
#IBOutlet var gridLayoutCollectionView: UICollectionView!
var clothingImages = [INSPhotoViewable]()
override init(frame: CGRect)
{
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
let gridViewCell = UINib(nibName: "GridViewCell", bundle: nil)
gridLayoutCollectionView.register(gridViewCell, forCellWithReuseIdentifier: "GridViewCell")
}
func storeGridViewImages(photos: [INSPhotoViewable])
{
clothingImages = photos
gridLayoutCollectionView.dataSource = self
gridLayoutCollectionView.delegate = self
}
}
However, I get a unexpectedly found nil while unwrapping an Optional value error when I tried to load the XIB file to a UIView in another class:
#IBAction func gridViewLayout(_ sender: UIBarButtonItem)
{
if let gridView = Bundle.main.loadNibNamed("GridViewGallery", owner: self, options: nil)?.first as? GridViewGallery
{
print("GRID VIEW FOUND")
self.addSubview(gridView)
}
}
The GridViewGallery File Owner's Custom Class is empty, as I set the Custom Class on the UIView property instead as seen in the first screenshot:
However, if I instead set the File Owner's Custom Class to GridViewGallery and leave the Custom Class as blank from the UIView itself, then I get a crash with error this class is not key value coding-compliant for the key gridLayoutCollectionView.
I'm not sure I understand what is causing this, and which way I should be setting the Custom Class.
Can someone assist me?
Thanks.
I am working with Xcode 7 and swift.
I am trying to connect a label that is on a Collection View prototype cell to my code. I know to do this, I have to make a subclass of UICollectionViewCell and put it in that class instead of the view controller, which I did. I run the app immediately after adding the outlet to the subclass, and the app crashes. I noticed at the top of the error message it says: Unknown Class nameOfSubclass in Interface Builder File. I have looked at other stack overflow questions and they say it is a simple matter of setting the module under the custom class. I did this, but the app still crashes and now it says: Unknown class _TtC13 (app name / module name) 17(nameOfSubclass) in Interface Builder file.
identity inspector of the prototype cell
identity inspector of the UICollectionView
Code
import UIKit
class SecondViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
private let reuseIdentifier = "hourlyWeatherCell"
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 48
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) //as! hourlyWeatherCell
// Configure the cell
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
printDate()
// Do any additional setup after loading the view.
self.collectionView.dataSource = self
self.collectionView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
class hourlyWeatherCell: UICollectionViewCell {
#IBOutlet weak var temperatureHLabel: UILabel!
}
}
With a little tinkering I got it to work. For some reason Xcode was not compiling the subclass it the UIViewContoller. I simply moved the subclass out of the view controller class (making the subclass a class), and everything worked fine.
Goal
I want to create a custom view that has a UITableView as a subview.
The custom view creates the table view programmatically. To the outside world (i.e., the ViewController), though, the custom view itself would appear to be a table view.
What I've tried
import UIKit
class CustomTableView: UIView {
// Do I make outlets?
//#IBOutlet var dataSource: UITableViewDataSource?
//#IBOutlet var delegate: UITableViewDelegate?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect){
super.init(frame: frame)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews()
var tableView: UITableView!
tableView = UITableView(frame: self.bounds)
// I'm not sure how to set the delegate and dataSource
// tableView.dataSource = ???
// tableView.delegate = ???
self.addSubview(tableView)
}
}
After creating the UITableView programmatically and adding it as a subview to the custom view parent, I can't figure out how to get the custom view to act like it is the table view. That is, I don't know how to get the custom view to do the communication between the View Controller and the table view for the delegate and dataSource.
What I've read
Designing Your Data Source and Delegate
Create a static UITableView without Storyboards
These articles seemed good, but I got a little lost.
How do I make the custom view act like it's own table subview with regard to delegate and data source?
The solution is to make the tableView a property of the custom class (as #BaseZen suggested). Then provide properties and methods in the custom class to mimic and pass along the properties and methods needed from tableView.
import UIKit
#IBDesignable class UICustomTableView: UIView {
private var myTableView: UITableView
required init(coder aDecoder: NSCoder) {
myTableView = UITableView()
super.init(coder: aDecoder)
}
override init(frame: CGRect){
myTableView = UITableView()
super.init(frame: frame)
}
override func awakeFromNib() {
super.awakeFromNib()
}
// TODO: #IBOutlet still can't be set in IB
#IBOutlet weak var delegate: UITableViewDelegate? {
get {
return myTableView.delegate
}
set {
myTableView.delegate = newValue
}
}
// TODO: #IBOutlet still can't be set in IB
#IBOutlet weak var dataSource: UITableViewDataSource? {
get {
return myTableView.dataSource
}
set {
myTableView.dataSource = newValue
}
}
func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String) {
myTableView.registerClass(cellClass, forCellReuseIdentifier: identifier)
}
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell? {
return myTableView.dequeueReusableCellWithIdentifier(identifier)
}
override func layoutSubviews() {
super.layoutSubviews()
// ...
// setup UITableView
myTableView.frame = self.bounds
self.addSubview(myTableView)
// ...
}
}
Then it can be used like a normal UITableView:
import UIKit
class TableViewDemoVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var customTableView: UICustomTableView!
var items: [String] = ["One", "Two", "Three"]
override func viewDidLoad() {
super.viewDidLoad()
// setup the table view from the IB reference
customTableView.delegate = self
customTableView.dataSource = self
customTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = self.customTableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
// ...
}
It's not the most elegant, but jump out a level:
class MyCustomViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
myCustomView = CustomTableView()
myCustomView.frame = ... /* layout programatically */
/* Alternatively to the above 2 lines,
lay out myCustomView in StoryBoard,
and capture it as an #IBOutlet. It will then be ready here
to muck with, well before it gets displayed and needs the data */
myCustomView.tableView.delegate = self
myCustomView.tableView.dataSource = self
}
/* Now implement all your dataSource and delegate methods */
}
* IMPORTANT *
The key that you're missing is that tableView must be a stored property of your custom view. It's important and needs to be promoted from a silly local! It should also be initialized in the awakeFromNib() function, even if you don't know the frame size. Then reset its frame at layout time.
At a higher level, I don't know "What You're Really Trying To Do." It may not actually be the right implementation technique to be embedding a UITableView within a custom view; consider just laying out the UITableView in the main view. Then if you need decoration around it, lay those out as separate views in StoryBoard.
i am new to iOS, so i am confronted with this problem: I wrote an UICollectionView in an UIViewController (parent) with an UICollectionResuableView as a header.
class ParentViewController: UIViewController, SideBarDelegate {
#IBOutlet weak var mPreviewsCollection: UICollectionView!
override func viewDidLoad() {
...
}
...
}
This UICollectionResuableView contain another UICollectionView (child) to provide dynamic customized tabs (maybe not the best idea, but it looks as was intended).
class ChildHeaderCollectionReusableView: UICollectionReusableView {
#IBOutlet var mTabHeader: UICollectionView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
...
}
...
Now i want to to change the content of the parent UICollectionView (reloadData()) when selecting a cell in the child. How can i do that? How can i execute methods in the running instance of the parent from outside?
e.g. var rootViewController = self.window!.rootViewController as KioskViewController returns another instance of my parent UIViewController with zero content