Objective-C modify parameter to method at runtime - objective-c

I'd like to change the value of a parameter at runtime and curious how this works in Obj-C.
I have a loop where value of 'n' is 0 increasing by 1 with each loop. How does one increment the passed parameter by 1 as n moves.
UIViewSubclass *uiViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:kPlacement0;
next time through loop I'd like 2nd argument to read as: andPlacement:kPlacement1;
and then: andPlacement:kPlacement2; and on...
do I make kPlacement a string and stringByAppendingString:[[NSNumber numberWithInt:n] stringValue]; ?
What's the Obj-C/Cocoa approach?

You can't modify your source code or make up variable references at runtime. Objective-C isn't that dynamic.
If the values of kPlacement0 through kPlacementMax are sequential, you may be able to use a for loop to step through them directly:
for (MyPlacement placement = kPlacement0; placement += kPlacementIncrement; placement <= kPlacementMax) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placement];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
(You will need to define kPlacementIncrement and kPlacementMax in addition to the kPlacement0, etc. constants. I'm using MyPlacement as the name of your enumeration type that the kPlacement0 etc. constants correspond to.)
If they are not sequential, put them in a C array and iterate on that array:
enum { numPlacements = <#Insert the number of placement constants here#> };
MyPlacement placements[numPlacements] = {
kPlacement0,
kPlacement1,
kPlacement2,
⋮
}
for (unsigned i = 0U; i < numPlacements; ++i) {
UIViewSubclass *instanceOfUIViewSubclass = [[UIViewSubclass alloc] initWithValue:([value integerValue])
andPlacement:placements[i]];
//Do something with instanceOfUIViewSubclass.
[instanceOfUIViewSubclass release];
}
You probably can come up with more-descriptive names than kPlacement0 etc. When you want to refer to them by number, do that; when you want to refer to them by name, give them good names.

Related

Accessing Attributes

I am new to programming and objective C, sorry I have to ask this basic question. I can not figure out the lines of code below. Why do I need to use & sign for the range when assess attributes? but not use it when I call the attribute again? is it because the first one is a setter and second one is getter?
Thanks for your advises in advances!
-(NSAttributedString*)characterWithAttribute: (NSString*)attributeName{
NSMutableAttributedString* characters = [[NSMutableAttributedString alloc]init];
int index = 0;
while(index < [self.textToAnalyze length]){
NSRange range;
id value = [self.textToAnalyze attribute:attributeName atIndex:index effectiveRange:&range];
if(value){
[characters appendAttributedString:[self.textToAnalyze attributedSubstringFromRange:range]];
index = (int)range.location + (int)range.length;
}
else{
index++;
}
}
return characters;
}
is it because the first one is a setter and second one is getter?
Basically, yes. The ampersand specifies the address of the variable rather than the value. When the called routine has an address, it can assign a value to it for you to use when it returns. If the called routine only had a value, it wouldn't be able to replace it in any way that was accessible to the caller.
Here's a link that might give you more in-depth details:
https://www.codingunit.com/c-tutorial-call-by-value-or-call-by-reference

Converting a string variable from Binary to Decimal in Objective C

Im trying to create a Binary to Decimal calculator and I am having trouble doing any sort of conversion that will actually work. First off Id like to introduce myself as a complete novice to objective c and to programming in general. As a result many concepts will appear difficult to me, so I am mostly looking for the easiest way to understand and not the most efficient way of doing this.
I have at the moment a calculator that will accept input and display this in a label. This part is working fine and I have no issues with it. The variable that the input is stored on is _display = [[NSMutableString stringWithCapacity:20] retain];
this is working perfectly and I am able to modify the data accordingly. What I would like to do is to be able to display an NSString of the conversion in another label. At the moment I have tried a few solutions and have not had any decent results, this is the latest attempt
- (NSMutableString *)displayValue2:(long long)element
{
_str= [[NSMutableString alloc] initWithString:#""];
if(element > 0){
for(NSInteger numberCopy = element; numberCopy > 0; numberCopy >>= 1)
{
[_str insertString:((numberCopy & 1) ? #"1" : #"0") atIndex:0];
}
}
else if(element == 0)
{
[_str insertString:#"0" atIndex:0];
}
else
{
element = element * (-1);
_str = [self displayValue2:element];
[_str insertString:#"0" atIndex:0];
NSLog(#"Prima for: %#",_str);
for(int i=0; i<[_str length];i++)
_str = _display;
NSLog(#"Dopo for: %#",_str);
}
return _str;
}
Within my View Controller I have a convert button setup, when this is pressed I want to set the second display field to the decimal equivalent. This is working as if I set displayValue2 to return a string of my choosing it works. All I need is help getting this conversion to work. At the moment this bit of code has led to "incomplete implementation" being displayed at the to of my class. Please help, and cheers to those who take time out to help.
So basically all you are really looking for is a way to convert binary numbers into decimal numbers, correct? Another way to think of this problem is changing a number's base from base 2 to base 10. I have used functions like this before in my projects:
+ (NSNumber *)convertBinaryStringToDecimalNumber:(NSString *)binaryString {
NSUInteger totalValue = 0;
for (int i = 0; i < binaryString.length; i++) {
totalValue += (int)([binaryString characterAtIndex:(binaryString.length - 1 - i)] - 48) * pow(2, i);
}
return #(totalValue);
}
Obviously this is accessing the binary as a string representation. This works well since you can easily access each value over a number which is more difficult. You could also easily change the return type from an NSNumber to some string literal. This also works for your element == 0 scenario.
// original number wrapped as a string
NSString *stringValue = [NSString stringWithFormat:#"%d", 11001];
// convert the value and get an NSNumber back
NSNumber *result = [self.class convertBinaryStringToDecinalNumber:stringValue];
// prints 25
NSLog(#"%#", result);
If I misunderstood something please clarify, if you do not understand the code let me know. Also, this may not be the most efficient but it is simple and clean.
I also strongly agree with Hot Licks comment. If you are truly interested in learning well and want to be an developed programmer there are a few basics you should be learning first (I learned with Java and am glad that I did).

What is the most efficient way to move NSArray objects to UITextFields?

I have some code where there may or may not be objects in the Array... this is the code I am dealing with:
oServices1.text = CustomServicesArray[0];
oServices2.text = CustomServicesArray[1];
oServices3.text = CustomServicesArray[2];
oServices4.text = CustomServicesArray[3];
oServices5.text = CustomServicesArray[4];
oServices6.text = CustomServicesArray[5];
oServices7.text = CustomServicesArray[6];
oServices8.text = CustomServicesArray[7];
oServices9.text = CustomServicesArray[8];
oServices10.text = CustomServicesArray[9];
oServices11.text = CustomServicesArray[10];
oServices12.text = CustomServicesArray[11];
oServices13.text = CustomServicesArray[12];
oServices14.text = CustomServicesArray[13];
oServices15.text = CustomServicesArray[14];
oServices16.text = CustomServicesArray[15];
oServices17.text = CustomServicesArray[16];
oServices18.text = CustomServicesArray[17];
oServices19.text = CustomServicesArray[18];
oServices20.text = CustomServicesArray[19];
oServices21.text = CustomServicesArray[20];
oServices22.text = CustomServicesArray[21];
oServices23.text = CustomServicesArray[22];
Rather than check each and every array object for nil, is there a way I can take the oServices*xx*.text UIFields and put them into some kind of array so I can just use a loop?
Are you aware of reflexivity? With KVC you could save up much code and time:
for(int i=1; i<=23; i++) {
NSString* key= [NSString stringWithFormat: #"oServices%d"i];
// Remember that variables should start with a lowercase letter
[[self valueForKey: key] setText: customServicesArray[i-1] ];
}
But if you don't want to bind all these variables in your storyboard/xib file (even this may be too much), just set the tag of each text field in the order that you want (from 1), so that you can get them back using viewWithTag:
// From the UIViewController
for(int i=1; i<=23; i++) { // Consider defining a constant instead of 23
[[self.view viewWithTag: i] setText: customServicesArray[i-1] ];
}
I consider this last solution better because you avoid binding so many variables.
You can use an OutletCollection to hold oServices and loop on that. Note however that outlet collections are not sorted so you would need to sort them beforehand (on the tag criteria, or location for example).
For ordering see this question.
Set the tag property of the UITextFields to their corresponding ordinal in the array. The default value of tag is 0, so you may need to set the tag property to ordinal + 1 if there are other views in the parent view of your UITextFields. On the parent view of your text fields, you can use the viewWithTag: method to retrieve the appropriate UITextField.

Pointer that can reference a UITextView or a UITextField

I want to print a form that contains a mixture of UITextFields and UITextViews, doing exactly the same thing with each one regardless of its actual type. To keep the code clean I'd like to assign each item in the views array to the same variable in order to retrieve its printing parameters. I thought I could do this with a variable of type id, but I haven't hit on anything that will compile. The code below doesn't compile but it shows what I want to do. My thanks to anyone who tells me how to do this correctly.
id theField;
//for each field on the page
for (j = offsetToFirstFormField; j < [self.fields count]; j++) {
theField = [self.fields objectAtIndex: j];
printStr = theField.text;
if ([printStr length] > 0) {
theFont = [theField font];
maxSize = CGSizeMake(theField.frame.size.width, theField.frame.size.height);
printStrSize = [printStr sizeWithFont:theFont constrainedToSize:maxSize lineBreakMode:UILineBreakModeClip];
printRect = CGRectMake((theField.frame.origin.x * xScale) + xOffset, (theField.frame.origin.y * yScale) + yOffset, printStrSize.width, printStrSize.height);
[printStr drawInRect:printRect withFont:theFont];
}
}
You can't use dot-syntax with id variables. You have to stick with message-sending syntax. For example, you have to change this:
printStr = theField.text;
to this:
printStr = [theField text];
If that doesn't fix it, edit your question and paste in the actual error messages you're getting.
Encapsulate your printable functionality in a protocol. So you will be using id<Printable> rather than id. Your protocol will have methods like getText getFont. Or you can use category to extend the existing classes.

Slice NSArray from end of array

What is the best way to "slice" an NSArray from the end, rather than the beginning, of the array (for example, finding the subarray containing the last few elements of a NSArray of unknown length)? In Python, you can use negative indices to accomplish this, e.g.:
new_list = old_list[-5:-3]
What's the most natural way to do this in Objective-C?
There's nothing to match Python's nice syntax for this, but you could do:
NSUInteger count = [myArray count];
NSArray * slice = [myArray subarrayWithRange:(NSRange){count-n, n}];
You could also write up a category for NSArray, something like:
#interface NSArray (jrdioko_slice)
- (NSArray *) jrdioko_sliceFrom:(NSInteger)start to:(NSInteger)stop;
#end
If you want to go this route, the Python source will certainly repay study. A list object creates a slice object when a slice operation is performed. The relevant method on a slice object is PySlice_GetIndicesEx. You'll just have to be careful turning those indexes into an NSRange. As the comment in that function warns "this is harder to get right than you might think". (I'll try to take a crack at this later.)
UPDATE: Here we have a slice category on NSArray. The index calculation logic is pretty much straight out of the Python code that I linked to above.* It's actually a lot easier than I thought at first if you don't have to worry about the stride part of a Python slice. I've run this through a few tests and it seems to work the same as the Python version.
#interface NSArray (WSS_Slice)
- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop;
#end
// Python allows skipping any of the indexes of a slice and supplies default
// values. Skipping an argument to a method is not possible, so (ab)use
// NSNotFound as "not specified" index value. The other way to do this would
// be with varargs, which might be even handier if one decided to implement
// the stride functionality.
enum {
WSS_SliceNoIndex = NSNotFound
};
#implementation NSArray (WSS_Slice)
- (NSArray *)WSS_arrayBySlicingFrom:(NSInteger)start to:(NSInteger)stop {
// There's an important caveat here: specifying the parameters as
// NSInteger allows negative indexes, but limits the method's
// (theoretical) use: the maximum size of an NSArray is NSUIntegerMax,
// which is quite a bit larger than NSIntegerMax.
NSUInteger count = [self count];
// Due to this caveat, bail if the array is too big.
if( count >= NSIntegerMax ) return nil;
// Define default start and stop
NSInteger defaultStart = 0;
NSInteger defaultStop = count;
// Set start to default if not specified
if( start == WSS_SliceNoIndex ){
start = defaultStart;
}
else {
// If start is negative, change it to the correct positive index.
if( start < 0 ) start += count;
// Correct for out-of-bounds index:
// If it's _still_ negative, set it to 0
if( start < 0 ) start = 0;
// If it's past the end, set it to just include the last item
if( start > count ) start = count;
}
// Perform all the same calculations on stop
if( stop == WSS_SliceNoIndex ){
stop = defaultStop;
}
else {
if( stop < 0 ) stop += count;
if( stop < 0 ) stop = 0;
if( stop > count ) stop = count;
}
// Calculate slice length with corrected indexes
NSInteger sliceLength = stop - start;
// If no slice, return a new empty array
if( sliceLength <= 0 ){
return [NSArray array];
}
else {
return [self subarrayWithRange:(NSRange){start, sliceLength}];
}
}
#end
*Therefore I think I need to include a link to the Python License and also note that this may still be “Copyright © 2001-2010 Python Software Foundation; All Rights Reserved”, because although this looks to me like a separately-copyrightable derivative work, I ain't a lawyer.