how to make switch case of enum case (swift) - objective-c

I got this code from ObjC. I want to convert it to Swift, however, I am having difficulties doing so...
ObjC code :
navgivet.h
typedef NS_ENUM(NSInteger, BB3Photo) {
kirkenType = 10 ,
festenType = 20 ,
praestType = 30
};
#property (nonatomic, assign) BB3Photo selectedPhotoType;
navgivet.m
- (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]];
}
switch (_selectedPhotoType) {
case kirkenType: {
}break;
case festenType: {
}break;
case praestType: {
}break;
default:
break;
Here's my swift code in this attempt
enum BBPhoto1: Int {
case kommunen = 10
case sagsbehandler = 20
case festen = 30
}
#IBAction func changeImage(sender: AnyObject){
if sender .isKindOfClass(UIButton){
let button: UIButton = sender as UIButton
selectedPhoto = BBPhoto1.fromRaw(button.tag)
}
let actionSheet = UIActionSheet(title: "Billeder", delegate: self, cancelButtonTitle: nil, destructiveButtonTitle: nil, otherButtonTitles: "Vælg fra Biblioteket", "Vælg Kamera")
actionSheet.showInView(self.view)
}
var selectedPhoto: BBPhoto1?
switch (selectedPhoto) {
case kommunen {
}
case sagsbehandler{
}
}
but I get errors : "Use of unresolved identifier kommunen" and same messege but with Sagsbehandler.
How do I make it work ?

There are 3 problems in your code.
The first is that selectedPhoto is declared as optional, so you must unwrap it before using in a switch statement - for example using optional binding.
The second problem is that the syntax you're using is incorrect. In each case you have to specify the full name (including the type), followed by a colon:
case BBPhoto1.kommunen:
// statements
but since the type can be inferred by the variable type used in the switch, you can ignore the enum type, but not the dot:
case .kommunen:
// statements
Last, in swift a switch statements require that all cases are handled either explicitly (3 in your case) or using a default case covering all cases non explicitly handled in the switch.
A working version of your code would look like:
enum BBPhoto1: Int {
case kommunen = 10
case sagsbehandler = 20
case festen = 30
}
var selectedPhoto: BBPhoto1?
if let selectedPhoto = selectedPhoto {
switch (selectedPhoto) {
case .kommunen:
println(selectedPhoto.toRaw())
case .sagsbehandler:
println(selectedPhoto.toRaw())
default:
println("none")
}
}
Note that, differently from other languages, the code in each case doesn't automatically fallthrough to the next case, so the break statement is not required - the only use case for it is when a case has no statement (a case with no statement is an error in swift), and in that case the break just acts as a placeholder and its meaning is 'do nothing'.
Suggested reading: Conditional Statements

Related

Swift - enum with button.tag?

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.

Applying an IF STATEMENT to my word generator

