view as property of model object alternative - ios

I'm building an arithmetic app & in it there are subclasses of NSObject for Numbers and Digits.Both of these have corresponding view objects which take a Datasource (either number or digit) and a delegate, the view controller.
I have found that it is useful in getting the views & the model to work together to set the digit views as a property of their corresponding digits.
For example, the Number class has an NSMutableArray property that holds its digits.
If I want to find the size for the corresponding NumberView, I write can write code like this in the controller:
-(void) updateNumberViewFrameSize:(ACNumberView*) sender
{
NSUInteger i;
float width = 0, height = 0;
for (ACDigit* digit in [sender.dataSource returnNumberViewDataSource].digitArray)
{
width += digit.digitView.size.width;
height += digit.digitView.size.width;
}
sender.frame = CGRectMake(sender.frame.origin.x, sender.frame.origin.y, width, height);
}
The code works just fine, but I feel that it is not good practice to hold that pointer to the view from the model, even if the model isn't using it itself.
If it is bad practice, what are the potential pitfalls, and Is there a better way to achieve this type end ?

First: You are right. This is no good design.
Second: You calculate the size of a number view inside the model. But a number view should know its size itself. It knows the number through its data source and can get the digits. So it has all information for calculating its size.
To show the problem, just imagine (even this situation is on iOS not that common), that you display the same number at two places (= with to different number views). This would break your model. Why?
Solution: Put all code related to a graphic state (drawing, size, …) into the number view and digit view. On half of the way that will be additional work. But at the end, when every code is migrated to the view layer, it is as easy as computing it inside the model.

Related

How do I create a calculator that with one input populates several label fields with different values?

I am new to using Xcode, slowly learning as I go. Attempting to create a calculator where I input a weight which will cause multiple different labels to display different values.
StoryBoard
This is how I have the storyboard setup. Basically behind the scenes I need the values calculated for each item. For example, I want to take the user input ("weight in kilograms" x 80) / 24 and have the result show up on the label to the right of "Fluid Rate". At the same time I will need to have a different calculation for each label.
Here is my code so far.
ViewController
One of the big issues I'm running into so far is that even when I set a value like, let fluid = 80, when I try to setup a calculation with it, Xcode tells me I cannot use a textfield with integers or binary operators. How do I go about making Xcode recognize the user input from the textfield as an integer?
I go through the problem you are facing I am giving the sample code snippet which might be helpful to you.
var fluidRate = 0
var GIR = 0
var enteredValue = 10
{
didSet {
fluidRate = enteredValue * 10 //consider your formula calculations
GIR = enteredValue * 50 //consider your formula calculations
//you can do like that
//self.fluidlabel.text = "\(fluidRate)"
}
}
enteredValue = 30
GIR
fluidRate
How do I go about making Xcode recognize the user input from the textfield as an integer?
Read the section Converting Strings in the documentation for Int.
That will get you from text to integer and it looks like you already know how to go the other way
HTH

Fitting multi-line text into a dynamically size-changing node

