objective c, nsstring to nsarray - objective-c

Im new to objective C so I need some help, is there any fast way of converting this string:
{{2, 2}, {103, 166}} to an NSArray, or just getting out the values to four NSInteger?

This is the storage format of a CGRect/NSRect. You can easily read it using CGRectFromString/NSRectFromString and then get the values like this:
NSString *string = #"{{2, 2}, {103, 166}}";
CGRect rect = CGRectFromString(string);
CGFloat x = rect.origin.x; // 2
CGFloat y = rect.origin.y; // 2
CGFloat width = rect.size.width; // 103
CGFloat height = rect.size.height; // 166

Related

Objective-C to Swift: NSScreen

I'm learning Swift. As a test, I'm translating some of my old Objective-C programs to swift. But I have a crazy error: In Objective-C I have the following code:
- (CGSize)makeSizeFromCentimetersWidth: (CGFloat)width andY: (CGFloat)height {
NSScreen *screen = [NSScreen mainScreen];
NSDictionary *description = [screen deviceDescription];
NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:#"NSScreenNumber"] unsignedIntValue]);
CGFloat resolution = (displayPixelSize.width / displayPhysicalSize.width) * 25.4f;
CGFloat pixelsWidth = 0.394 * width * resolution;
CGFloat pixelsHeight = 0.394 * height * resolution;
return CGSizeMake(pixelsWidth, pixelsHeight);
}
In swift I have translated to this:
func makeSizeFromCentimeters(width: CGFloat, height: CGFloat) -> CGSize {
var screen: NSScreen = NSScreen.mainScreen()!
var description: NSDictionary = screen.deviceDescription
var displayPixelSize: NSSize = description.objectForKey(NSDeviceSize)!.sizeValue
var displayPhysicalSize: CGSize = CGDisplayScreenSize(description.objectForKey("NSScreenNumber")!.unsignedIntValue)
var resolution = (displayPixelSize.width / displayPhysicalSize.width) * 25.4
var pixelsWidth: CGFloat = 0.394 * width * resolution
var pixelsHeight: CGFloat = 0.394 * height * resolution
return CGSizeMake(pixelsWidth, pixelsHeight)
}
In Objective-C the code does what it should: Calculate a size from centimeters to pixels, to give out (in my case) an NSImageView with exactly the size of the given centimeters. But in Swift, the returned size, is always 0:
NSLog("%f", makeSizeFromCentimeters(2, height: 2).width)
NSLog("%f", makeSizeFromCentimeters(2, height: 2).height)
Is there an translating error? Which variable is 0? (No idea why it should be 0 if it's not caused by a variable).
Thank you for your help!

How can I deal with operation between Int and CGFloat?

In Objective-C I can do the following operation:
Objective-C code:
CGFloat width = CGRectGetWidth(mView.bounds);
CGFloat total = width*[myArray count];
but in Swift, it will raise an error:
Could not find an overload for '*' that accepts the supplied arguments
How can I avoid this situation elegantly?
First, let's create some demo data
let array: NSArray = ["a", "b", "c"] //you could use a Swift array, too
let view = UIView() //just some view
Now, everything else works almost the same way as in Obj-C
let width: CGFloat = CGRectGetWidth(view.bounds)
or simply
let width = CGRectGetWidth(rect) //type of the variable is inferred
and total:
let total = width * CGFloat(array.count)
Note that we have to add a CGFloat cast for array.count. Obj-C would implicitly cast NSUInteger to CGFloat but Swift has no implicit casts, so we have to add an explicit one.
In Swift you cannot multiply two numbers of different types (NSNumber, Int, Double, etc.) directly. The width of a CGRect is of floating point type and the array count is of integer type. Here's a working example:
let myArray: Int[] = [1,2,3]
let rect: CGRect = CGRect(x:0,y:0,width:100,height:100)
let total: Double = rect.size.width * Double(myArray.count)
Swift does not allow operations between two numbers of different types. Therefore, before to multiply your array.count (Int) by your width (CGFloat), you'll have to cast it to CGFloat.
Fortunately, Swift provides a simple CGFloat initializer init(_:) that creates a new CGFloat from an Int. This initializer has the following declaration:
init<Source>(_ value: Source) where Source : BinaryInteger
Creates a new value, rounded to the closest possible representation.
The Swift 5 Playground sample code below shows how to perform your calculation by using CGFloat's initializer:
import UIKit
import CoreGraphics
// Set array and view
let array = Array(1...3)
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: rect)
// Perform operation
let width = view.bounds.width
let total = width * CGFloat(array.count)
print(total) // prints: 300.0
need to change All Int to CGFloat Type, or change All CGFloat to Int
let a: CGFloat = 0.25
let b: Int = 1
// wrong: Binary operator '*' cannot be applied to operands of type 'CGFloat' and 'Int'
// let c = a * b
// right: change All Int to CGFloat Type
let r = a * CGFloat(b)
// right: change All CGFloat to Int
let r = Int(a) * b