When I try adding an "if" statement to my word generator I get an error stating "Expected Expression". If I take the if statement out, it works fine but what I want to do is have several word generators and depending on the value of my variable "variable" determine which word generator is accessed.
Example: If "variable" is equal to 1 then the first word generator is accessed. If "variable is equal to 2 then the second word generator is accessed
Below is the code from my implementation file.
#import "ViewController2.h"
#import "ViewController.h"
#import "ViewController3.h"
#interface ViewController2 ()
#end
#implementation ViewController2
-(IBAction)random {
if (int variable = 3) {
int text = rand() %3;
switch (text) {
case 0:
introLabel.text = #"Test 1";
break;
case 1:
introLabel.text = #"Test 2";
break;
case 2:
introLabel.text = #"Test 3";
break;
default:
break;
}
}
}
-(IBAction)backButton:(id)sender {
ViewController *viewController2 = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:viewController2 animated:YES];
}
-(IBAction)moreButton:(id)sender {
ViewController3 *viewController2 = [[ViewController3 alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:viewController2 animated:YES];
}
Thanks in advance for any help.
Your question is unclear but I think you are talking about the line:
if (int variable = 3) {
That is invalid Objective-C syntax. Perhaps you want:
if (variable == 3) {
This assumes you have an instance variable named variable (which is a terrible name).
So your random method becomes something like:
-(IBAction)random {
if (variable == 1) {
int text = rand() %3;
switch (text) {
case 0:
introLabel.text = #"Test 1";
break;
case 1:
introLabel.text = #"Test 2";
break;
case 2:
introLabel.text = #"Test 3";
break;
default:
break;
}
} else if (variable == 2) {
// process the 2nd word generator here
} else if (variable == 3) {
// process the 3rd word generator here
}
}
Again, you need to add an instance variable named variable and you set that value somewhere appropriate. Or variable can be another local variable assigned the value of rand() like you do with the text variable.

Objective C: How to Write a Instantiating Custom Init

Very basic question, but I have an error in my code that can only be answered by one assumption: my class isn't being instantiated!
I haven't written much in Objective C in some time, and I was never really good, so please point out even the most painfully obvious.
I am using:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
ObjectSelectionViewController *volume = [[ObjectSelectionViewController alloc] initWithMeasureType:2];
NSLog(#"%#", [length measurementType]);
NSLog(#"%#", [mass measurementType]);
NSLog(#"%#", [volume measurementType]);
The NSLogs return whichever measurement was assigned last, regardless of the separate allocs and inits.
Here is the constructor of the ObjectSelectionViewController class:
#import "ObjectSelectionViewController.h"
#implementation ObjectSelectionViewController
NSString *measurementType;
-(ObjectSelectionViewController*) initWithMeasureType:(int)value
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
-(NSString*) measurementType
{
return measureType;
}
Thanks for the help, it's driving me crazy!
You need to make measureType an instance variable, so that each object of this type that you create has its own copy:
#interface ObjectSelectionViewController : NSViewController {
NSString * measureType; // Declare an NSString instance variable
}
- (id) initWithMeasureType: (int)value;
#end
As it is, there is only one copy of the variable, and every time you instantiate a new object, its value changes. Since each instance is referring to the same copy, they all get the same value:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
NSLog(#"%#", [length measurementType]); // Prints "Length"
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
NSLog(#"%#", [length measurementType]); // Prints "Mass"
You also need to change your init... method as mentioned by other answerers:
- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
Since you are assigning a literal string to the instance variable, you do not need to worry about managing its memory; if you were doing anything more complicated, you would probably do well by declaring a property.
Another note: initializer methods should always return id, a generic object pointer, to allow subclasses to work properly.
You need to call [super init] first, like this:
-(id) initWithMeasureType:(int)value
{
if ((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}
Constructors are a convention in Objective-C rather than a language feature. So, for example, there's no automatic calling of parent constructors (just like you wouldn't expect any other overridden method to call its parent implementations). Similarly, the names used for constructors are just conventions, so the compiler knows nothing of init. With that in mind, what you actually want as your constructor is:
-(id) initWithMeasureType:(int)value
{
if((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}
Assuming measureType is an instance variable (declared as part of the interface, generally speaking) and not a global then that should do what you want.
In your custom init method you just need to start with:
self = [super init];
- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}

the best way to implement readable switch case with strings in objective-c?

In other dynamic languages like ruby, javascript etc. you can do simply this:
switch(someString) {
case "foo":
//do something;
break;
case "bar":
// do something else;
break;
default:
// do something by default;
}
In objective-c, because it's derived very colsely from c language, you can't do that. My best practice for this is:
#import "CaseDemo.h"
#define foo 1
#define bar 2
static NSMutableDictionary * cases;
#implementation CaseDemo
- (id)init
{
self = [super init];
if (self != nil) {
if (cases == nil) {
// this dict can be defined as a class variable
cases = [[NSMutableDictionary alloc] initWithCapacity:2];
[cases setObject:[NSNumber numberWithInt:foo] forKey:#"foo"];
[cases setObject:[NSNumber numberWithInt:bar] forKey:#"bar"];
}
}
return self;
}
- (void) switchFooBar:(NSString *) param {
switch([[cases objectForKey:param] intValue]) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
#end
It's seems to be ok, but #define makes foo and bar like a reserved word, and I can't use in my code. If I replace define constants with class constants, this problem is fixed, because in other classes I must use MyClassName before the constant name. But how can I minimize the object allocation for this simple task? Someone have a "better practice" for this?
EDIT:
The code below is what I wanted to do, but it's a little bit unconfortable to get the values of the enum or #define. Because I created an application what have just an input where I can write the string to get that hash and go back to xcode and set the values for the enums. So my problem is I can't do that in runtime time, because of the main behavour of switch case statement... Or if I do that with that NSDictionary way -> its have a lot of overhead compared with this solution.
#import "CaseDemo.h"
typedef enum {
foo = 1033772579,
bar = -907719821
} FooBar;
unsigned int APHash(NSString* s)
{
const char* str = [s UTF8String];
unsigned int len = [s length];
unsigned int hash = 0xAAAAAAAA;
unsigned int i = 0;
for(i = 0; i < len; str++, i++)
{
hash ^= ((i & 1) == 0) ? ( (hash << 7) ^ (*str) * (hash >> 3)) :
(~((hash << 11) + ((*str) ^ (hash >> 5))));
}
return hash;
}
#implementation CaseDemo
- (void) switchFooBar:(NSString *) param {
switch(APHash(param)) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
#end
NOTE: the hash function can defined elsewhere in common namespace to use it anywhere, typically I create a Utils.h or Common.h for this kind of stuff.
NOTE2: In "real word" we need to use some cryptographic hashing function, but now I used the algorithm by Arash Partow to keep the example simple.
So, my final question: Is there a way to evaluate these values with the preprocessor somehow? I think no, but maybe? :-)
Something like:
// !!!!!! I know this code is not working, I don't want comments about "this is wrong" !!!!
// I want a solution to invoke method with preprocessor, or something like that.
typedef enum {
foo = APHash(#"foo"),
bar = APHash(#"bar")
} FooBar;
UPDATE: I found a "maybe solution" but it seems to be work with g++ 4.6> only. generalized constant expressions may be do it for me. But I'm still testing...
typedef enum {
foo,
bar
} FooBar;
- (void) switchFooBar:(NSString *) param {
switch([[cases objectForKey:param] intValue]) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
NSString * extension = [fileName pathExtension];
NSString * directory = nil;
NSUInteger index = [#[#"txt",#"png",#"caf",#"mp4"] indexOfObjectPassingTest:^
BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return [obj isEqualToString:extension];
}];
switch (index)
{
case 0:
directory = #"texts/";
break;
case 1:
directory = #"images/";
break;
case 2:
directory = #"sounds/";
break;
case 3:
directory = #"videos/";
break;
default:
#throw [NSException exceptionWithName:#"unkonwnFileFormat"
reason:[NSString stringWithFormat:#"zip file contain nknown file format: %#",fileName]
userInfo:nil];
break;
}
The technique is extracted from production code and modified to use colors for this example. The premise is that a string comes in with the text name of a color from some external feed. This inbound color name is matched against known Crayola color names in the system. If the new color name matches any known Crayola color name strings, the numeric value for HTML hex code equivalent of that Crayola color name is returned.
First use http://www.unit-conversion.info/texttools/crc/ and put all of your known Crayola color names through it to get numerical equivalents. These will be used in the case statements. Then put those values into an enumerated for cleanliness (e.g. LivingColors below). These numbers become equivalent to the actual color name string.
Then at run time the variable text is put through the same function, but internal to your code, to generate the same kind of numeric constant. If the numeric constant from the code matches the statically generated constant, then the text strings that they represent are exactly equal.
The internal code function is crc32() found in zlib.h. This generates a unique number based upon the text put through it just like the web page converter above. The unique number from crc32() can then be used in a common C switch() statement to match against the known colors which were pre-processed into numbers into the enumerated.
To use the native system function crc32() to generate CRC32B values, include the /usr/lib/libz.1.dylib in your project for linking. Be sure to include or #import <zlib.h> in your source that references crc32()
Implement an Objective C category on NSString to make the native NSString class understand the crc32: and htmlColor: messages.
Finally, read/get the name of the color into an NSString object, then send the string the htmlColor: message, it switches to match the 'strings' and returns the HTML hex equivalent value for a Crayola color name.
#import <zlib.h>
#define typedefEnum( enumName ) typedef enum enumName enumName; enum enumName
/**
#see Crayola Web Colors https://www.w3schools.com/colors/colors_crayola.asp
#see CRC32B value generator for static case strings http://www.unit-conversion.info/texttools/crc/ or http://www.md5calc.com
*/
#define typedefEnum( enumName ) typedef enum enumName enumName; enum enumName
typedefEnum( LivingColors ) {
kRedColor = 0xc22c196f, // "Red" is 0xED0A3F in HTML
kBlueberryColor = 0xfbefa670, // "Blueberry" is 0x4F86F7 in HTML
kLightChromeGreenColor = 0x44b77242, // "Light Chrome Green" is 0xBEE64B in HTML
kPermanentGeraniumLakeColor = 0xecc4f3e4, // "Permanent Geranium Lake" is 0xE12C2C in HTML
kIlluminatingEmeraldColor = 0x4828d5f2, // "Illuminating Emerald" is 0x319177 in HTML
kWildWatermelonColor = 0x1a17c629, // "Wild Watermelon" is 0xFD5B78 in HTML
kWashTheDogColor = 0xea9fcbe6, // "Wash the Dog" is 0xFED85D in HTML
kNilColor = 0xDEADBEEF // could use '0' but what fun is that?
};
// generates the CRC32B, same used to check each ethernet packet on the network you receive so it’s fast
- (NSUInteger) crc32 {
NSUInteger theResult;
theResult = (NSUInteger)crc32( 0L,
(const unsigned char *) [self UTF8String],
(short)self.length);
return theResult;
}
/// #return the HTML hex value for a recognized color name string.
- (NSUInteger) htmlColor {
NSUInteger theResult = 0x0;
LivingColors theColorInLivingColor = kNilColor;
theColorInLivingColor = (LivingColors) [self crc32];
// return the HTML value for a known color by effectively switching on a string.
switch ( theColorInLivingColor ) {
case kRedColor : {
theResult = 0xED0A3F;
}
break;
case kBlueberryColor : {
theResult = 0x4F86F7;
}
break;
case kLightChromeGreenColor : {
theResult = 0xBEE64B;
}
break;
case kPermanentGeraniumLakeColor : {
theResult = 0xE12C2C;
}
break;
case kIlluminatingEmeraldColor : {
theResult = 0x319177;
}
break;
case kWildWatermelonColor : {
theResult = 0xFD5B78;
}
break;
case kWashTheDogColor : {
theResult = 0xFED85D;
}
break;
case kNilColor :
default : {
theResult = 0x0;
}
break;
}
return theResult;
}
For the example an Objective C Category was made to add the two methods the existing Cocoa class NSString, rather than subclass it.
The end result is that an NSString object appears to have the ability to natively get a CRC32B value of itself (very handy beyond this example) and can essentially switch() on the color’s name string that possibly came in from the user, a text file, etc. to identify a match much faster than any text string comparison can occur.
Fast, efficient, and reliable, this approach can easily be adapted to any kind of variable text matching to static known value text. Bear in mind that CRC32B checksums are generated by bitwise operations and the C switch statement use bitwise operations optimized at compile time. Remember this is speedy because CRC32B is the highly optimized function used to check each ethernet packet your Mac/iPhone/iPad receives... even when you download multi-gigabyte files like macOS Sierra.

Get instance name of sender in objective-C

I want to get the name of a sender in Objective-C. For example, below I have a method which is called by an instance of UISlider in Interface Builder, I want to know what the instance name of it is so I can later add conditional blocks to the method for which instance of UISlider called the method.
e.g.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
NSLog(#"%#",sender);
Outputs:2010-10-15 22:46:02.257 EPC[3225:207] <UISlider: 0x495b140; frame = (205 3; 118 23); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x492e340>>
I want to be able to say
if(sender==myInstanceName) {
//do this
}
You could use
.tag member
to read an write and integer ID for the slider like this:
-(IBAction)sliderChanged:(UISlider
*)sender {
switch (sender.tag) {
case 0:
//SLider 0
break;
case 1:
//SLider 1
break;
default:
break;
}
}
Tag ID's can also be set for components in IB.
If your set on a string then you would need to subclass a UISlider.
You would use the tag property of UIView for identifying the sender.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
if (sender.tag == 1)
{
// do whatever
}
else
{
// do something else
}
}