Swift - enum with button.tag? - objective-c

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.

Related

how to make switch case of enum case (swift)

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

How to write init method in Swift?

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

Updating UILabel and clearing it?

I have a UILabel that I start at "0" and then as my game progresses I call the below code to add "1" to the score each time a button is pressed. This all works fine.
playerOneScore.text = [NSString stringWithFormat:#"%d",[playerOneScore.text intValue]+1];
My problem is that when the game ends and the user presses the "Replay" button. This replay button sets the label back to "0" by calling this.
playerOneScore.text = #"0";
But then when the game progresses my label jumps from "0" to where it had initially left off + 1. What am I doing wrong here? Not making sense.
Any help would be very grateful! Thank you!
More code requested (simplified):
- (void)viewDidLoad
{
playerOneScore.text = #"0";
}
-(IBAction)touchDown {
if (playerOneTurn == YES) {
if (button1.touchInside == YES) {
playerOneScore.text = [NSString stringWithFormat:#"%d",[playerOneScore.text intValue]+1];
}
}
}
-(void) replayGame {
playerOneScore.text = #"0";
}
Not sure what's going on, but I would suggest using a separate NSInteger property to track the score. Then override the property setter to update the label text.
Add the property to your interface or a private category:
#property (nonatomic, assign) NSInteger score;
Then override the setter:
- (void)setScore:(NSInteger)value {
_score = value;
playerOneScore.text = [NSString stringWithFormat:#"%d", value];
}
Then in your action, update the score and label using the new property:
-(IBAction)touchDown {
if (playerOneTurn == YES) {
if (button1.touchInside == YES) {
self.score += 1;
}
}
}
Note that I haven't compiled or tested this code, but it should be close.
EDIT: fixed typo in setter

How to resume a CAAnimation after coming back from multitasking

in my app i have an array of CALayer that I have animated along a bezierPath. When I close and reopen the app my layers are not animating and not in the same position as before closing the app. I have implemented two methods, pauseLayer and resumeLayer that works when I trigger them with two buttons inside my app but they won't work after closing the app. The code is the following
- (void)pauseLayers{
for(int y=0; y<=end;y++)
{
CFTimeInterval pausedTime = [car[y] convertTime:CACurrentMediaTime() fromLayer:nil];
car[y].speed = 0.0;
car[y].timeOffset = pausedTime;
standardUserDefaults[y] = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults[y]) {
[standardUserDefaults[y] setDouble:pausedTime forKey:#"pausedTime"];
[standardUserDefaults[y] synchronize];
}
NSLog(#"saving positions");
}
}
-(void)resumeLayers
{
for(int y=0; y<=end;y++)
{
standardUserDefaults[y] = [NSUserDefaults standardUserDefaults];
car[y].timeOffset = [standardUserDefaults[y] doubleForKey:#"pausedTime"];
CFTimeInterval pausedTime = [car[y] timeOffset];
car[y].speed = 1.0;
car[y].timeOffset = 0.0;
car[y].beginTime = 0.0;
CFTimeInterval timeSincePause = [car[y] convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
car[y].beginTime = timeSincePause;
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
mosquitosViewController *mvc = [[mosquitosViewController alloc] init];
[mvc pauseLayers];
}
The problem with what you are trying to do above is that you are creating a completely new instance of your view controller, which is not the one that was showing onscreen. That's why nothing happens when you send the pauseLayers message.
What you should do is register to receive notifications for when your app goes to and comes from the background and call the appropriate methods (pauseLayers and resumeLayers) when that notification arrives.
You should add the following code somewhere in your mosquitosViewController implementation (I usually do so in viewDidLoad):
// Register for notification that app did enter background
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(pauseLayers)
name:UIApplicationDidEnterBackgroundNotification
object:[UIApplication sharedApplication]];
// Register for notification that app did enter foreground
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(resumeLayers)
name:UIApplicationWillEnterForegroundNotification
object:[UIApplication sharedApplication]];
I write a Swift 4 version extension based on #cclogg and #Matej Bukovinski answers from this thread. All you need is to call layer.makeAnimationsPersistent()
Full Gist here: CALayer+AnimationPlayback.swift, CALayer+PersistentAnimations.swift
Core part:
public extension CALayer {
static private var persistentHelperKey = "CALayer.LayerPersistentHelper"
public func makeAnimationsPersistent() {
var object = objc_getAssociatedObject(self, &CALayer.persistentHelperKey)
if object == nil {
object = LayerPersistentHelper(with: self)
let nonatomic = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &CALayer.persistentHelperKey, object, nonatomic)
}
}
}
public class LayerPersistentHelper {
private var persistentAnimations: [String: CAAnimation] = [:]
private var persistentSpeed: Float = 0.0
private weak var layer: CALayer?
public init(with layer: CALayer) {
self.layer = layer
addNotificationObservers()
}
deinit {
removeNotificationObservers()
}
}
private extension LayerPersistentHelper {
func addNotificationObservers() {
let center = NotificationCenter.default
let enterForeground = NSNotification.Name.UIApplicationWillEnterForeground
let enterBackground = NSNotification.Name.UIApplicationDidEnterBackground
center.addObserver(self, selector: #selector(didBecomeActive), name: enterForeground, object: nil)
center.addObserver(self, selector: #selector(willResignActive), name: enterBackground, object: nil)
}
func removeNotificationObservers() {
NotificationCenter.default.removeObserver(self)
}
func persistAnimations(with keys: [String]?) {
guard let layer = self.layer else { return }
keys?.forEach { (key) in
if let animation = layer.animation(forKey: key) {
persistentAnimations[key] = animation
}
}
}
func restoreAnimations(with keys: [String]?) {
guard let layer = self.layer else { return }
keys?.forEach { (key) in
if let animation = persistentAnimations[key] {
layer.add(animation, forKey: key)
}
}
}
}
#objc extension LayerPersistentHelper {
func didBecomeActive() {
guard let layer = self.layer else { return }
restoreAnimations(with: Array(persistentAnimations.keys))
persistentAnimations.removeAll()
if persistentSpeed == 1.0 { // if layer was playing before background, resume it
layer.resumeAnimations()
}
}
func willResignActive() {
guard let layer = self.layer else { return }
persistentSpeed = layer.speed
layer.speed = 1.0 // in case layer was paused from outside, set speed to 1.0 to get all animations
persistAnimations(with: layer.animationKeys())
layer.speed = persistentSpeed // restore original speed
layer.pauseAnimations()
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"1");
mosquitosViewController *mvc = [[mosquitosViewController alloc] init];
[mvc pauseLayers];
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
See my answer to this post for details on how to restart an animation after multitasking:
Restoring animation where it left off when app resumes from background

How to programmatically open an NSComboBox's list?

I've been around this for a while.. I thought this should be an easy task, but it isn't =D
What I am trying to do, is to display the combobox's list when the user clicks the combobox but not specifically in the button.
Any Idea?
Thanks in advance!
This answer fits the title of the question, but not question itself. Omer wanted to touch a text field and have the box popup.
This solution shows the popup when the user enters text.
I found this answer on cocoabuilder from Jens Alfke. I reposted his code here. Thanks Jens.
original cocoabuilder post: (http://www.cocoabuilder.com/archive/cocoa)
#interface NSComboBox (MYExpansionAPI)
#property (getter=isExpanded) BOOL expanded;
#end
#implementation NSComboBox (MYExpansionAPI)
- (BOOL) isExpanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
return [[ax accessibilityAttributeValue:
NSAccessibilityExpandedAttribute] boolValue];
}
- (void) setExpanded: (BOOL)expanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
[ax accessibilitySetValue: [NSNumber numberWithBool: expanded]
forAttribute: NSAccessibilityExpandedAttribute];
}
I used this code in my controlTextDidChange: method.
- (void) controlTextDidChange:(NSNotification *) aNotification {
NSTextField *textField = [aNotification object];
NSString *value = [textField stringValue];
NSComboBox *box = [self comboBox];
if (value == nil || [value length] == 0) {
if ([box isExpanded]) { [box setExpanded:NO]; }
} else {
if (![box isExpanded]) { [box setExpanded:YES]; }
}
}
Returns true if the NSComboBox's list is expanded
comboBox.cell?.isAccessibilityExpanded() ?? false
Open the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(true)
Close the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(false)
Ref. jmoody’s answer.
You can use the following code line:
[(NSComboBoxCell*)self.acomboBox.cell performSelector:#selector(popUp:)];
Put
comboBoxCell.performSelector(Selector("popUp:"))
Into
override func controlTextDidChange(obj: NSNotification) {}
is what I ended up with. Thanks #Ahmed Lotfy
Here's the full code, it works for me on OSX 10.11
override func controlTextDidChange(obj: NSNotification) {
if let comboBoxCell = self.comboBox.cell as? NSComboBoxCell {
comboBoxCell.performSelector(Selector("popUp:"))
}
}
Thanks to jmoody and Jens Alfke mentioned above. Here is a SWIFT translation of the above solution.
import Cocoa
class CComboBoxEx: NSComboBox {
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
func isExpanded() -> Bool{
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
if ax!.accessibilityAttributeValue(NSAccessibilityExpandedAttribute) != nil {
return true
}
}
return false
}
func setExpanded (bExpanded:Bool) {
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
ax!.accessibilitySetValue(NSNumber(bool: bExpanded), forAttribute: NSAccessibilityExpandedAttribute)
}
}
}
NSComboBox was not designed to work this way. Because the user may want to edit the text in the control, they'll need to be able to click it without unexpectedly popping up the choices.
You would need to subclass NSComboBoxCell and change this behavior ... but then you'd have a standard-looking control that does not behave in a standard way. If you're determined to do this, take a look at the open source version of NSComboBoxCell. The interesting methods appear to be -popUpForComboBoxCell: and friends.
Based on the other answers I wrote this solution (tested with Xcode 10.2.1, Swift 5). It uses the same ideas but it's a little shorter.
// Put this extension for NSComboBox somewhere in your project
import Cocoa
public extension NSComboBox {
var isExpanded: Bool{
set {
cell?.setAccessibilityExpanded(newValue)
}
get {
return cell?.isAccessibilityExpanded() ?? false
}
}
}
// Set your corresponding NSViewController as NSComboBoxDelegate
// in the storyboard and add this piece of code
// to expand the combobox when the user types
class MyViewController: NSViewController, NSComboBoxDelegate {
func controlTextDidChange(_ notification: Notification) {
guard let comboBox = notification.object as? NSComboBox else { return }
if comboBox.isExpanded == false {
comboBox.isExpanded = true
}
}
}