I am using some values to perform some calculations. For testing purposes I show in Label1 a value as string, since it is stored as a string and in Label2 I show a casted value as a Double since I need them at the end as doubles for my calculations.
The weird thing is, that when I access the ViewController the first time it doesn't show any values. But if I go back and klick on it again using the navigation controller it actually works. But I need the values right away cause my original intention is as I said, not showing some labels but rather making some calculations with it.
I made a little gif to show you what the problem is but I have problem with adding photos. Basically what happens is, that I click on the ViewController with the labels and nothing is showed. I go back and press again and the values will be showed in the labels.
Why is that and how can it be showed right away/ used for calculations right away
Thanks for the help. :)
class AHPfinalPreferencesViewController: UIViewController {
var ahpPrios = [AHPPriorityStruct]()
let decoder = JSONDecoder()
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
let ajkpXc = globaLajkpXc
let ajkpXijr = globaLajkpXijr
let valueA = globaLajkpXc
let valueB = Double(globaLajkpXijr)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UserService.ahpPref(for: User.current) { (ahpPrios) in
self.ahpPrios = ahpPrios
print("This is our AHP PRIOS", ahpPrios)
for ahpPrio in ahpPrios {
print(ahpPrio)
}
print("this is the global ajk. ", self.ajkpXc)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Mark: - Get Data
label1.text = valueA
label2.text = "\(String(describing: valueB))"
// MARK: - Set Values for calculation
// setValues()
// ahpCalculation()
}
}
Could it be because of the globalVariables? I know that it is not the right way to do it but for my purposes its absolutely "okay"
import Foundation
import FirebaseAuth.FIRUser
import FirebaseDatabase
import FirebaseUI
import FirebaseAuth
import CodableFirebase
var globaLajkpXc: String = String()
var globaLajkpXijr: String = String()
var globaLajkpXqpa: String = String()
struct UserService {
static func ahpPref(for user: User, completion: #escaping ([AHPPriorityStruct]) -> Void) {
let ref = Database.database().reference().child("AHPRatings").child(user.uid)
ref.observe(DataEventType.value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let ahpPrios = try FirebaseDecoder().decode(AHPPriorityStruct.self, from: value)
print(ahpPrios)
// MARK: - lets store the values in the actual constants :)
let ajkpXc = ahpPrios.ajkpXc
let ajkpXijr = ahpPrios.ajkpXijr
let ajkpXqpa = ahpPrios.ajkpXqpa
globaLajkpXc = ajkpXc ?? "no Value"
globaLajkpXijr = ajkpXijr ?? "no Value"
globaLajkpXqpa = ajkpXqpa ?? "no Value"
} catch let error {
print(error)
}
})
}
}
[1]: https://i.stack.imgur.com/VKxaE.png
You are calling UserService's ahpPref in your controller's viewWillAppear. BUT you are attempting to put your valueA (globaLajkpXc's value) to your label in your controller's viewDidLoad.
So what does that mean? Do you know which of these two controller's life cycle method gets called and when they do get called?
To solve your problem, have your label assigning value code
label1.text = globaLajkpXc
move in the completion block of your ahpPref (in the viewWillAppear).
Here's the Apple's documentation about the UIViewController's lifecycle: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html
Also, below this line: globaLajkpXqpa = ajkpXqpa ?? "no Value"
add your completion call, like:
completion([ahpPrios]).
This should make my answer above work.
I have been struggling to access label from UITableViewCell in a UITableViewController..
The code for the class reboundShotsCount looks like this:
This class is defined inside the ShotDetail Class which is subclass of UITableViewController.
public class reboundShotsCount : UITableViewCell {
#IBOutlet public var reboundCountLabel: UILabel!
}
The code in ShotDetail
var shots : [Shot] = [Shot]()
var shot : Shot!
var comments : [Comment] = [Comment]()
var previousImageManager : ImageManager!
override func viewDidLoad() {
super.viewDidLoad()
// Enable GIF decompression
self.previousImageManager = ImageManager.shared
let decoder = ImageDecoderComposition(decoders: [AnimatedImageDecoder(), ImageDecoder()])
let loader = ImageLoader(configuration: ImageLoaderConfiguration(dataLoader: ImageDataLoader(), decoder: decoder), delegate: AnimatedImageLoaderDelegate())
let cache = AnimatedImageMemoryCache()
ImageManager.shared = ImageManager(configuration: ImageManagerConfiguration(loader: loader, cache: cache))
title = shot.title
let reboundShotCell = reboundShotsCount()
// the countLabel returns nil
let countLabel = reboundShotCell.reboundCountLabel
// The error comes here.
countLabel.text = "\(shot.reboundCount)"
let api = DribbleObjectHandler()
api.loadComments(shot.commentsUrl, completion: didLoadComments)
api.loadShots(shot.reboundUrl, completion: didLoadReboundShots)
}
i am aware that the code creates a new instance of reboundShotsCount in viewDidLoad..
And if i define shot variable inside the reboundShotsCount class. it return's nil.
I am stuck and dont know what to do?
You need to set text in cellForRowAtIndexPath dataSource method of tableview....What you did is in viewDidLoad and at that point tableview has not loaded yet and neither tableviewcell –
I created an UIViewController which has two UITexFields and an UIImageView inside of it.
What I want it to be is a profile page, which provides information usable everywhere in the app.
What I tried to do is the following:
I created a Class with this code (based on Apple's tutorial on creating apps):
import UIKit
Class ProfilClass: NSObject, NSCoding {
//MARK: Properties
var bild: UIImage?
var vorname: String
var geburt: String
//MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("profil")
//MARK: Types
struct propKey {
static let bildKey = "bild"
static let vornameKey = "vorname"
static let geburtKey = "geburt"
}
//MARK: Initialization
init?(bild: UIImage?, vorname: String, geburt: String){
self.bild = bild
self.vorname = vorname
self.geburt = geburt
super.init()
if vorname.isEmpty || geburt.isEmpty {
return nil
}
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(bild, forKey: propKey.bildKey)
aCoder.encodeObject(vorname, forKey: propKey.vornameKey)
aCoder.encodeObject(geburt, forKey: propKey.geburtKey)
}
required convenience init?(coder aDecoder: NSCoder){
let bild = aDecoder.decodeObjectForKey(propKey.bildKey) as? UIImage
let vorname = aDecoder.decodeObjectForKey(propKey.vornameKey) as! String
let geburt = aDecoder.decodeObjectForKey(propKey.geburtKey) as! String
self.init(bild: bild, vorname: vorname, geburt: geburt)
}
}
I try to use this Class inside of my UIViewController:
override func viewDidLoad() {
[...]
if let profil = profil{
vornameLabel.text = profil.vorname
vornameField.text = profil.vorname
profilePic.image = profil.bild
geburtstagsLabel.text = profil.geburt
geburtstagField.text = profil.geburt
}
}
And when a save button is tapped:
#IBAction func butTap(sender: UIBarButtonItem) {
let vorname = vornameField.text
let geburt = geburtstagField.text
let bild = profilePic.image
profil = ProfilClass(bild: bild, vorname: vorname!, geburt: geburt!)
}
But after I close the UIViewController by going back to another one and reopen it, all the information is lost.
I don't know how to get the information again (I assume it is saved somewhere).
Can anyone help me?
public class MyViewState : NSObject{
static var isFromLogin = false
static var isFromCQ = false
}
add above to anywhere in any view controller it will accessible everywhere like
MyViewState.isFromLogin
make a shared class like following this will save data all the time
Class ProfilClass: NSObject, NSCoding {
static let sharedInstance = ProfilClass()
// here methods etc will go
}
and
ProfilClass.sharedInstance.(properties or method)
If you want data after app relaunched too , then save this to Userdefaults and load this class again and access anywhere in the app
Your mistake comes right at the end:
I assume it is saved somewhere
It is not, unless you ask for it to be saved. You need to use NSUserDefaults or NSKeyedArchiver to write your object. You've written all the code required to make that work, now you just need to do the reading and writing.
For example, to write your saved data you'll need something like this:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(profil, forKey: "SavedUserProfile")
I can't comment on reading because you have limited the code you posted:
[...]
if let profil = profil{
That is where your reading code should happen. I'm guessing(!) you're doing something like this:
let defaults = NSUserDefaults.standardUserDefaults()
if let profil = defaults.objectForKey("SavedUserProfile") as? ProfilClass
As you're following a specific Apple tutorial: You might find it easier just to continue following there rather than trying to use a different solution. Specifically, you need to continue onto the heading "Save and Load the Meal List", which is where the actual saving and loading happens. Apple writes the data to disk rather than user defaults, so you should follow along.
To be clear: the code you've written only enables your object to be saved and loaded. It doesn't actually do the saving.
var isFromLogin = false
var isFromCQ = false
Class ProfilClass: NSObject, NSCoding {.......}
add above to class it will accessible everywhere publicly . OR also create .swift file and declare constant variable to access globally
How can I write a function in a separate swift file and use it (import it) to my ViewController.swift file? I have written a lot of code and all of the code is in the ViewController.swift file, I really need to make this look good and place functions on separate files, for cleaner code. I have functions dealing with parsing HTML, functions dealing with ordering results, presenting results, responding to user actions, etc. Many thanks for any help!
if let htmlString = String(contentsOfURL: checkedUrl, encoding: NSUTF8StringEncoding, error: nil) {
// Parsing HTML
let opt = CInt(HTML_PARSE_NOERROR.value | HTML_PARSE_RECOVER.value)
var err : NSError?
var parser = HTMLParser(html: htmlString, encoding: NSUTF8StringEncoding, option: opt, error: &err)
var bodyNode = parser.body
// Create an array of the part of HTML you need
if let inputNodes = bodyNode?.findChildTags("h4") { //inputNodes is an array with all the "h4" tag strings
for node in inputNodes {
let result = html2String(node.rawContents)
println("Nyheter: \(result)")
}
}
When I add that function to a separate swift file, how can I use it in my ViewDidLoad method using a "shorthand"? A short keyword that grabs that chunk of code and use it?
Easy. You just create a new Swift file into your Xcode project (File - New - File - Swift file or just ⌘-N) and put your functions and classes there. No need to import anything in your view controller file as both files are part of the same package and thus see each others functions and types (unless marked as private).
func parseHtml(url: NSURL) -> String { ... your code goes here ... }
You need to use singletons.
Create NSObject in User.swift
import Foundation
import UIKit
class User: NSObject {
var name: String = 0
func getName() -> String{
name = "Erik Lydecker"
return name
}
}
Then initialize your object and trigger the method there.
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let instanceOfUser = User()
instanceOfUser.getName() // Erik Lydecker
}
}
You can create a Utils class, filled with static variables/functions
For example:
class Utils {
static func convert(to variable: String) -> Int? {
... do some stuff to variable
return newVariable
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = Utils.convert(to: "123") {
print(converted)
}
}
By making use of static functions you can access them anywhere and everywhere to reuse them.
------------------
Another way is to make use of extensions
For example:
extension String {
var toInt: Int? {
return Int(self)
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = "SomeValue".toInt {
print(converted)
}
}
Both of these can be used in many different scenarios.
How to achieve reflection in Swift Language?
How can I instantiate a class
[[NSClassFromString(#"Foo") alloc] init];
You must put #objc(SwiftClassName) above your swift class.
Like:
#objc(SubClass)
class SubClass: SuperClass {...}
This is the way I init derived UIViewController by class name
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass()
More information is here
In iOS 9
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass.init()
Less hacky solution here: https://stackoverflow.com/a/32265287/308315
Note that Swift classes are namespaced now so instead of "MyViewController" it'd be "AppName.MyViewController"
Deprecated since XCode6-beta 6/7
Solution developed using XCode6-beta 3
Thanks to the answer of Edwin Vermeer I was able to build something to instantiate Swift classes into an Obj-C class by doing this:
// swift file
// extend the NSObject class
extension NSObject {
// create a static method to get a swift class for a string name
class func swiftClassFromString(className: String) -> AnyClass! {
// get the project name
if var appName: String? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as String? {
// generate the full name of your class (take a look into your "YourProject-swift.h" file)
let classStringName = "_TtC\(appName!.utf16count)\(appName)\(countElements(className))\(className)"
// return the class!
return NSClassFromString(classStringName)
}
return nil;
}
}
// obj-c file
#import "YourProject-Swift.h"
- (void)aMethod {
Class class = NSClassFromString(key);
if (!class)
class = [NSObject swiftClassFromString:(key)];
// do something with the class
}
EDIT
You can also do it in pure obj-c:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%d%#%d%#", appName.length, appName, className.length, className];
return NSClassFromString(classStringName);
}
I hope this will help somebody !
UPDATE: Starting with beta 6 NSStringFromClass will return your bundle name plus class name separated by a dot. So it will be something like MyApp.MyClass
Swift classes will have a constructed internal name that is build up of the following parts:
It will start with _TtC,
followed by a number that is the length of your application name,
followed by your application name,
folowed by a number that is the length of your class name,
followed by your class name.
So your class name will be something like _TtC5MyApp7MyClass
You can get this name as a string by executing:
var classString = NSStringFromClass(self.dynamicType)
Update In Swift 3 this has changed to:
var classString = NSStringFromClass(type(of: self))
Using that string, you can create an instance of your Swift class by executing:
var anyobjectype : AnyObject.Type = NSClassFromString(classString)
var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type
var rec: AnyObject = nsobjectype()
It's almost the same
func NSClassFromString(_ aClassName: String!) -> AnyClass!
Check this doc:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString
I was able to instantiate an object dynamically
var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
println("yes!")
}
I haven't found a way to create AnyClass from a String (without using Obj-C). I think they don't want you to do that because it basically breaks the type system.
For swift2, I created a very simple extension to do this more quickly
https://github.com/damienromito/NSObject-FromClassName
extension NSObject {
class func fromClassName(className : String) -> NSObject {
let className = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String + "." + className
let aClass = NSClassFromString(className) as! UIViewController.Type
return aClass.init()
}
}
In my case, i do this to load the ViewController I want:
override func viewDidLoad() {
super.viewDidLoad()
let controllers = ["SettingsViewController", "ProfileViewController", "PlayerViewController"]
self.presentController(controllers.firstObject as! String)
}
func presentController(controllerName : String){
let nav = UINavigationController(rootViewController: NSObject.fromClassName(controllerName) as! UIViewController )
nav.navigationBar.translucent = false
self.navigationController?.presentViewController(nav, animated: true, completion: nil)
}
This will get you the name of the class that you want to instantiate. Then you can use Edwins answer to instantiate a new object of your class.
As of beta 6 _stdlib_getTypeName gets the mangled type name of a variable. Paste this into an empty playground:
import Foundation
class PureSwiftClass {
}
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
The output is:
TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS
Ewan Swick's blog entry helps to decipher these strings: http://www.eswick.com/2014/06/inside-swift/
e.g. _TtSi stands for Swift's internal Int type.
In Swift 2.0 (tested in the Xcode 7.01) _20150930
let vcName = "HomeTableViewController"
let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
// Convert string to class
let anyobjecType: AnyObject.Type = NSClassFromString(ns + "." + vcName)!
if anyobjecType is UIViewController.Type {
// vc is instance
let vc = (anyobjecType as! UIViewController.Type).init()
print(vc)
}
xcode 7 beta 5:
class MyClass {
required init() { print("Hi!") }
}
if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type {
let object = classObject.init()
}
string from class
let classString = NSStringFromClass(TestViewController.self)
or
let classString = NSStringFromClass(TestViewController.classForCoder())
init a UIViewController class from string:
let vcClass = NSClassFromString(classString) as! UIViewController.Type
let viewController = vcClass.init()
I am using this category for Swift 3:
//
// String+AnyClass.swift
// Adminer
//
// Created by Ondrej Rafaj on 14/07/2017.
// Copyright © 2017 manGoweb UK Ltd. All rights reserved.
//
import Foundation
extension String {
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
}
class StringClassConverter<T> {
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String else {
return nil
}
guard let aClass: T.Type = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
The use would be:
func getViewController(fromString: String) -> UIViewController? {
guard let viewController: UIViewController.Type = "MyViewController".converToClass() else {
return nil
}
return viewController.init()
}
I think I'm right in saying that you can't, at least not with the current beta (2). Hopefully this is something that will change in future versions.
You can use NSClassFromString to get a variable of type AnyClass but there appears to be no way in Swift to instantiate it. You can use a bridge to Objective C and do it there or -- if it works in your case -- fall back to using a switch statement.
Apparently, it is not possible (anymore) to instantiate an object in Swift when the name of the class is only known at runtime. An Objective-C wrapper is possible for subclasses of NSObject.
At least you can instantiate an object of the same class as another object given at runtime without an Objective-C wrapper (using xCode Version 6.2 - 6C107a):
class Test : NSObject {}
var test1 = Test()
var test2 = test1.dynamicType.alloc()
In Swift 2.0 (tested in the beta2 of Xcode 7) it works like this:
protocol Init {
init()
}
var type = NSClassFromString(className) as? Init.Type
let obj = type!.init()
For sure the type coming from NSClassFromString have to implement this init protocol.
I expect it is clear, className is a String containing the Obj-C runtime name of the class which is by default NOT just "Foo", but this discussion is IMHO not the major topic of your question.
You need this protocol because be default all Swift classes don't implement an init method.
Looks like the correct incantation would be...
func newForName<T:NSObject>(p:String) -> T? {
var result:T? = nil
if let k:AnyClass = NSClassFromString(p) {
result = (k as! T).dynamicType.init()
}
return result
}
...where "p" stands for "packaged" – a distinct issue.
But the critical cast from AnyClass to T currently causes a compiler crash, so in the meantime one must bust initialization of k into a separate closure, which compiles fine.
I use different targets, and in this case the swift class is not found. You should replace CFBundleName with CFBundleExecutable. I also fixed the warnings:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%lu%#%lu%#", (unsigned long)appName.length, appName, (unsigned long)className.length, className];
return NSClassFromString(classStringName);
}
Isn't the solution as simple as this?
// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)
// className = "MyApp.MyClass"
Also in Swift 2.0 (possibly before?) You can access the type directly with the dynamicType property
i.e.
class User {
required init() { // class must have an explicit required init()
}
var name: String = ""
}
let aUser = User()
aUser.name = "Tom"
print(aUser)
let bUser = aUser.dynamicType.init()
print(bUser)
Output
aUser: User = {
name = "Tom"
}
bUser: User = {
name = ""
}
Works for my use case
Try this.
let className: String = String(ControllerName.classForCoder())
print(className)
I have implemented like this,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
ImplementationClass.init()
}
Swift 5, easy to use, thanks to #Ondrej Rafaj's
Source code:
extension String {
fileprivate
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
var controller: UIViewController?{
guard let viewController: UIViewController.Type = convertToClass() else {
return nil
}
return viewController.init()
}
}
class StringClassConverter<T> {
fileprivate
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String, let aClass = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
Call like this:
guard let ctrl = "ViewCtrl".controller else {
return
}
// ctrl do sth
A page jump example shown here, the hope can help you!
let vc:UIViewController = (NSClassFromString("SwiftAutoCellHeight."+type) as! UIViewController.Type).init()
self.navigationController?.pushViewController(vc, animated: true)
// Click the Table response
tableView.deselectRow(at: indexPath, animated: true)
let sectionModel = models[(indexPath as NSIndexPath).section]
var className = sectionModel.rowsTargetControlerNames[(indexPath as NSIndexPath).row]
className = "GTMRefreshDemo.\(className)"
if let cls = NSClassFromString(className) as? UIViewController.Type {
let dvc = cls.init()
self.navigationController?.pushViewController(dvc, animated: true)
}
Swift3+
extension String {
var `class`: AnyClass? {
guard
let dict = Bundle.main.infoDictionary,
var appName = dict["CFBundleName"] as? String
else { return nil }
appName.replacingOccurrences(of: " ", with: "_")
let className = appName + "." + self
return NSClassFromString(className)
}
}
Here is a good example:
class EPRocks {
#require init() { }
}
class EPAwesome : EPRocks {
func awesome() -> String { return "Yes"; }
}
var epawesome = EPAwesome.self();
print(epawesome.awesome);