I'm trying to define a class Building which inherits MGLPolygon.
MGLPolygon is defined as:
public class MGLPolygon : MGLMultiPoint, MGLOverlay {
public var interiorPolygons: [MGLPolygon]? { get }
public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt)
public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt, interiorPolygons: [MGLPolygon]?)
}
MGLPolygon's designated initializer is hidden in the swift version of SDK. The following would fail:
class Building: MGLPolygon {
let name: String
init(name: String, coordinates: [CLLocationCoordinate2D]){
self.name = name
super.init(coordinates: &coordinates, count: UInt(coordinates.count))
// Must call a designated initializer of the superclass 'MGLPolygon'
}
}
I checked the original SDK code in Objective-C:
#implementation MGLPolygon
#dynamic overlayBounds;
+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
}
+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
}
- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
if (self = [super initWithCoordinates:coords count:count]) {
if (interiorPolygons.count) {
_interiorPolygons = interiorPolygons;
}
}
return self;
}
- (mbgl::LinearRing<double>)ring {
NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;
mbgl::LinearRing<double> result;
result.reserve(self.pointCount);
for (NSUInteger i = 0; i < count; i++) {
result.push_back(mbgl::Point<double>(coordinates[i].longitude, coordinates[i].latitude));
}
return result;
}
- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
mbgl::Polygon<double> geometry;
geometry.push_back(self.ring);
for (MGLPolygon *polygon in self.interiorPolygons) {
geometry.push_back(polygon.ring);
}
mbgl::FillAnnotation annotation { geometry };
annotation.opacity = [delegate alphaForShapeAnnotation:self];
annotation.outlineColor = [delegate strokeColorForShapeAnnotation:self];
annotation.color = [delegate fillColorForPolygonAnnotation:self];
return annotation;
}
#end
However, unfortunately I'm not good with Objective-C and I don't understand the code.
What am I asking?
What is the designated initializer of MGLPolygon in Swift? What does it take as parameters?
Extra question
Why is the designated initializer hidden?
I think you will need to create a class method in your subclass (e.g. +buildingWithCoordinates:count:) which calls super's implementation in order to handle this.
My solution according to #incanus's advice:
class Building: MGLPolygon {
var name: String?
afterInit(name: String){
self.name = name
}
}
func initBuilding(name: String, coordinates: [CLLocationCoordinate2D]) -> Building {
let building = Building(coordinates: &coordinates, count: UInt(coordinates.count))
building.afterInit(name)
return building
}
It's not elegant but it works.
Related
I want to change Create function
for avoiding the Global variable usage.
I want to pass my values (e.g scores) while Calling
and creating other scenes. One method i found is
the use of Global variable in create Function
but i want to use those values without using global
variables. Please reply me fast , i'm new in cocos2d-x.
and also tell me if there is any other method.
i personally use these two approaches ... so choose which fits you the most!
take for example this ScoreScene class and how its created.
1)- approach one
class ScoreScene : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene(bool won, int score) {
auto scene = Scene::create();
auto layer = ScoreScene::create(won, score);
scene->addChild(layer);
return scene;
}
static ScoreScene* create(bool won, int score) {
ScoreScene *layer = new(std::nothrow) ScoreScene;
if (layer)
{
layer->isWinner = won;
layer->m_score = score;
if (layer->init())
{
layer->autorelease();
return layer;
}
}
CC_SAFE_DELETE(layer);
return nullptr;
}
bool init() override {
if(!layer->init()) return;
// initialization code here
return true;
}
private:
// your properties
int m_score;
bool isWinner;
};
1)- approach two (personally preferred)
make just one method which is createScene instead of two.
class ScoreScene : public cocos2d::Layer
{
public:
cocos2d::Scene* ScoreScene::createScene(bool won, int score)
{
ScoreScene *layer = new(std::nothrow) ScoreScene;
if (layer)
{
layer->isWinner = won;
layer->m_score = score;
if (layer->init())
{
auto scene = Scene::create();
layer->autorelease();
scene->addChild(layer);
return scene;
}
}
CC_SAFE_DELETE(layer);
return nullptr;
}
bool init() override {
if(!layer->init()) return;
// initialization code here
return true;
}
private:
// your properties
int m_score;
bool isWinner;
};
I used the same Approach But thanks for answering..
cocos2d::Scene* GameOverScene::createScene(unsigned int tempscoree)
{
GameOverScene *pRet = new(std::nothrow) GameOverScene();
if (pRet && pRet->init(tempscoree))
{
auto scene = Scene::create();
pRet->autorelease();
scene->addChild(pRet);
return scene;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
bool GameOverScene::init(unsigned int tempScore)
{
if ( !Layer::init() )
{
return false;
}
//Now i can use tempScore any where in to this init() method.
}
I want to check if the received data from a MySQL-database can be converted to Double and if that's possible I want to append it to an array.
From another question i got the following code:
extension String {
struct NumberFormatter {
static let instance = NSNumberFormatter()
}
var doubleValue:Double? {
return NumberFormatter.instance.numberFromString(self)?.doubleValue
}
var integerValue:Int? {
return NumberFormatter.instance.numberFromString(self)?.integerValue
}
}
Here's my concerned Code (which is in a for-loop):
if let value = datas[i].puls.doubleValue {
pulsWerte.append(value)
print(datas[i].puls)
}else {
badIndex.append(i)
continue
}
In case an entry of my database is NULL, my program should proceed in the else-branch. But if I test such a case I end up with a runtime-error and the following two messages:
-[NSNull length]: unrecognized selector sent to instance 0x1451238
&
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x1451238'
Does anyone know my mistake?
UPDATE
This is how my datas-array gets created:
For downloading and allocating the data I'm using Objectiv-C.
In my Data.h-file I create my Data-class.
#interface Data : NSObject
#property (nonatomic, strong) NSString *sozialversicherungsnummer;
#property (nonatomic, strong) NSString *messzeitpunkt;
#property (nonatomic, strong) NSString *puls;
#property (nonatomic, strong) NSString *sauerstoffgehalt;
#end
And in my HomeModel.m I create a NSMutableArray named "_data". After my download is finished this array gets filled as follows:
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
// Create a new data object and set its props to JsonElement properties
Data *newData = [[Data alloc] init];
newData.sozialversicherungsnummer = jsonElement[#"Sozialversicherungsnummer"];
newData.messzeitpunkt = jsonElement[#"Messzeitpunkt"];
newData.puls = jsonElement[#"Puls"];
newData.sauerstoffgehalt = jsonElement[#"Sauerstoffgehalt"];
// Add this question to the locations array
[_data addObject:newData];
}
When this step is completed I get this "_data"-array under the name of a parameter called "items" by a function called "itemsDownloaded".
And now I call this function in my Swift-file and here I'm able to typecast the downloaded "items" as "Data" (my self created class).
func itemsDownloaded(items: [AnyObject]!) {
let datas = items as! [Data]
.
.
.
First of all you can define the following protocol.
DoubleConvertible
protocol DoubleConvertible {
func toDouble() -> Double?
}
Making String, Int and Double conform DoubleConvertible
Next, for each type that you want to be convertible to Double you need to make that type conform to DoubleConvertible.
Let's do it for String
extension String: DoubleConvertible {
func toDouble() -> Double? {
return Double(self)
}
}
and Int
extension Int: DoubleConvertible {
func toDouble() -> Double? {
return Double(self)
}
}
and ... well Double
extension Double: DoubleConvertible {
func toDouble() -> Double? {
return self
}
}
From [Any] to [Double]
Now given a list of values of several different types
let list: [Any?] = ["123", nil, "Hello", 456, true, 789.1]
We can easily convert it to a list of Double.
let listOfDouble = list
.flatMap { $0 as? DoubleConvertible }
.flatMap { $0.toDouble() }
listOfDouble // [123.0, 456.0, 789.1]
Your implementation of doubleValue:Double? assumes that self is a String.
var doubleValue:Double? {
return NumberFormatter.instance.numberFromString(self)?.doubleValue
}
The datas[i].puls is created by an Objective-C JSON Parser which produces in the case of NULL an instance of NSNull but you expect to set nil.
The easiest way is to check jsonElement[#"Puls"] for NSNull and then set newData.puls to NULL:
id puls = jsonElement[#"Puls"];
if puls == [NSNull null] {
newData.puls = null;
} else {
newData.puls = puls;
}
I recently discovered the way to create NS_OPTIONS equivalent in Swift, however I cannot use them from Objective-C code in a Objective-C/Swift project.
Here is a sample project I did:
ObjcObject.h & ObjcObject.m
typedef NS_OPTIONS(NSUInteger, MyObjcOption)
{
MyOptionNone = 0,
MyObjcOptionCase01 = 1 << 0,
MyObjcOptionCase02 = 1 << 1,
MyObjcOptionCaseAll = MyObjcOptionCase01 | MyObjcOptionCase02
};
#interface ObjcObject : NSObject
+ (void)printMyObjcOption:(MyObjcOption)option;
#end
#implementation ObjcObject
+ (void)printMyObjcOption:(MyObjcOption)option
{
if (option == 0)
NSLog(#"None");
if (option & MyObjcOptionCase01)
NSLog(#"MyObjcOptionCase01");
if (option & MyObjcOptionCase02)
NSLog(#"MyObjcOptionCase02");
}
#end
SwiftObject.swift
struct MySwiftOption: RawOptionSetType, BooleanType {
private var value: UInt = 0
var rawValue: UInt { return self.value }
init(_ value: UInt) { self.value = value }
init(rawValue value: UInt) { self.value = value }
init(nilLiteral: ()) { self.value = 0 }
var boolValue: Bool { return value != 0 }
static var allZeros: MySwiftOption { return self(0) }
static var None: MySwiftOption { return self(0) }
static var All: MySwiftOption { return self.Case01 | self.Case02 }
static var Case01: MySwiftOption { return self(1 << 0) }
static var Case02: MySwiftOption { return self(1 << 1) }
}
public class SwiftObject: NSObject {
class func printMySwiftOption(option: MySwiftOption) {
if option.rawValue == 0 {
println("None")
}
if option & MySwiftOption.Case01 {
println(".Case01")
}
if option & MySwiftOption.Case02 {
println(".Case02")
}
}
class func sayHello() {
println("Hello")
}
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MyObjcOption objcOption = MyObjcOptionCase02;
[ObjcObject printMyObjcOption:objcOption];
[SwiftObject sayHello];
// MySwiftOption swiftOption = MySwiftOptionCase02; // Error: Use of undeclared identifier 'MySwiftOption'
// [SwiftObject printMySwiftOption:swiftOption];
return YES;
}
In the Objective-C code, I always get an error Use of undeclared identifier 'MySwiftOption'.
Is it a known issue? Is there a workaround?
You can't expose structs from Swift to ObjectiveC. Actually, have a look at the -Swift.h file to see what you can access from Objective. You won't find MySwiftOptions in there.
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics Tuples,
Enumerations defined in Swift,
Structures defined in Swift,
Top-level functions defined in Swift,
Global variables defined in Swift,
Typealiases defined in Swift,
Swift-style variadics Nested types,
Curried functions
I, for one, am living with these limitations on a daily bases.
Using Swift with Cocoa and Objective-C at Apple.
Well, this possibly a duplicated question. I've found some questions like this one:
Is there a way to set associated objects in Swift?
However, I want to add an Int property into swift's extension and these answers in the link above doesn't work.
Here's my code:
import ObjectiveC
var xoAssociationKey: UInt8 = 0
extension NSData {
var position: Int {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as Int
}
set {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
override convenience init() {
self.init()
position = 0
}
}
And I get fatal error: unexpectedly found nil while unwrapping an Optional value everytime I access position
FYI, I did find a solution for this error in Objective C and I'm looking for a swift solution. Here's my code in objective C if you interested:
static char PROPERTY_KEY;
#implementation NSData (Extension)
#dynamic position;
- (NSInteger)position {
return [objc_getAssociatedObject(self, &PROPERTY_KEY) integerValue];
}
- (void)setPosition:(NSInteger)position {
// Must convert to an object for this trick to work
objc_setAssociatedObject(self, &PROPERTY_KEY, #(position), OBJC_ASSOCIATION_COPY);
}
- (instancetype)init {
self = [super init];
if (self) {
self.position = 0;
}
return self;
}
NSData is part of a class cluster, so your custom init method is not necessarily called,
e.g.
let d = NSMutableData()
does not use your init method. The next problem is that your init method calls
itself recursively, therefore
let d = NSData()
crashes with a stack overflow. Note also that the Objective-C code relies on
undefined behaviour, because it replaces a method in a class extension.
So better remove your custom initialization, and change the getter to
return a default value if the associated object has not been set.
This can easily be achieved with an optional cast (as? Int) and the
nil-coalescing operator (??):
extension NSData {
var position: Int {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? Int ?? 0
}
set {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
}
I am having a problem with generics tree structure in Objective C. I am mainly Java programmer, so I guess I need help from you guys here. On Android I am using this code without a problem:
public class Tree<T> {
private T head;
private ArrayList<Tree<T>> leafs = new ArrayList<Tree<T>>();
private Tree<T> parent = null;
private HashMap<T, Tree<T>> locate = new HashMap<T, Tree<T>>();
public Tree(T head) {
this.head = head;
locate.put(head, this);
}
public void addLeaf(T root, T leaf) {
if (locate.containsKey(root)) {
locate.get(root).addLeaf(leaf);
} else {
addLeaf(root).addLeaf(leaf);
}
}
public Tree<T> addLeaf(T leaf) {
Tree<T> t = new Tree<T>(leaf);
leafs.add(t);
t.parent = this;
t.locate = this.locate;
locate.put(leaf, t);
return t;
}
public Tree<T> setAsParent(T parentRoot) {
Tree<T> t = new Tree<T>(parentRoot);
t.leafs.add(this);
this.parent = t;
t.locate = this.locate;
t.locate.put(head, this);
t.locate.put(parentRoot, t);
return t;
}
public T getHead() {
return head;
}
public Tree<T> getTree(T element) {
return locate.get(element);
}
public Tree<T> getParent() {
return parent;
}
public Collection<T> getSuccessors(T root) {
Collection<T> successors = new ArrayList<T>();
Tree<T> tree = getTree(root);
if (null != tree) {
for (Tree<T> leaf : tree.leafs) {
successors.add(leaf.head);
}
}
return successors;
}
public Collection<Tree<T>> getSubTrees() {
return leafs;
}
public static <T> Collection<T> getSuccessors(T of, Collection<Tree<T>> in) {
for (Tree<T> tree : in) {
if (tree.locate.containsKey(of)) {
return tree.getSuccessors(of);
}
}
return new ArrayList<T>();
}
}
But when I tried to port it as it is to Objective-C it simply doesn't work. Here is the code:
#implementation Tree {
Node* head;
NSMutableArray* leafs;
Tree* parent;
NSMutableDictionary* locate;
}
#synthesize head;
-(id) initWithHead:(Node *) Head {
self = [super init];
if (self) {
head = Head;
leafs = [[NSMutableArray alloc]init];
locate = [[NSMutableDictionary alloc] init];
[locate setObject:head forKey:(id)self];
}
return self;
}
-(void) addLeafWithRoot:(Node *)Root leaf:(Node *)Leaf {
if([locate objectForKey:Root] != nil) {
[[locate objectForKey:Root] addLeaf:Leaf];
}else{
[[self addLeaf:Root] addLeaf:Leaf];
}
}
-(Tree *) addLeaf:(Node*) Leaf {
Tree * t = [[Tree alloc] initWithHead:Leaf];
[leafs addObject:t];
[t setParent:t];
[self setLocate:[t getLocate]];
[locate setObject:Leaf forKey:(id)t];
return t;
}
-(Tree*) getTree:(Node *) Node {
return [locate objectForKey:Node];
}
-(NSMutableArray *) getSuccesor:(Node *) root {
NSMutableArray *successors = [[NSMutableArray alloc] init];
Tree* tree = [self getTree:root];
if(tree != nil) {
for (Tree* leaf in [tree getLeafs]) {
[successors addObject:[leaf getHead]];
}
}
return successors;
}
-(void) setParent:(Tree *) Tree {
parent = Tree;
}
-(void) setLocate:(NSMutableDictionary *) Locate {
locate = Locate;
}
-(NSMutableDictionary *) getLocate {
return locate;
}
-(NSMutableArray *) getLeafs {
return leafs;
}
-(Node *) getHead {
return head;
}
-(id)copyWithZone:(NSZone *)zone
{
Tree *another = [[[self class] allocWithZone:zone] init];
another.head = self.head;
return another;
}
#end
I guess that is due to, that on Android I am using Tree class Node type and that's simply I cannot use in Objective-C. Can I modify this solution, to works on iOS, or do I have to come with something totally different? Thanks in advance.
By the way, Node class is basic class with getters and setters to hold some text and int information.
There are memory management issues with your implementation. (Head is not retained in init, and you haven't implemented dealloc.) It also has few of the benefits of a tree because it isn't smart about where to put new nodes, and has no facility for searching.
Instead of writing your own basic data type, you should use either CFTree or a Cocoa wrapper for CFTree. See this answer for more.