Objective-C category for styling - ios

What is the best way to create re-usable styling for UI objects for iOS app?
E.g. I want to use the same style for each UITextField - top/bottom line and thinking about creating a category to provide the styling and simply apply it in View Controller.
Is there any better solution to do this?
UITextField+TextFieldStyler.m
- (void)addTopBorderWithColor:(UIColor *)color
{
CGRect topBorderFrame = CGRectMake(0, 0, self.bounds.size.width, 1.0);
UIView *topBorder = [[UIView alloc] initWithFrame:topBorderFrame];
topBorder.backgroundColor = color;
[self addSubview:topBorder];
}
PXRLoginViewController.m
[self.nameFIeld addTopBorderWithColor:[UIColor whiteColor]];
[self.passwordField addTopBorderWithColor:[UIColor whiteColor]];

I believe that the approach you are using is completely fine. It's also used by Apple itself for the same use cases, looking at the doc
Note: Cocoa and Cocoa Touch include a variety of categories for some
of the primary framework classes.
The string-drawing functionality
mentioned in the introduction to this chapter is in fact already
provided for NSString by the NSStringDrawing category for OS X, which
includes the drawAtPoint:withAttributes: and
drawInRect:withAttributes: methods. For iOS, the UIStringDrawing
category includes methods such as drawAtPoint:withFont: and
drawInRect:withFont:.
As you can see, they use the same pattern for extending the drawing behaviour of the NSString.

Related

Basic Request: How to Get Text Input in Cocos2d Application

I have what I thought would be a simple task: create two input fields on a scene and perform the usual input methods on them (touch to set input focus, allow text entry using on-screen keyboard, deselect field when "Done" key is pressed, re-establish input focus if the field is touched/selected again, etc.), basically the usual functionality that you see in most applications that allow user text input.
I have looked through most of the relevant forums for iOS development, especially StackOverflow, and I have only seen incomplete code snippets and vague references, some of which don't even exist anymore.
I thought that this would be a simple task, but everyone seems to think that vague directions are sufficient for someone who just simply wants a straight answer.
Can someone provide a complete description of what needs to be done to accomplish this task?
Thanks.
p.s.: After realizing that I didn't provide complete information, this is the environment and tools that I'm using:
Library Framework: Cocos2D v2
Development Studio: Xcode 5.1
Target Platforms: iOS on iPhone, iPad, and iPod touch devices (port to Android platforms in progress)
Minimum iOS required: 6.1
If you are using Cocos2d v3, use CCTextField for text input. For example:
CCTextField *enterName = [CCTextField textFieldWithSpriteFrame:[CCSpriteFrame frameWithImageNamed:#"textfield_background.png"]];
enterName.fontSize = 16.0f;
enterName.contentSize = CGSizeMake(100.0f, 50.0f);
enterName.preferredSize =CGSizeMake(100.0f, 50.0f);
enterName.positionType = CCPositionTypeNormalized;
enterName.position = ccp(0.5f, 0.5f);
[self addChild:enterName z:5];
If you are using Cocos2d v2, use UIKit components. For example:
CGSize size = [[CCDirector sharedDirector] winSize];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(size.width * 0.5,
size.height* 0.1,
100, 100)];
textField.borderStyle = UITextBorderStyleRoundedRect;
[[[CCDirector sharedDirector] view]addSubview:textField];
As a note, Cocos2d v3 actually uses a UITextField in iOS and a NSTextField in Mac.
#ifdef __CC_PLATFORM_IOS
/** iOS: UITextField used by the CCTextField. */
#property (nonatomic,readonly) UITextField* textField;
#elif defined(__CC_PLATFORM_MAC)
/** Mac: NSTextField used by the CCTextField. */
#property (nonatomic,readonly) NSTextField* textField;
#endif
You can use basic UIView and add it over the cocos2d layers, using this code:
[[[CCDirector sharedDirector] view] addSubview:yourCustomView];

Create multiple UIViews dynamically

I want to create a bar chart using UIView animations. The bar chart will be dynamically created, using data retrieved from my persistent store.
To teach myself how to use UIView animations, I followed this Ray Wenderlich tutorial then built a modified app based on it. As a result, I feel comfortable with the animation aspects--at least as long as it involves discrete (Storyboard-created) views specified by hard-coded data.
However, in the full implementation, the bar chart will have multiple columns, the number, heights, widths, etc. of which will be specified proportionally by live data.
At this point, I've created a UIView in the storyboard, linked to a custom UIView subclass called RiserBar, thinking to create instances of the views (RiserBar *) on the fly. However--and I feel kinda dumb admitting it--I realize I don't really know how to implement this idea. For example, if I'm iterating through an array containing the specifications for several bars, what would that look like? Would each bar have a name? How would those names be generated?
Here's the code I'm thinking about. aBAr is an instance variable. The frame specification, as well as specArray are just dummies, as the exact specification mechanism isn't clear just yet, but the idea is to create as many RiserBar objects as there are entries in specArray:
-(void) makeBarChart
{
RiserBar *aBar;
for (int i=0; i<specArray.count; i++)
{
aBar = [[RiserBar alloc]initWithFrame:CGRectZero];
aBar.backgroundColor = [UIColor redColor];
[self.view addSubview:aBar];
[UIView animateWithDuration:1
delay:.2
options: UIViewAnimationCurveEaseOut
animations:^
{
aBar.frame = CGRectMake(50, self.view.frame.origin.y, startingSpec.width, startingSpec.height);
aBar.frame = CGRectMake(50, self.view.frame.origin.y, endSpec.width, endSpec.height);
}
completion:^(BOOL finished)
{
NSLog(#"Done!");
}];
}
}
Please help me straighten out my thinking about this!
Thanks!

How to add multiple subviews to my iOS view in one line of code? (via "monkeypatching" an `addSubviews` instance method to UIView perhaps?)

lets just say I had just one UILabel subview element & one UITextView subview element inside of a given ViewController.m's viewDidLoad method like so:
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake(20, 140, 280, 40)];
name.text = #"Name: Eric";
UITextView *bio = [[UITextView alloc] initWithFrame:CGRectMake(20, 190, 280, 80)];
bio.text = "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature fro...";
In a normal fashion I could add them one at a time:
[self.view addSubview:name];
[self.view addSubview:bio];
But if there were several dozen or perhaps hundreds of UI components, adding each of them one at time would add dozens to hundreds of lines of code to my project.
Coming from a ruby background, I believe brevity is very important. Is there any way to pass in my UI elements as an array like so?:
[self.view addSubviews:#[name, bio]];
It might be a little early for me to start patching iOS's libraries at boot time but that would definitely be an acceptable solution to me.
Coming from a ruby/rails template of understanding, I'm wondering, is there something equivalent to Rail's /initializers folder in iOS applications? FYI non/pre-rails folks, the /initializers folder in rails is scanned by rails at the beginning of any rails session, and all code inside its .rb files is executed before anything else in rails is loaded aside from the dependencies libraries which are loaded just before that. Thus the ActiveRecord gem (library) for example could then be reinstantiated in an initializer and be amended to at that point. I know Objective C is immutable in a lot of cases though and don't know if that would be a problem.
Existing Cocoa library methods are of course preferred.
In ruby I'd probably patch the method to UIViewController maybe a little something like this:
class UIViewController
def addSubviews(subviews)
subviews.each {|obj| self.view.addSubview(obj) }
end
end
I'd go for a category on UIView.
File --> New --> File --> Objective-C category. Name it something like EasyAdditionOfSubviews, make it a category on UIView.
UIView+EasyAdditionOfSubviews.h
#import <UIKit/UIKit.h>
#interface UIView (EasyAdditionOfSubviews)
- (void)addSubviews:(NSArray *)views;
#end
UIView+EasyAdditionOfSubviews.m
#import "UIView+EasyAdditionOfSubviews.h"
#implementation UIView (EasyAdditionOfSubviews)
- (void)addSubviews:(NSArray *)views
{
for (UIView *view in views) {
if ([view isKindOfClass:[UIView class]]) {
[self addSubview:view];
}
}
}
#end
This way, you just #import UIView+EasyAdditionOfSubviews.h anywhere you want to be able to add an array of views at a time.
And you're better off making sure that what you're passing to addSubview: is indeed a UIView, otherwise you risk a crash (for example, try passing it an NSArray).
But to address your wider concern:
But if there were several dozen or perhaps hundreds of UI components,
adding each of them one at time would add dozens to hundreds of lines
of code to my project.
If you're building a UI programmatically, you want to be exactly sure what, how, and when subviews are added to a view; it's perfectly fine to be explicit when adding a subview to a view.
As a matter of fact, your workaround to not explicitly adding the subview to its superview is... to explicitly add it to an array, which you then add to the superview. Not good for brevity!
Assuming subViews is an array, you could do:
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[superview addSubview:obj];
}];
As #pranav mentions, an alternative is to use the for..in syntax:
for (UIView* view in subViews) {
[superview addSubview:view];
}