A multiline auto typing text box class (which uses an SKNode as the parent) is created using basically 2 elements:
an SKSpriteNode that acts as text box frame & background image/texture holder.
an NSMutableArray containing a set limited amount (rows) of NSStrings that each have a set character length.
After modifying this text box class so that it can be initialized with any frame width & height, I realized I didn't program the NSMutableArray to automatically change its content in a such way that it nicely fits within the background node (with a bit of padding involved as well). So here I am wondering how to do that since NSString's can only return the character count and not the width & height of each string in points (points could have maybe helped me create character constraints in some way).
Right now, the NSMutableArray uses a hardcoded maximum character count per NSString & a maximum row count for the entire array (it's 5 rows right now and when that limit is reached, a new "page"/array is created). This forces me to manually re-adjust these parameters every time I change the background node frame size which defeats the purpose of the class allowing the background frame to change.
Thing is, I'm trying to solve this in such a way that when I post this class on github, I want the solution to take into consideration any fontName & fontSize.
What are my options for solving this problem?
I've done something similar to this. It doesn't work 100% as to what you want, but should be similar enough. It uses a root node and from there, it will build multi-line text using an array of NSString which will in turn be used to build the SKLabelNode.
I'll outline what I did. I should also say I only run this when new text is set. In other words, I do not incur the penalty of deriving the information every frame. Only once.
The generalized steps are:
You will iterate over each character in the text string. Note I do this because my code supports word wrapping as well as other alignment capabilities. So for me, I want that level of control. As this is being done only upon creation, I'm fine with the overhead. If you don't want to word wrap you could always just create an array of words and work from there.
As you iterate over each character, you'll be generating an array of lines. Where each line in the array is a line that will fit in your frame. For now let's not worry about vertical constraints. So here we are primarily worried about width. For the current line, each character you are iterating over will get added to the current line. For this potential line string, you will use NSString's sizeWithAttributes, which is configured for your font. For example in my code it is an NSDictionary which contains: NSFontAttributeName : [UIFont fontWithName:self.fontName size:self.size]. This will be used to check the width, if that width exceeds the frame width, you are overrunning the line.
So the code may look something like:
size = [line sizeWithAttributes:attributes];
if (size.width > maxTextWidth) {
needNewline = YES;
}
If you have overrun a line, you need to determine if you are word wrapping. If you are, you can just add the current line (minus one character) to the lines array. If not you have prune off the last word in the current line and then add that to the array of lines.
The tricky parts are dealing with whitespace and handling non-word wrapped overflow. I have not addressed whitespace but you need to consider this very much in your code. Additionally, you also do want to factor in leading pixels, etc.
Once you have your array of lines, you can then create your children SKLabelNodes. I add them to the root, which allows me to move the group anywhere it needs to be.
The real key here is the lines array generation.

Computing text size with NSLayoutManager

I'm working on a view which uses TextKit framework to typeset text in columns like this:
I use my UIView's bounds with edge insets (black rectangle) to compute 10 CGRects which I then transform into NSTextContainers (red rectangles). In drawRect: I pass those to the NSLayoutManager which typesets and draws the glyphs for me.
My question is: How can I compute the number of columns required? I can draw a constant number of columns but with varying text lengths, I need to adjust the number of columns programmatically.
I found the method [NSLayoutManager glyphRangeForTextContainer:] which returns range of length 0 when the text container is to be left empty. Therefore, I could loop to create text containers and use this method to determine if more containers are needed. However, this method is said to be inefficient as it triggers the layout computation and I'm not happy running it in a loop perhaps hundreds of times over.
There has to be a better way!
Thanks for your answers, Pete.
Well, after some digging through the TextKit framework I've finally found the answer.
My code works in a loop like this:
while ([self needsMoreColumns]) {
[self addColumn];
}
...
- (BOOL)needsMoreColumns {
// Always create at least one column
if (self.layoutManager.textContainers.count == 0)
return YES;
// Find out the glyph range of the last column
NSRange range = [self.layoutManager glyphRangeForTextContainer:[self.layoutManager.textContainers lastObject]];
NSUInteger glyphs = [self.layoutManager numberOfGlyphs];
// Compare it with the number of glyphs
return range.location + range.length < glyphs;
}
I didn't include the method [self addColumn] as it's a no brainer. It simply uses the geometry of my layout and position of the last column (if any) to compute the CGRect of the next one. Then, it creates NSTextContainer with respective size and stores the origin property of the rectangle in a dedicated array for drawing purposes.
I've also discovered methods [NSLayoutManager firstUnlaidCharacterIndex] and [NSLayoutManager firstUnlaidGlyphIndex] but they don't seem to work as expected. After laying out three columns worth of text in only one column, they returned the length of the entire string and not the position of the first character which didn't fit into the first column. That's why I rather used the range-based approach.
That's all folks, be safe!
Pete.

Any advantage of using 'self.view.frame.size.height', over storing it in a variable instead?

Suppose, an app has a view controller with a plethora of views inside, whose frames are relative to the view controller's frame.
Is there any advantage/disadvantage of using self.view.frame.size.height/width, over storing their values in variables, and using them instead ?? Advantages/disadvantages in terms of memory usage, CPU usage, time-delay, et cetera.
And, in the core of it, are these values of height, width, or related parameters stored in memory somewhere, or are they fetched/calculated every time the code requests them ??
I'll suggest storing the value in a variable and using that will be better.
If you call self.view.frame.size.height it will be calculated each time. They'll affect the performance.
doing:
int counter = self.view.frame.size.width;
for(int loop=0;loop<counter;loop++)
{
}
is better than:
for(int loop=0;loop<self.view.frame.size.width;loop++)
{
}
In certain scenarios, if the width and height is changing dynamically(animation logic), and you need to do something based on the values then using self.view.frame.size.width will be a good thing.
Advantages:
When views are resized, the width and height variable automatically gets updated with resized values. Storing local variables for width and height will make you write unncecessary code to manage the updation of both on each possible event causing the view to resize.
With your local variable a duplicate will increase in memory. Though the magnitude of memory waste is negligible, there is some extra memory used to hold your duplicate variable is always a fact.
Disadvantages:
If you refer the width and height from the view.frame.size.height itself, then you may loose the initial value.
I just want to point out that self.view.frame.size.height = 1 will not work as many expected. It is same as CGRect temp = self.view.frame; temp.size.height = 1;
So always separate setter/getter call and structure accessors in different line. i.e.
CGRect frame = self.view.frame; // use objc dot syntax to get frame
frame.size.height = 100; // use C struct accessors
self.view.frame = frame; // use dot syntax to set frame

Boardgame-Map with crossroads etc

I have a little logical problem over here.
As the title says, I try to build a boardgame as a computer-program (maybe with internet-support, but thats another story)
As for now, I have a map, which has some crossroads in it, hence I cannot simply define the fields as '1, 2, 3, 4, ...' because if there is a crossroad at field 10, I would have more than one field which has to be labeled 11 (Because then there is a field left and right of field 10, for example.)
So the problem is, if I cannot define the Board in numbers then I cannot simply get the possible positions a player can take when he rolls 2d6-dices with calculating 'Field-Nr. + RandomRange(1,6) + RandomRange(1,6)'
Does anybody have an idea, how to define a Map like this on another way, where I still can calculate the possible new-fields for Player X with a 2d6-dice-roll?
Thanks in advance.
If i understand well... (i don't thing so) this might help you. Just use dynamic arrays for your boardgame field and change your actions after the dimensions x,y .... Look at this "type Name = array of {array of ...} Base type; // Dynamic array"
It sounds like you have a graph of connected vertices. When a player is at a particular vertex of N edges, assuming N < 12, the new Field will be reached from traversing edge number N % ( rand(6) + rand(6) ).
You could also just do rand(12), but that would have an even distribution, unlike 2d6.
Instead of dynamic arrays, I would recommend using a linked-list of records to describe the surrounding cells, and traverse the player's location and possible moves using that linked-list.
First, define a record that describes each cell in your board's playable grid (the cells on the grid can be four-sided like a chessboard, or hexagonal like in Civilization V) ... each cell record should contain info such as coordinates, which players are also in that cell, any rewards/hazards/etc that would affect gameplay, etc. (you get the idea).
Finally, the linked-list joins all of these cells, effectively pointing to any connected cells. That way, all you'd need is the cell location of Player X and calculate possible moves over n amount of cells (determined by the dice roll), traversing the adjoining cells (that don't have hazards, for example).
If all you want is to track the possible roads, you can also use this approach to identify possible paths (instead of cells) Player X can travel on.

Resources