Plotting hits within given areas of circle - Code for setup getting bloated

[Alternative approach given by user1118321, old and new code listed for reference]
I am busy writing a small app which allows a game of darts 501 to be scored. I have set up a class to contain the individual scoring areas (GDSBoardArea) and one mutable array which represents the entire dart board and holds all the individual scoring class instances.
I have written the code for one section (20 score, inner and outer, and triple and double 20) but the issue I have is that the code for this is quite bloated and was woindering if there was a better way to init all the areas with less, more managable code as I will have to repeat this below another 19 times for the other sections?
I have a class that holds the settings for the various individual areas, one class to hold all these areas and helper functions.
I did attempt to try reuse the CGPoints and area arrays but this simply caused issues I think related to the fact that it updates a reference as the scoringAreas addObject does not copy the array object (Please correct me if I am wrong).
I setup the scoring areas once when the application runs using the createAreas function and I cant seem to reuse the CGPoints and arrays holding them.
My questions are:
1 - Is there a way to pass in a variable array of CGPoints to "GDSBoardArea"'s last parameter instead of having to create the CGPoints, add them to an array and pass the array in.
2 - Does the mutable array method addobject copy or reference the object passed to it? Is it there any way to update an array and then make a copy of it to a "master" mutable array so that the master array is effectively holding a different array in each element. I am basically try to avoid having to create a new array for each of the master arrays elements.
createAreas
-(void)createAreas
{
scoringAreas = [[NSMutableArray alloc] init];
/*
20 scoring section
*/
// 20 score - Inner triangle area
/////////////////////////////////////////
// Set up CGPoints for area
CGPoint pt_inner20_1 = CGPointMake(368.0f, 234.0f);
CGPoint pt_inner20_2 = CGPointMake(413.0f, 234.0f);
CGPoint pt_inner20_3 = CGPointMake(389.0f, 360.0f);
// Add points to array for passing to GDSBoardArea Class
NSMutableArray * areaPointsArray_Inner20 = [NSMutableArray array];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_1]];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_2]];
[areaPointsArray_Inner20 addObject:[NSValue valueWithCGPoint:pt_inner20_3]];
// Create GDSBoardArea Instance with init values
GDSBoardArea *scoreAreaFor_Inner20 = [[GDSBoardArea alloc] initWithName:#"20-Inner" abbrev:#"20" areaValue:20 pointsArray:NULL];
// Add Array of CGPoints to GDSScoreArea
[scoreAreaFor_Inner20 setPointsForPath:areaPointsArray_Inner20];
// Add ScoreArea to ScoreAreas Class
[scoringAreas addObject:scoreAreaFor_Inner20];
// 20 score - Outer Quadrilateral
/////////////////////////////////////////
CGPoint pt_outer20_1 = CGPointMake(353.0f, 125.0f);
CGPoint pt_outer20_2 = CGPointMake(429.0f, 127.0f);
CGPoint pt_outer20_3 = CGPointMake(415.0f, 208.0f);
CGPoint pt_outer20_4 = CGPointMake(365.0f, 209.0f);
NSMutableArray * areaPointsArray_Outer20 = [NSMutableArray array];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_1]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_2]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_3]];
[areaPointsArray_Outer20 addObject:[NSValue valueWithCGPoint:pt_outer20_4]];
GDSBoardArea *scoreAreaFor_Outer20 = [[GDSBoardArea alloc] initWithName:#"20-Outer" abbrev:#"20" areaValue:20 pointsArray:NULL];
[scoreAreaFor_Outer20 setPointsForPath:areaPointsArray_Outer20];
[scoringAreas addObject:scoreAreaFor_Outer20];
// Double 20 Quadrilateral
/////////////////////////////////////////
CGPoint pt_d20_1 = CGPointMake(351.0f, 102.0f);
CGPoint pt_d20_2 = CGPointMake(353.0f, 118.0f);
CGPoint pt_d20_3 = CGPointMake(430.0f, 119.0f);
CGPoint pt_d20_4 = CGPointMake(433.0f, 104.0f);
NSMutableArray * areaPointsArray_Double20 = [NSMutableArray array];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_1]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_2]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_3]];
[areaPointsArray_Double20 addObject:[NSValue valueWithCGPoint:pt_d20_4]];
GDSBoardArea *scoreAreaFor_double20 = [[GDSBoardArea alloc] initWithName:#"20-Double" abbrev:#"d20" areaValue:40 pointsArray:NULL];
[scoreAreaFor_double20 setPointsForPath:areaPointsArray_Double20];
[scoringAreas addObject:scoreAreaFor_double20];
// Triple 20 Quadrilateral
/////////////////////////////////////////
CGPoint pt_t20_1 = CGPointMake(367.0f, 214.0f);
CGPoint pt_t20_2 = CGPointMake(368.0f, 226.0f);
CGPoint pt_t20_3 = CGPointMake(412.0f, 226.0f);
CGPoint pt_t20_4 = CGPointMake(413.0f, 214.0f);
NSMutableArray * areaPointsArray_Triple20 = [NSMutableArray array];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_1]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_2]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_3]];
[areaPointsArray_Triple20 addObject:[NSValue valueWithCGPoint:pt_t20_4]];
GDSBoardArea *scoreAreaFor_triple20 = [[GDSBoardArea alloc] initWithName:#"20-Triple" abbrev:#"t20" areaValue:60 pointsArray:NULL];
[scoreAreaFor_triple20 setPointsForPath:areaPointsArray_Triple20];
[scoringAreas addObject:scoreAreaFor_triple20];
}
ScoringAreas is a Mutable Array that simplt represents all the areas of the board and is defined as:
#property (strong, atomic) NSMutableArray *scoringAreas;
GDSBoardArea Class in case its relevant
// GDSBoardArea.h
#import <Foundation/Foundation.h>
#interface GDSBoardArea : NSObject
#property NSMutableArray *pointsForPath;
#property int areaValue;
#property NSString *name;
#property NSString *abbrev;
-(id) init;
-(id)initWithName:(NSString *)name_ abbrev:(NSString *)abbrev_ areaValue:(int)areaValue_ pointsArray:(NSMutableArray*)pointsArray;
#end
--
// GDSBoardArea.m
#import "GDSBoardArea.h"
#implementation GDSBoardArea
-(id)init{
if (self = [super init]) {
/* perform your post-initialization logic here */
[self setName:#""];
[self setAbbrev:#""];
[self setAreaValue:0];
[self setPointsForPath:NULL];
}
return self;
}
-(id)initWithName:(NSString *)name_ abbrev:(NSString *)abbrev_ areaValue:(int)areaValue_ pointsArray:(NSMutableArray*)pointsArray
{
self = [super init];
if (self) {
self.name = name_;
self.abbrev = abbrev_;
self.areaValue = areaValue_;
self.pointsForPath = pointsArray;
}
return self;
}
#end
Code change as per answer from user1118321:
createAreas function was completely removed and direct calculation done as follows (Will add more as it is fleshed out until complete)
-(GDSBoardArea *)checkPointsScore:(CGPoint)tappedPoint
{
#define NUM_PIE_SLICES 20
#define NUM_RINGS 7
#define DEG_PER_SLICE 18
#define PII 3.141592653
#define OFFSETFORBOARD 99.3f
float radius_board = 344.0f;
float thickness_innerbull = 16.0f;
float thickness_outerbull = 18.0f;
float thickness_innerSingleScore = 116.0f;
float thickness_double = 16.0f;
float thickness_outerSingleScore = 80.0f;
float thickness_triple = 20.0f;
float thickness_noscore = 78.0f;
GDSBoardArea *tappedScoreArea = NULL;
double dartX = tappedPoint.x;
double dartY = tappedPoint.y;
double centerX = 390.0f;
double centerY = 390.0f;
double angle = atan2((dartY - centerY), (dartX - centerX)) * 180 / PII;
angle = angle + OFFSETFORBOARD; // To force slice 0 to correspond to Score of 20
int slice = (int)floor(angle / DEG_PER_SLICE);
NSLog(#"angle: %f", angle);
NSLog(#"slice #: %i", slice);
return tappedScoreArea;
}
If it were me, I wouldn't bother at all with storing the various areas of the board. First, they aren't triangles and quads, and second, it's fairly easy to calculate them. You can figure out which "pie slice" you're in by getting the arctangent of the point where the dart lands, like this:
double angle = atan2((dartY - centerY) / (dartX - centerX));
You can then convert that to an integer between 1 and # of slices like this:
int slice = (int)floor(angle / anglePerSlice);
Next, you can figure out which ring the user is in by using the distance from the board's center:
double deltaX = dartX - centerX;
double deltaY = dartY - centerY;
double distFromCenter = sqrt (deltaX * deltaX + deltaY * deltaY);
You could then convert that into an index by looking it up in an array. You have a single array containing all the boundaries in a single slice. All slices have their boundaries at the same distances. So it would look something like this:
double concentricBounds[kNumBounds] = { 10, 15, 100, 110, 200, 210 }; // Just made up - I don't know what distances you're using.
bool done = false;
int index = 0;
for (int i = 0; (i < kNumBounds) && (!done); i++)
{
if (distFromCenter < concentricBounds [ i ])
{
done = true;
index = i;
}
}
if (!done)
{
...point was off the board...
}
So now you have the slice it was in and the ring it's in. You can just have a 2D array of point values to look up into:
int points [][] = { {50, 25, 20, 60, 20, 40 }, { 50, 25, 5, 15, 5, 15 }, ...etc... };
Note that atan2() returns angles with 0° being parallel to the X-axis and it increases in counter-clockwise order. So you may have to rotate the scores to orient your board correctly.

How To Get UIView Height Only?

I have this code :
_width.text = NSStringFromCGRect(_screen.frame);
and it gives me on my iPod 5:
{{0,20}, {320,548}}
how to get only height value = 548 so that I can use it to calculate another components.
thank you.
I guess you want a CGFloat, not a NSString if you want to calculate other components.
CGFloat height = _screen.frame.size.height;
Or if you want a string after all:
_width.text = [NSString stringWithFormat:#"%f", _screen.frame.size.height];
The frame property of UIView is a CGRect struct. To learn about CGRect go take a look at the documentation.
I would encourage you to use CGGeometry check the documentation, in your case use CGRectGetHeight().
CGFloat height = CGRectGetHeight(_screen.frame);
Try to use like this...
You can get Height Like this...
CGFloat height = _screen.frame.size.height;
_width.text = [NSString stringWithFormat:#"%f",height];
You can get Width Like this...
CGFloat width = _screen.frame.size.width;
_width.text = [NSString stringWithFormat:#"%f",width];
You can get x coordinate Like this Like this...
CGFloat x = _screen.frame.origin.x;
You can get y coordinate Like this Like this...
CGFloat y = _screen.frame.origin.y;
_screen.frame is a struct with a CGSize and a CGPoint.
CGPoint origin = _screen.frame.origin;
CGSize size = _screen.frame.size;
The struct CGPoint has the members x and y
CGFloat x = origin.x; // float on 32bit and double on 64bit
CGFloat y = origin.y;
The struct CGSize has the members width and height
CGFloat width = size.width;
CGFloat height = size.height;
You can print a message with a formatted NSString or C-String with the format specifier %
NSString *output = [NSString stringWithFormat:#"My screen height is: %f", height];
If you want to show the height as 543 not as 543.0, then use the float string specifier in the NSString creation method
NSString *string = [NSString stringWithFormat:#"Integer height is: %.0f", height];
// even with values like 0.9999 the output would be "Integer height is: 0"
If the property _width.text is a NSString.
_width.text = output;

use of escape sequence

i want to show image height and width in a format a*b.for that i am using following code.problem is that label not show proper value where as height and ImagesizeWidth variable show correct value.
UIImage *newImage = image;
NSString *c= #"*";
int height = image.size.height;
int ImageSizeWidth = image.size.width;
//int size = image.size.height * image.size.width;
pixelInformation.text = [NSString stringWithFormat:#"%d%a%d",ImageSizeWidth,c,height];
can anyone help me. thanks in advance.
You should use the %# format specifier of Objective-C objects. They need to have description implemented.
So the last line should be,
pixelInformation.text = [NSString stringWithFormat:#"%d%#%d",ImageSizeWidth,c,height];
It's not %a. And you can directly put a * in the string though.
pixelInformation.text = [NSString stringWithFormat:#"%d*%d",ImageSizeWidth,height];