Implementing custom textView - iphone

I want to improve a super cool text view with placeholder and add to it super cool frame like textField has. To do it you simply need to add this code to your awakeFromNib method:
- (void)awakeFromNib
{
[super awakeFromNib];
if (self.editable) {
CALayer *selfLayer = self.layer;
UIImage *stretchableImage = [UIImage imageNamed:#"TextView"];
selfLayer.contents = (id)stretchableImage.CGImage;
selfLayer.contentsScale = [UIScreen mainScreen].scale; // Needed for the retina display, otherwise our image will not be scaled properly.
selfLayer.contentsCenter = CGRectMake(0.5, 0.5, 1.0/stretchableImage.size.width,1.0/stretchableImage.size.height);
self.backgroundColor = [UIColor clearColor];
}
}
The problem is if you add this if() to the above mentioned placeholderTextView's awakeFromNib the drawRect method of the placeholderTextView does not getting called! WHY ? Is it because of accessing layer property of this view? Please guide me through this graphics stuff..!

Related

creating a nice custom UITableViewCell

I am trying to create a table view which has a layout like what yobongo has:
and what I have now is really crappy, where the UIImage has different size, etc, etc...
How do I fix it to have something nice like that? I tried rearranging via IB but then mine looks like this:
I wanted to create a UIImage in the cell that has a fixed size (mine resizes here and there). How do I set that? I also want a rounded edge around the UIIMage... I have played around with the spring and struts via IB and I think I might have messed up something that I can't fix again..
I also want so that there exists a gap between rows and a border like in the picture below
I also wanted to implement a chat box like below where it expands if the text is more than it's limit. How can I do this?
Fixed image size
You have to set UIImageView frame for all image views. And then you have to play with UIImageView's contentMode property - where you can scale image to fit frame, fill frame, keep aspect ratio, etc. And you also have to set clipsToBounds to YES to clip "overlapping" image parts.
Round Corners
You can use CALayer for this, which is also available in UIImageView. It's matter of four lines ...
imageView.layer.cornerRadius = 3.0;
imageView.layer.masksToBounds = YES;
imageView.layer.borderColor = [UIColor blackColor].CGColor;
imageView.layer.borderWidth = 1.0;
Example:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier];
if ( self ) {
...
self.imageView.layer.cornerRadius = 3.0;
self.imageView.layer.masksToBounds = YES;
self.imageView.layer.borderColor = [UIColor blackColor].CGColor;
self.imageView.layer.borderWidth = 1.0;
...
}
return self;
}
Expandable Text Input
You have to prepare good background image for this. And then you can create stretchable image via UIImage class method: – stretchableImageWithLeftCapWidth:topCapHeight:
Each row will be subclassed UITableViewCell where you can handle all these things. Stretchable background, etc. Resizing via UITextView's delegate (textViewDidChange:), etc.
Google for some examples or search SO.
Gaps
UITableViewDelegate has method ...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
... where you can specify row height. To create gap, add this to your custom cell ...
Header:
UIImageView *__backgroundImageView;
Initializer:
__backgroundImageView = [[UIImageView alloc] initWithImage:...stretchableImage...];
[self.contentView addSubview:__backgroundImageView];
[self.contentView sendSubviewToBack:__backgroundImageView];
Layouting:
- (void)layoutSubviews {
[super layoutSubviews];
// This draws background image over the whole cell and adds 5px gap top/bottom
CGRect rect = self.contentView.bounds;
rect.origin.y += 5; // Draw background image 5 pixels below cell top
rect.size.height -= 2 * 5; // Remove top/bottom gap from background image height
__backgroundImageView.frame = rect;
...
}
Memory Management:
- (void)dealloc {
[super dealloc];
[__backgroundImageView release]; __backgroundImage = nil;
...
}
If you want all images (of potentially different source sizes) to appear the same size in your UITableViewCell then you need to adjust the sizes of the images. The easiest way to do that is to use the ScaleToFill contentMode of your UIImageView, which will then do the work for you.
You can get rounded/bordered/colored corners on any view by doing this:
#import <QuartzCore/QuartzCore.h> // for layer.cornerRadius
...
self.comboTextView.layer.cornerRadius = 10.0;
self.comboTextView.layer.borderColor = [[UIColor grayColor] CGColor];
self.comboTextView.layer.borderWidth = 2;
Expanding TextView: I'd probably do this: implement the UITextViewDelegate function
- (void)textViewDidChange:(UITextView *)textView
then in that method get the textView.contentSize property, and set the textView.frame property to that size.
Create custom class that inherits from the UIView. Add ivar UIImageView* _img_v
Override setFrame:
-(void) setFrame:(CGRect) r {
[super setFrame: r];
if( _img_v.image && r.size.width*r.size.height ) {
CGSize isz = _img_v.image.size;
float sx = r.size.width/isz.width;
float sy = r.size.height/isz.height;
if( sx > sy ) {
sx = sy;
} else {
sy = sx;
}
CGRect img_frame = CGRectMake(0, 0, isz.width*sx, isz.height*sy);
img_frame.origin = CGPointMake((r.size.width - img_frame.size.width)/2.0, (r.size.height - img_frame.size.height)/2.0);
[_img_v setFrame: img_frame];
}
}
This won't crop the images, but you can also use scaleToFill for that.