iOS: Accessibility Label for CALayer

I want to add accessibilityLabel for some of my CALayer's.
Here is the example:
CALayer *testLayer = [CALayer layer];
[self.view.layer addSublayer:testLayer];
testLayer.backgroundColor = [UIColor purpleColor].CGColor;
testLayer.isAccessibilityElement = YES;
testLayer.accessibilityLabel = #"Some text";
testLayer.frame = CGRectMake(0, 300, 100, 100);
This approach doesn't work for me. Is it possible to make accessibility working for CALayers?
I don't want to use accessibility container in superview (there is complex hierarchy)
Thank you!
AFAIK, A custom view built from CALayers does not have support for accessibility so I guess simple answer to your question would be no! You might want to check Apple's guidelines to create Accessibility for Dynamic Elements.

simplest (rectangular) drop shadow for an UIView

I've seen lots of snippets that either: are too complicated for something as simple as a drop shadow, requiring subclassing UIView and using quartz2d calls, or I can't get them to work.
I just want to do this on a view I'm adding as a subview to another one (the subview is taken from another viewController I'm just allocating - I know that's probably not nice but oh well), no IB or anything. what's the simplest / most accepted way to go about it? would it be different if I want it to work on iOS 4?
It's as easy as importing <QuartzCore/QuartzCore.h> and using a similar snippet as below:
self.viewAboutContainer.layer.shadowColor = [[UIColor blackColor] CGColor];
self.viewAboutContainer.layer.shadowOpacity = 0.7;
self.viewAboutContainer.layer.shadowRadius = 4.0;
self.viewAboutContainer.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
self.viewAboutContainer.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.viewAboutContainer.bounds].CGPath;

Resources