I am new to Swift and am having a bit of trouble with this bit in particular. Attached is some code I need to run as part of a project. Details are at the end.
class Screen:DataSource {
var items:[String]=[]
func run(){
let lv = TableView()
items = ["John","Paul","George","Ringo"]
let ds = self
lv.dataSource=ds
lv.drawList()
}
}
class TableView {
// This class displays a list given a DataSource
var dataSource:DataSource?
init (){
self.dataSource=nil
}
func drawList(){
for(var i=0;i<dataSource!.getSize();i++) {
print("\(dataSource!.getItem(at:i))")
print("--------------------------")
}
}
}
protocol DataSource {
func getSize()->Int
func getItem(at pos:Int)->String
}
let screen = Screen()
screen.run()
Without changing the "run" function/method, I need to have it print this:
John
--------------------------
Paul
--------------------------
George
--------------------------
Ringo
--------------------------
I'm not sure what to modify in my Screen class. This is what I have so far:
class Screen: DataSource {
var items: [String]=[]
func run() {
let lv = TableView()
items = ["John","Paul","George","Ringo"]
let ds = self
lv.dataSource=ds
lv.drawList()
}
//NEED HELP HERE
}
Any tips would be appreciated.
Use an extension to make Screen conform to DataSource:
class Screen {
var items:[String]=[]
func run(){
let lv = TableView()
items = ["John","Paul","George","Ringo"]
let ds = self
lv.dataSource=ds
lv.drawList()
}
}
extension Screen: DataSource {
func getSize() -> Int { return items.count }
func getItem(at index:Int) -> String { return items[index] }
}
You could also put the DataSource conformance and methods on the class itself, but doing it in an extension is a common style for organizing your class's members.
Related
How can I change the value inside a class from an outside function?
class MyClass {
var number = 5
}
func changeNumber(){
number = 2
}
I'm more or less new to delegate and protocols, but as I understand it always happens between two classes. My problem is that that function runs when a certain note is played on a midi device and I need to change the text value of a label in a viewControler class.
welcome to StackOverflow! You could just change the numberProperty like this
class MyClass{
var number = 5
}
func changeNumber(_ myClass: MyClass){
myClass.number = 2
}
let myClassInstance = MyClass()
print(myClassInstance.number) //5
changeNumber(myClassInstance)
print(myClassInstance.number) //2
The conventional approach is to make the view controller the delegate:
final class MyViewController: UIViewController {
var number = 5
let midi = Midi()
func viewDidLoad() {
super.viewDidLoad()
midi.delegate = self
}
}
extension MyViewController: MidiDelegate {
func changeNumber(device: Midi, value: Int) {
number = value
}
}
Edit: I am new to the field, I did not get a response. Can anyone tell me if I am missing some information? or how I could improve it?
I want an instance from the next object to set a Label text in my current cell
cell.Label.text = talents(nextIndex).name //<- Something of this sort
Tried: passing array input to SectionController to use as
talents[index+1]
Error: File out of range
My Section Controller
class SectionController: ListSectionController {
var talents: Talent!
weak var delegate: SectionControllerDelegate?
}
extension SectionController {
override func sizeForItem(at index: Int) -> CGSize {
guard
let context = collectionContext
else {
return .zero
}
let width = context.containerSize.width
let height = context.containerSize.height
return CGSize(width: width, height: height)
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
guard let cell = collectionContext?.dequeueReusableCellFromStoryboard(withIdentifier: "HomeCell",
for: self,
at: index) as? HomeCell else {
fatalError()
}
cell.nameLabel.text = talents.name
cell.descView.text = talents.largeDesc
cell.shortDesc.text = talents.smallDesc
// let nextTalent = talents[index+1]
// cell.nextIntroFunc(nextlabels: nextTalent)
return cell
}
override func didUpdate(to object: Any) {
talents = object as? Talent
}
}
My ListAdapterDataSource
extension Home: ListAdapterDataSource {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
print(talents)
return talents
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any)
-> ListSectionController {
return SectionController()
}
func emptyView(for listAdapter: ListAdapter) -> UIView? {
print("emptyView")
return nil
}
}
talents in the section controller isn't an array. It's just a single Talent, so calling [index + 1] on it is going to throw an error. (You might want to rename the property of the model in the section controller to talent so it's less confusing.)
Best practice for ListSectionController is to keep a 1:1 relationship between your object model and the section controller. If you want to have reference to the next object, you could create a cell/view model layer based on talent that includes reference to the next talent as well.
For example:
class TalentSectionModel: ListDiffable {
let talent: Talent
let nextTalent: Talent?
init(_ talent: Talent, nextTalent: Talent?) {
self.talent = talent
self.nextTalent = nextTalent
}
// whatever properties you want to expose
var name: String {
return talent.name
}
// example of exposing info about next talent
var nextTalentName: String? {
return nextTalent?.name
}
// ListDiffable protocol
// make sure to implement these two so diffing works
func diffIdentifier() -> NSObjectProtocol {}
func isEqual(toDiffableObject object: ListDiffable?) -> Bool {}
}
You could also just only pass in the necessary properties from the next talent without passing in the entire nextTalent if you don't want the section model to store the entire object.
Now with the TalentSectionModel you would map over your talents array in Home and return an array of view models in objects() under ListAdapterDataSource. Then, in your section controller you'll have the next talent's information available to you through the view model.
Resources:
ListKit Modeling and Binding (talks about the ListBindingSectionController, but also helpful to read for general understanding of model and section controller relationship),
ListKit best practices
I am trying to get a list of classes that have adopted a certain Protocol Migration: Preparation, and then to append those classes into an array. Here is the function in question:
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
var migrationsList = [Preparation.Type]()
var count = UInt32(0)
let classList = objc_copyClassList(&count)!
for i in 0..<Int(count) {
let classInfo = ClassInfo(classList[i])!
if let cls = classInfo.classObject as? Migration.Type {
migrationsList.append(cls)
print(cls.description)
}
}
return migrationsList
}
}
In principle all that should work, but when debugging I note that the classInfo variable is referring to each class in the iteration, but when assigning and casting in the if let as line, the constant cls is always blank - neither a value/class nor nil, just completely blank.
Any idea what I got wrong with that code?
I am also open to suggestions for any better way to get a list of all classes that have adopted a particular protocol...
EDIT: I forgot to provide the code for ClassInfo
import Foundation
struct ClassInfo: CustomStringConvertible, Equatable {
let classObject: AnyClass
let className: String
init?(_ classObject: AnyClass?) {
guard classObject != nil else { return nil }
self.classObject = classObject!
let cName = class_getName(classObject)!
self.className = String(cString: cName)
}
var superclassInfo: ClassInfo? {
let superclassObject: AnyClass? = class_getSuperclass(self.classObject)
return ClassInfo(superclassObject)
}
var description: String {
return self.className
}
static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool {
return lhs.className == rhs.className
}
}
I can't explain why cls is always blank, like I said in my comment it's something I run into every time I'm dealing with meta types. As for making the code work as intended, I found this q&a and updated it with Swift 3 to get this code which should cover your situation. It's important to stress that this will only work if you correctly expose Swift to the Objective-C runtime.
Drop this code anywhere and call print(Migrations.getMigrations()) from a convenient entry point.
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
return getClassesImplementingProtocol(p: Preparation.self) as! [Preparation.Type]
}
static func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
let classes = objc_getClassList()
var ret = [AnyClass]()
for cls in classes {
if class_conformsToProtocol(cls, p) {
ret.append(cls)
}
}
return ret
}
static func objc_getClassList() -> [AnyClass] {
let expectedClassCount = ObjectiveC.objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = ObjectiveC.objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyClass]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)] {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
class Migration: Preparation {
}
#objc
protocol Preparation {
}
I am quite new to swift and I have a question regarding the definition of functions in a class.
I want to generate several items and give each of them a special function so I can run them by itemxy.useitem()
class item {
var name = "test"
func useitem(){
print("test")
}
}
let staff = item()
staff.name = "Staff"
staff.useitem() // prints: test
*override staff.useitem() = {print("this is a staff")}*
staff.useitem() // prints: this is a staff
how can I align a new function to my item staff?
These are not entirely swift related and are more general programming, you wont get your answers to such problems here. You should read up on basic programming principles before you tackle things further.
Having said that your problem is easily solved with Inheritance or Protocols.
Inheritance
class Item {
var name: String
init(name: String) {
self.name = name
}
func use() {
print("using a \(name)")
}
}
class Hat: Item {
override func use() {
print("I put on a \(name)")
}
}
class Pen: Item {
init() {
super.init(name: "Pen")
}
}
let pen = Pen()
pen.use()
let hat = Hat(name: "Beanie")
hat.use()
Protocol
protocol Item {
var name: String { get set }
func use()
}
extension Item {
func use() {
print("using a \(name)")
}
}
struct Pen: Item {
var name: String
init() {
self.name = "Pen"
}
}
struct Hat: Item {
var name: String
func use() {
print("I put on a \(name)")
}
}
let pen = Pen()
pen.use()
let hat = Hat(name: "Beanie")
hat.use()
The Goal
I want to call the instantiateViewControllerWithIdentifier method on a Storyboard to get a view controller. But I want to do it with generics for code reuse and simplicity.
The Non-Compiling Code
let viewController = StoryboardHelper.viewControllerFromStoryboard<ChooseProviderViewController>()
class func viewControllerFromStoryboard<T>() -> T {
let storyboard = mainStoryboard()
let viewController = storyboard.instantiateViewControllerWithIdentifier(resolveViewController(T)) as T
return viewController
}
private class func resolveViewController<T>() -> String {
if T is ChooseProviderViewController {
return chooseProviderViewControllerIdentifier;
}
return ""
}
Now, the above code is what I have at the moment, and how I would like the API to be. It would be nice to say "StoryboardHelper, here is the type of the view controller I want, go get the view controller instance for me".
So, in the end, I would like some way to do this:
let instanceOfXViewController = StoryboardHelper.viewControllerFromStoryboard<XViewController>()
let instanceOfYViewController = StoryboardHelper.viewControllerFromStoryboard<YViewController>()
I have a answer for you but it will only work with Swift 1.2 (+), in 1.1 it will return a intance of the wrong (super) type. I changed it to a more general situation/naming but it is effectly the same problem. First since you only want to manage UIViewControllers you should restrict T. Like <T:UIViewController> . Secondly you should/can pass the Type as an argument and create an instance of it.
import Foundation
class Helper {
class func instanceFromT<T:R>(T.Type) -> T {
return T.self()
}
}
class R {
required init () {
}
func me() -> String {
return "me is R"
}
}
class B : R {
override func me() -> String {
return "me is B"
}
}
class A : R {
override func me() -> String {
return "me is A"
}
}
let my = Helper.instanceFromT(A.self)
println(my.me()) // "me is A"