How to deal with background image orientation in UIView

I'm using setting the background image using methodology below. When I rotate my device the background repeats, which make sense because it is not an image. How do I deal with orientation change if this is the way I'm setting my background image?
- (void)viewDidLoad {
[super viewDidLoad];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
self.view.backgroundColor = background;
[background release];
}
It took me awhile to understand this concept. I didn't want to create the same image portrait and landscape. The key here is that CGAffineTransformMakeRotation rotates from the original state of your UIImageView or any UIView for that matter. This assumes your background image has orientation to it. E.g. You want your UIImageView to stay put, while other objects behaves to normal orientation change event.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
backgroundImage.transform = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight){
backgroundImage.transform = CGAffineTransformMakeRotation(-M_PI / 2);
}
else {
backgroundImage.transform = CGAffineTransformMakeRotation(0.0);
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//set the UIImageView to current UIView frame
backgroundImage.frame = self.view.frame;
}
You have to take 2 images both for horizontal and vertical and instead of allocating you can use [...: colorWithPatternImage:...]; and set it when orientation is changed to the background of the view.
hAPPY iCODING...
If I understand correctly, Your background gets created or overwritten every time you change the orientation right. By default backgroundColor is nil. You can check for this, if it is nil then you go ahead and set the values.
Its like
if ( self.view.backgroundColor == nil){
//set the new values here
}

Subclassed UIAlertView not drawn correctly in iOS 4.2

