Hello below is code for the CCHMapClusterController. Im trying to rewrite this function to Swift, but I keep getting this error:
"Variable annotation used before being Initialised"
on the line:
clusterAnnotation = annotation
I have no clue in Objective C, could someone check if I did the last few lines properly?
Objective C:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation: (id<MKAnnotation>)annotation
{
MKAnnotationView *annotationView;
if ([annotation isKindOfClass:CCHMapClusterAnnotation.class]) {
static NSString *identifier = #"clusterAnnotation";
ClusterAnnotationView *clusterAnnotationView = (ClusterAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (clusterAnnotationView) {
clusterAnnotationView.annotation = annotation;
} else {
clusterAnnotationView = [[ClusterAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
clusterAnnotationView.canShowCallout = YES;
}
CCHMapClusterAnnotation *clusterAnnotation = (CCHMapClusterAnnotation *)annotation;
clusterAnnotationView.count = clusterAnnotation.annotations.count;
clusterAnnotationView.blue = (clusterAnnotation.mapClusterController == self.mapClusterControllerBlue);
clusterAnnotationView.uniqueLocation = clusterAnnotation.isUniqueLocation;
annotationView = clusterAnnotationView;
}
return annotationView;
}
Swift Code:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is MKUserLocation {
// return nil so map view draws "blue dot" for standard user location
return nil
}
var annotationView : MKAnnotationView?
if annotation is CCHMapClusterAnnotation {
// let a : clusterAnnotationView = annotation as clusterAnnotationView
let identifier: NSString = "clusterAnnotation"
var clusterAnnotationView = (mapView.dequeueReusableAnnotationViewWithIdentifier(identifier as String)) as? ClusterAnnotationView!
if (clusterAnnotationView != nil) {
clusterAnnotationView!.annotation = annotation
} else {
clusterAnnotationView = ClusterAnnotationView(annotation: annotation, reuseIdentifier: "clusterAnnotation")
clusterAnnotationView!.canShowCallout = true
}
var clusterAnnotation : CCHMapClusterAnnotation;
var annotation : CCHMapClusterAnnotation
clusterAnnotation = annotation
clusterAnnotationView.count = clusterAnnotation.annotations.count
clusterAnnotationView!.blue = (clusterAnnotation.mapClusterController == self.mapClusterControllerBlue);
clusterAnnotationView!.uniqueLocation = clusterAnnotation.isUniqueLocation();
annotationView = clusterAnnotationView
}
With these lines:
var clusterAnnotation : CCHMapClusterAnnotation;
var annotation : CCHMapClusterAnnotation
clusterAnnotation = annotation
two variables are declared but not initialized so you get that warning.
In addition, it's also declaring a new local variable annotation that has the same name as the existing annotation parameter. You may be getting a warning or error for that as well.
If you're trying to convert this Objective-C line:
CCHMapClusterAnnotation *clusterAnnotation =
(CCHMapClusterAnnotation *)annotation;
which is casting the annotation parameter as a CCHMapClusterAnnotation, then a possible Swift version is:
let clusterAnnotation = annotation as? CCHMapClusterAnnotation
However, you could also combine the annotation class check you have above (if annotation is ...) with this casted assignment:
if let clusterAnnotation = annotation as? CCHMapClusterAnnotation {
...
}
Related
I am trying to use obj c library in swift but I am having issue with the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I think that I am missing something in var annotationView:MKPinAnnotationView! declaration is wrong but can't find a way around.
Code is:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
var annotationView:MKPinAnnotationView!
if(annotation is KPAnnotation){
var kingpinAnnotation = annotation as KPAnnotation
if (kingpinAnnotation.isCluster()){
annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("cluster") as MKPinAnnotationView // THIS IS THE ERROR LINE
if (annotationView == nil) {
annotationView = MKPinAnnotationView(annotation: kingpinAnnotation, reuseIdentifier: "cluster")
}
annotationView.pinColor = .Purple;
} else {
annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("pin") as MKPinAnnotationView
if (annotationView == nil) {
annotationView = MKPinAnnotationView(annotation: kingpinAnnotation, reuseIdentifier: "pin")
}
annotationView.pinColor = .Red;
}
annotationView.canShowCallout = true;
return annotationView;
}
The "forced cast" as MKPinAnnotationView (to a non-optional type) aborts with a runtime exception
if mapView.dequeueReusableAnnotationViewWithIdentifier() returns nil.
You can use an optional cast as? instead:
annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("cluster")
as? MKPinAnnotationView
which assigns nil to annotationView in that case.
If it is guaranteed that all elements in the reuse queue have the type
MKPinAnnotationView then a cast to an implicitly unwrapped optional
would work as well:
annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("cluster")
as MKPinAnnotationView!
but the first version is the safer one.
I searched on google and on SO but didn't find any useful help for this issue.
I'm trying to translate this code from objective-c to swift:
- (void)metaTitleUpdated:(NSString *)title {
NSLog(#"delegate title updated to %#", title);
NSArray *chunks = [title componentsSeparatedByString:#";"];
if ([chunks count]) {
NSArray *streamTitle = [[chunks objectAtIndex:0] componentsSeparatedByString:#"="];
if ([streamTitle count] > 1) {
titleLabel.text = [streamTitle objectAtIndex:1];
}
}
}
so far i have translated it to this:
func metaTitleUpdated(input: String) {
println("delegate title updated to \(title)")
let chunks: NSArray = title!.componentsSeparatedByString(";")
if (chunks.count) {
let streamTitle = chunks .objectAtIndex(0) .componentsSeparatedByString(";")
if (streamTitle.count > 1) {
titleLabel.text = streamTitle.objectAtIndex(1)
}
}
}
but i always get the error "Type 'Int' does not conform to protocol 'BooleanType'" in the line: if (chunks.count) {
What does cause this error? Is the rest of the code in swift correct or are there any other errors?
chunks.count has the type Int, but the if statement requires a boolean expression.
This is different from (Objective-)C, where the controlling expression of an if statement can have any scalar type and is compared with zero.
So the corresponding Swift code for
if ([chunks count]) { ... }
is
if chunks.count != 0 { ... }
I solved the answer by myself.
func metaTitleUpdated(title: String) {
var StreamTitle = split(title) {$0 == "="}
var derRichtigeTitel: String = StreamTitle[1]
titleLabel.text = derRichtigeTitel
println("delegate title updated to \(derRichtigeTitel)")
}
i have this code in ObjC
and i want or trying to convert it to swift
typedef NS_ENUM(NSInteger, BB3Photo) {
kirkenType = 10 ,
festenType = 20 ,
praestType = 30
};
#property (nonatomic, assign) BB3Photo selectedPhotoType;
- (IBAction)changeImage:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *button = sender;
_selectedPhotoType = button.tag;
}
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:#"Vælg Billed"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:#"Vælg fra Biblioteket", #"Vælg Kamera", nil];
sheet.actionSheetStyle = UIActionSheetStyleDefault;
[sheet showInView:[self.view window]];
}
here's what i have made from it
enum BBPhoto1: Int {
case kommunen = 10
case sagsbehandler = 20
case festen = 30
}
var selectedPhoto = BBPhoto1.self
#IBAction func changeImage(sender: AnyObject){
if sender .isKindOfClass(UIButton){
let button: UIButton = sender as UIButton
selectedPHoto = (sender as UIButton).tag as BBPhoto1 // doesent work "cannot assign that result as expression"
selectedPHoto = button.tag // doesnt work either "cannot assign that result as expression"
self.selectedPhoto = BBPhoto1.fromRaw((sender as UIButton).tag) // nope "cannot convert the expressions type () to type UIButton"
}
}
i want to be able to have a switch statement with button tags to the same funktion but different in the code
You want to use the tag as the raw value of your BBPhoto1 enum. You can do this with conditional unwrapping:
#IBAction func changeImage(sender: AnyObject){
if let button = sender as UIButton {
if let photoType = BBPhoto1.fromRaw(button.tag) {
self.selectedPhoto = photoType
}
}
}
There's also a problem with the declaration of your selectedPhoto property. It should be:
var selectedPhoto: BBPhoto1?
The way you have it now it doesn't hold a BBPhoto1 value, but instead the type of BBPhoto1 itself.
Note that the fromRaw syntax has changed to an initializer in Xcode 6.1:
#IBAction func changeImage(sender: AnyObject){
if let button = sender as UIButton {
if let photoType = BBPhoto1(rawValue: button.tag) {
self.selectedPhoto = photoType
}
}
}
How about:
#IBAction func changeImage(sender: AnyObject){
if sender .isKindOfClass(UIButton){
let button: UIButton = sender as UIButton
selectedPHoto = BBPhoto1.fromRaw(button.tag)
}
}
or (shorter):
#IBAction func changeImage(sender: UIButton){
selectedPHoto = BBPhoto1.fromRaw(sender.tag)
}
Ok, Not the same code; as in validating a text field, but the same principle in Swift 4, iOS 11, xCode 9.2.
enum SFT: Int {
case StudentNameFieldTag
case StudentIDFieldTag
case StudentClassFieldTag
case ChallengeCodeFieldTag
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.tag == SFT.StudentClassFieldTag.rawValue {
let whitespaceSet = CharacterSet.whitespaces
if let _ = string.rangeOfCharacter(from: whitespaceSet) {
return false
} else {
return true
}
}
return true
}
The fromRaw() method returns an optional, so you have to declare your property as optional:
var selectedPHoto: BBPhoto1?
and use this code:
self.selectedPhoto = BBPhoto1.fromRaw((sender as UIButton).tag)
Alternatively, you can unwrap the fromRaw return value:
self.selectedPHoto = BBPhoto1.fromRaw((sender as UIButton).tag)!
Note however that in this case, if the raw value doesn't map to an enum, a runtime exception will be raised.
I thought I'd be cautious and try out Swift on an existing Obj-C project by converting one class. And a small, simple one at that. Oh dear.
Transliterating the original obj-c into Swift should be straightforward and so it seemed. Unfortunately, whilst the encoder to persistent store seems to work, it crashes with an EXC_BREAKPOINT error at the first line of the init coder.
IF (and the caps are intentional) NSCoding/Swift gives the same persistent content as NSCoding/ObjC, then my all obj-c version should be able to read what is encoded by Swift and vice versa. This proves not to be the case - and my perfectly-functioning obj-c version crashes out when it tries to read the persistent store from the Swift version. Surely, if NSCoding is implemented correctly, it ought to generate something in one that is readable in t'other? Otherwise, there ought to be separate NSCodingSwift and NSCodingObjC protocols?
So, to summarise, I can read/write in obj-c. I can't write/obj-c and read/swift and I can write/swift read/obj-c and I can't read/write in swift.
Here are the two versions:
let keyBeaconItemNameKey = "name"
let keyBeaconItemUUIDKey = "uuid"
let keyBeaconItemMajorValueKey = "major"
let keyBeaconItemMinorValueKey = "minor"
import UIKit
import CoreLocation
class SMBeaconItem : NSObject, NSCoding
{
var name : String!
var uuid : NSUUID!
var major : NSNumber!
var minor : NSNumber!
init(newName : String, newUUID : NSUUID, newMajor : NSNumber, newMinor : NSNumber )
{
name = newName
uuid = newUUID
major = newMajor
minor = newMinor
}
init( coder decoder : NSCoder!)
{
name = decoder.decodeObjectForKey(keyBeaconItemNameKey) as String
uuid = decoder.decodeObjectForKey(keyBeaconItemUUIDKey) as NSUUID
major = decoder.decodeObjectForKey(keyBeaconItemMajorValueKey) as NSNumber
minor = decoder.decodeObjectForKey(keyBeaconItemMinorValueKey) as NSNumber
}
func encodeWithCoder( encoder: NSCoder!)
{
encoder.encodeObject(name, forKey:keyBeaconItemNameKey)
encoder.encodeObject(uuid, forKey:keyBeaconItemUUIDKey)
encoder.encodeObject(major, forKey:keyBeaconItemMajorValueKey)
encoder.encodeObject(minor, forKey:keyBeaconItemMinorValueKey)
}
}
And the working original:
#implementation SMBeaconItem
- (instancetype)initWithName:(NSString *)name uuid:(NSUUID *)uuid major:(CLBeaconMajorValue)major minor:(CLBeaconMinorValue)minor
{
self = [super init];
if (!self)
{
return nil;
}
_name = name;
_uuid = uuid;
_majorValue = major;
_minorValue = minor;
return self;
}
#pragma mark - Persistence
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (!self)
{
return nil;
}
_name = [aDecoder decodeObjectForKey:keyBeaconItemNameKey];
_uuid = [aDecoder decodeObjectForKey:keyBeaconItemUUIDKey];
_majorValue = [[aDecoder decodeObjectForKey:keyBeaconItemMajorValueKey] unsignedIntegerValue];
_minorValue = [[aDecoder decodeObjectForKey:keyBeaconItemMinorValueKey] unsignedIntegerValue];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:keyBeaconItemNameKey];
[aCoder encodeObject:self.uuid forKey:keyBeaconItemUUIDKey];
[aCoder encodeObject:[NSNumber numberWithUnsignedInteger:self.majorValue] forKey:keyBeaconItemMajorValueKey];
[aCoder encodeObject:[NSNumber numberWithUnsignedInteger:self.minorValue] forKey:keyBeaconItemMinorValueKey];
}
#end
Thanks for any help you can give.
The only thing that stands out to me is that you're using String instead of NSString as the name's type. In the betas Apple has been putting out, String (strangely) is not a one-for-one replacement of NSString. Namely, some methods are missing and require calling .bridgeToObjectiveC() to get the NSString version. Using that type instead will probably conform to what NSCoder is expecting, though that difference should not be so.
I didn't actually test this claim since I'm not on my dev machine. But that's my gut instinct. Try it and see what happens! If nothing changes, try switching the order of how you're setting the vars and see if it's a problem related to the name field or simply the first line of the init function.
Below code working is swift for save retrieve NSCoding value in UserDefaults
import UIKit
import Foundation
class ViewController: UIViewController {
var employees: Employees?
let static_key = "nscdeing_data_saved"
override func viewDidLoad() {
super.viewDidLoad()
var request = URLRequest(url: URL(string: "http://dummy.restapiexample.com/api/v1/employees")!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 60)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let status = (response as? HTTPURLResponse)?.statusCode, status == 200, let data = data{
do {
guard let dic = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any] else { return }
self.employees = Employees.init(fromDictionary: dic)
let archiveData = try NSKeyedArchiver.archivedData(withRootObject: self.employees as Any, requiringSecureCoding: true)
UserDefaults.standard.set(archiveData, forKey: self.static_key)
} catch let error {
fatalError(error.localizedDescription)
}
}
}.resume()
}
#IBAction func printAction(_ sender: Any) {
if let data = UserDefaults.standard.data(forKey: static_key){
do {
let value = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)
print(value as Any)
} catch let error {
fatalError(error.localizedDescription)
}
}
}
}
class Employees : NSObject, NSCoding, NSSecureCoding{
static var supportsSecureCoding: Bool{
return true
}
var data : [Datum]!
var status : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
status = dictionary["status"] as? String
data = [Datum]()
if let dataArray = dictionary["data"] as? [[String:Any]]{
for dic in dataArray{
let value = Datum(fromDictionary: dic)
data.append(value)
}
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]{
var dictionary = [String:Any]()
if status != nil{
dictionary["status"] = status
}
if data != nil{
var dictionaryElements = [[String:Any]]()
for dataElement in data {
dictionaryElements.append(dataElement.toDictionary())
}
dictionary["data"] = dictionaryElements
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder){
data = aDecoder.decodeObject(forKey: "data") as? [Datum]
status = aDecoder.decodeObject(forKey: "status") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder){
if data != nil{
aCoder.encode(data, forKey: "data")
}
if status != nil{
aCoder.encode(status, forKey: "status")
}
}
}
class Datum : NSObject, NSCoding, NSSecureCoding{
static var supportsSecureCoding: Bool{
return true
}
var employeeAge : String!
var employeeName : String!
var employeeSalary : String!
var id : String!
var profileImage : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
employeeAge = dictionary["employee_age"] as? String
employeeName = dictionary["employee_name"] as? String
employeeSalary = dictionary["employee_salary"] as? String
id = dictionary["id"] as? String
profileImage = dictionary["profile_image"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]{
var dictionary = [String:Any]()
if employeeAge != nil{
dictionary["employee_age"] = employeeAge
}
if employeeName != nil{
dictionary["employee_name"] = employeeName
}
if employeeSalary != nil{
dictionary["employee_salary"] = employeeSalary
}
if id != nil{
dictionary["id"] = id
}
if profileImage != nil{
dictionary["profile_image"] = profileImage
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder){
employeeAge = aDecoder.decodeObject(forKey: "employee_age") as? String
employeeName = aDecoder.decodeObject(forKey: "employee_name") as? String
employeeSalary = aDecoder.decodeObject(forKey: "employee_salary") as? String
id = aDecoder.decodeObject(forKey: "id") as? String
profileImage = aDecoder.decodeObject(forKey: "profile_image") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder){
if employeeAge != nil{
aCoder.encode(employeeAge, forKey: "employee_age")
}
if employeeName != nil{
aCoder.encode(employeeName, forKey: "employee_name")
}
if employeeSalary != nil{
aCoder.encode(employeeSalary, forKey: "employee_salary")
}
if id != nil{
aCoder.encode(id, forKey: "id")
}
if profileImage != nil{
aCoder.encode(profileImage, forKey: "profile_image")
}
}
}
I want to write an init method in Swift. Here I initialize an NSObject class in Objective-C:
-(id)initWithNewsDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
self.title = dictionary[#"title"];
self.shortDescription = dictionary[#"description"];
self.newsDescription = dictionary[#"content:encoded"];
self.link = dictionary[#"link"];
self.pubDate = [self getDate:dictionary[#"pubDate"]];
}
return self;
}
How can I write this method in Swift ?
that could be good bases for your class, I guess:
class MyClass {
// you may need to set the proper types in accordance with your dictionarty's content
var title: String?
var shortDescription: String?
var newsDescription: String?
var link: NSURL?
var pubDate: NSDate?
//
init () {
// uncomment this line if your class has been inherited from any other class
//super.init()
}
//
convenience init(_ dictionary: Dictionary<String, AnyObject>) {
self.init()
title = dictionary["title"] as? NSString
shortDescription = dictionary["shortDescription"] as? NSString
newsDescription = dictionary["newsDescription"] as? NSString
link = dictionary["link"] as? NSURL
pubDate = self.getDate(dictionary["pubDate"])
}
//
func getDate(object: AnyObject?) -> NSDate? {
// parse the object as a date here and replace the next line for your wish...
return object as? NSDate
}
}
advanced-mode
I would like to avoid to copy-pand-paste the keys in a project, so I'd put the possible keys into e.g. an enum like this:
enum MyKeys : Int {
case KeyTitle, KeyShortDescription, KeyNewsDescription, KeyLink, KeyPubDate
func toKey() -> String! {
switch self {
case .KeyLink:
return "title"
case .KeyNewsDescription:
return "newsDescription"
case .KeyPubDate:
return "pubDate"
case .KeyShortDescription:
return "shortDescription"
case .KeyTitle:
return "title"
default:
return ""
}
}
}
and you can improve your convenience init(...) method like e.g. this, and in the future you can avoid any possible mistyping of the keys in your code:
convenience init(_ dictionary: Dictionary<String, AnyObject>) {
self.init()
title = dictionary[MyKeys.KeyTitle.toKey()] as? NSString
shortDescription = dictionary[MyKeys.KeyShortDescription.toKey()] as? NSString
newsDescription = dictionary[MyKeys.KeyNewsDescription.toKey()] as? NSString
link = dictionary[MyKeys.KeyLink.toKey()] as? NSURL
pubDate = self.getDate(dictionary[MyKeys.KeyPubDate.toKey()])
}
NOTE: that is just a raw idea of how you could do it, it is not necessary to use conveniece initializer at all, but it looked obvious choice regarding I don't know anything about your final class – you have shared one method only.
class myClass {
var text: String
var response: String?
init(text: String) {
self.text = text
}
}
See Swift: Initialization
Do not need for call this method from other class it will get called automatically
override init()
{
super.init()
// synthesize.delegate = self
// println("my array elements are \(readingData)")
}
try:
initWithDictionary(dictionary : NSDictionary) {
init()
self.title = "... etc"
}
Source:
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html