I've subclassed an UIAlertView as follow:
#interface NarrationAlertView : UIAlertView {
UIImage * backgroundImage; //The image I want as custom background
UILabel * textualNarrationView; //The test I wanna be displayed on the view
}
And implemented it this way :
- (id)initNarrationViewWithImage:(UIImage *)image{
if (self = [super init]){
UILabel * alertTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.textualNarrationView = alertTextLabel;
[alertTextLabel release];
[self addSubview:alertTextLabel];
}
return self;
}
- (void)drawRect:(CGRect)rect {
/* Here I draw the image as background */
CGSize imageSize = self.backgroundImage.size;
[self.backgroundImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
}
- (void)layoutSubviews {
/* To fit the text */
[textualNarrationView sizeToFit];
CGRect textRect = textualNarrationView.frame;
textRect.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(textRect)) / 2;
textRect.origin.y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(textRect)) / 2;
textRect.origin.y -= 70;
textualNarrationView.frame = textRect;
}
- (void)show{
/* Showing the view */
[super show];
CGSize imageSize = self.backgroundImage.size;
self.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
}
On the previous versions of iOS (I'm always testing on the simulator) the subclass run fine and when shown it displayed the custom background image drawn correctly and just text upon it, whereas in the 4.2 version it draws the UIAlertView classic background (the blue rounded corners rectangle) on top of my image.
What am I doing wrong? Any suggestion about UIAlertView programming and UIView too will be appreciated.
UPDATE Has anyone got some UIAlertView replacement class to share?
We had a similar problem with UIAlertView in iOS 4.2; we were customizing the layout, added text boxes and rearranging the buttons.
This won't be a popular answer, but due to the changes to UIAlertView, we had to abandon using it entirely for this purpose. We always knew it was a fragile implementation since customizing/subclassing UIAlertView isn't officially supported and makes assumptions about the internal structure of the view hierarchy, but the release of 4.2 really kicked us into gear.
In the end, we implemented our own UI element to replace the customized UIAlertView.
Prior to iOS 4.2 UIAlertView's standard dark blue rounded rectangle was drawn in drawRect. the rounded rectangle could be removed by subclassing UIAlertView and implementing drawRect without calling super. however in 4.2 the rounded rectangle is a UIImageView subview.
the quick, easy (not best) solution: If you are not adding any UIImageView instances to your UIAlertView subclass you can simply remove the default UIImageView that is loaded by observing subview additions:
- (void)didAddSubview:(UIView *)subview {
if ([subview isMemberOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
}
}
I got burned by this too. I ended up writing a replacement class, which I'm sharing on github:
https://github.com/TomSwift/TSAlertView

stretchableImageWithLeftCapWidth:topCapHeight doesn't work in initWithCoder: of UIImageView subclass

I have a UIImageView subclass called ShadowView, which displays a shadow that can be used under anything. ShadowViews are to be loaded from a nib.
In initWithCoder:, I have the following code:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self != nil) {
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
[self setContentMode:UIViewContentModeScaleToFill];
[self setImage:shadowImage];
}
return self;
}
When I run the app, though, this image does not appear.
But if I change it to
...
UIImage *shadowImage = [UIImage imageNamed:#"drop_shadow_4_pix.png"];
...
it works fine, but it is stretched wrong.
Any ideas as to why this is happening?
Edit: it is the same when I load the shadowview programmatically, with initWithFrame: implemented similarly to initWithCoder:.
Another Edit: I think I solved the problem. I needed to set the autoresizing masks.
Is shadowImage nil?
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
That method could return nil if the base image is less than 5 pixels wide or 5 pixels tall since it needs the 4 pixels for the caps + 1 pixel to stretch.

UIImageView subclass does not display

I am using a custom subclass of UIImageView, but I can't figure out why it's not being displayed.
The relevant code from my UIImageView subclass:
-(id) initWithImage:(UIImage*)image{
if(self = [super initWithImage:image]){
}
return self;
}
And from the view controller for the view that will be displaying my subclass:
UIImage *zoomRatateSliderHorizontal =
[[UIImage imageNamed:#"ZoomRotate Slider Horizontal.png"]
stretchableImageWithLeftCapWidth:(75.0)/2-1.0 topCapHeight:0];
scaleHorizontalControl = [[ViewTransformationController alloc]
initWithImage:zoomRatateSliderHorizontal];
scaleHorizontalControl.onScreenFrame = CGRectMake(0.0, 5.0,
self.view.frame.size.width,
scaleHorizontalControl.image.size.height);
scaleHorizontalControl.frame = scaleHorizontalControl.onScreenFrame;
scaleHorizontalControl.offScreenFrame = CGRectZero;
[self.view addSubview:scaleHorizontalControl];
Before I was subclassing, I had no trouble with the following in the view controller:
UIImage *zoomRatateSliderVertical =
[[UIImage imageNamed:#"ZoomRotate Slider Vertical.png"]
stretchableImageWithLeftCapWidth:0 topCapHeight:(75.0)/2-1.0];
scaleOrRatateViewVertical = [[UIImageView alloc]
initWithImage:zoomRatateSliderVertical];
scaleOrRatateViewVertical.frame = CGRectMake(-zoomRatateSliderVertical.size.width,
zoomRatateSliderHorizontal.size.height+5.0+5.0,
zoomRatateSliderVertical.size.width,
465.0 - zoomRatateSliderHorizontal.size.height-10.0-5.0);
[self.view addSubview:scaleOrRatateViewVertical];
Using break points I checked the frame and image being passe to my class, and they both appear to be valid and desired.
Any advice on what I'm doing wrong I'd greatly appreciate.
The Apple Docs read:
The UIImageView class is optimized to draw its images to the display. UIImageView will not call drawRect: a subclass. If your subclass needs custom drawing code, it is recommended you use UIView as the base class.
So I'd subclass UIView and work from there.