Format UILabel and UIImages with CoreText - objective-c

i don't worked enougth with core text, but as i thought it's only possible way to do this, but who knows.
So i have a server, that sends me information:
NSString *text = #"hello, today is best day";
NSArray *images; // array of url's from server to images (http://80.89.ru/123.png ....)
// I don't know how many url's there will be
I have a window, in this window i need to correctly draw UILabel with that string and all images, that i get from server with this text.
So i configured UILabel in InterfaceBuilder and config my label with images into ViewController
#interface ViewC()
{
UILabel *longStringFromServer;
}
- (void)viewDidLoad
{
int y = 20; int x = 20;
for (NSString *url in images)
{
UIImageView *view = [UIImageView alloc] initWithFrame....];
y += 50;
// load image by url into UIImageView
}
longStringFromServer = text;
}
What I have:
What I need:
How do i can reach that result on second picture ?

Related

How to colorize the portion left of the knob on a NSSlider

In my project an NSSlider controls the volume of a AVPlayer. I'd like to colorize the portion of the NSSlider left of the knob. How can this be achieved?
You should use NSProgressIndicator for that.
As alternative, you can use a custom NSSliderCell and override - (BOOL)_usesCustomTrackImage to return YES and override - (void)drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped to draw your custom bar. There, you can use [NSCell doubleValue] to get the current position of the slider.
You should subclass NSSliderCell and write something like this:
#interface CustomSliderCell : NSSliderCell {
NSRect _barRect;
NSRect _currentKnobRect;
}
// You should set image for the barFill
// (or not if you want to use the default background)
// And image for the bar before the knob image
#property (strong, nonatomic) NSImage *barFillImage;
#property (strong, nonatomic) NSImage *barFillBeforeKnobImage;
// Slider also has the ages so you should set
// the different images for the left and the right one:
#property (strong, nonatomic) NSImage *barLeftAgeImage;
#property (strong, nonatomic) NSImage *barRightAgeImage;
#end
And implementation:
- (void)drawKnob:(NSRect)knobRect {
[super drawKnob:knobRect];
_currentKnobRect = knobRect;
}
-(void)drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped {
_barRect = cellFrame;
NSRect beforeKnobRect = [self createBeforeKnobRect];
NSRect afterKnobRect = [self createAfterKnobRect];
// Draw bar before the knob
NSDrawThreePartImage(beforeKnobRect, _barLeftAgeImage, _barFillBeforeKnobImage, _barFillBeforeKnobImage,
NO, NSCompositeSourceOver, 1.0, flipped);
// If you want to draw the default background
// add the following line at the at the beginning of the method:
// [super drawBarInside:cellFrame flipped:flipped];
// And comment the next line:
NSDrawThreePartImage(afterKnobRect, _barFillImage, _barFillImage, _barRightAgeImage,
NO, NSCompositeSourceOver, 1.0, flipped);
}
- (NSRect)createBeforeKnobRect {
NSRect beforeKnobRect = _barRect;
beforeKnobRect.size.width = _currentKnobRect.origin.x + _knobImage.size.width / 2;
beforeKnobRect.size.height = _barFillBeforeKnobImage.size.height;
beforeKnobRect.origin.y = beforeKnobRect.size.height / 2;
return beforeKnobRect;
}
- (NSRect)createAfterKnobRect {
NSRect afterKnobRect = _currentKnobRect;
afterKnobRect.origin.x += _knobImage.size.width / 2;
afterKnobRect.size.width = _barRect.size.width - afterKnobRect.origin.x;
afterKnobRect.size.height = _barFillImage.size.height;
afterKnobRect.origin.y = afterKnobRect.size.height / 2;
return afterKnobRect;
}
I've created LADSLider which will help you to
create what you want really simple and fast.

Identify UIImages Individually

I've got an app where multiple UIImages can be added to the view. Those images can then be dragged around the screen. How can I check which image has been dragged and then save that image's coordinates to a file and no other UIImage in the same view. I need a way of tagging each UIImage if possible to separate them out and identify them each individually. Hopefully this makes sense!
This is how I'm adding each UIImage to the view:
CGRect imageFrame = CGRectMake(activeView.center.x - 50, activeView.center.y - 50, 200, 200);
imageResizableView = [[SPUserResizableView alloc] initWithFrame:imageFrame];
UIImage *image = [UIImage imageNamed:#"galaxy.jpg"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageResizableView.contentView = imageView;
imageResizableView.delegate = self;
[activeView addSubview:imageResizableView];
You can use the tag property on UIVIew.
As per mentioned in the official doc:
tag An integer that you can use to identify view objects in your application.
#property(nonatomic) NSInteger tag Discussion The default value is 0. You can set the value of this tag and use that value to identify the view later.
UIImageViews are UIView subclasses, so have a tag property. Set that for each image, then when you want that one image [self.subViews viewWithTag:number]; to find it.
EDIT: If I understand this, there are many UIImageViews, but multiple imageViews may show the same image.
Assuming that, then you partition the tag's 32 bits to say 16 bits upper as the unique imageView number, and the lower 16 are the image number. You will then need to iterate through all subviews looking for the image. You can use macros to make this easier:
#define TAG_NUMBER(imageViewNum, imageNum) ((imageViewNum << 16) | imageNum)
#define IMAGE_FROM_TAG(tag) (tag & 0xFFFF)
etc
when you want to find all imageviews showing that image:
for(UIVIew *view in self.subviews) {
if(![view isKindOf:[UIIMageView class]]) continue;
int imageNum = IMAGE_FROM(view.tag);
if(imageNum == theOneIwant) {
save frame of "view"
}
}
If integer tags are good enough, then use the tag property which already exists for UIViews.
However, if you want something more, you can use obj_setAssociatedObject to add a tagName property to UIView.
#interface UIView (tagName)
#property (nonatomic, copy) NSString *tagName;
#end
--
#import <objc/runtime.h>
#implementation UIView (tagName)
static char tagNameKey[1];
- (NSString*)tagName {
return objc_getAssociatedObject(self, tagNameKey);
}
- (void)setTagName:(NSString *)tagName {
objc_setAssociatedObject(self, tagNameKey, tagName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#end
which can then be used like so...
NSString *imageName = #"galaxy.jpg";
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.tagName = imageName;
and later...
NSString *imageName = imageView.tagName;
EDIT
Of course, you can add whatever you want, for example to mimic viewWithTag
- (UIView*)viewWithTagName:(NSString *)tagName {
if ([self.tagName isEqualToString:tagName]) {
return self;
}
UIView *result = nil;
for (UIView *view in self.subviews) {
if ((result = [view viewWithTagName:tagName]) != nil) {
return result;
}
}
return nil;
}

Using in-memory UIWebView to generate PDF in PhoneGap

I'm trying to work out how to do this.
NOTE: I'm not an experienced objective-c developer (hence why I'm using PhoneGap in the first place)
The short of it: My UIWebView (no, not the PhoneGap one that renders the webapp, a 2nd UIWebView created in-memory and not visible) is not rendering into the PDF. I just get an blank PDF. I'll post some of my thinking and code, and hopefully someone will know what I'm doing wrong.
My starting place is that there is already a print plugin for PhoneGap here:
https://github.com/phonegap/phonegap-plugins/tree/master/iPhone/PrintPlugin
This plugin creates a UIWebView on-the-fly, you pass some HTML to it via JavaScript, and then it calls some print controller to do the printing.
So I borrowed some ideas from that. Then I noticed this awesome blog post on generating PDF's
http://www.ioslearner.com/convert-html-uiwebview-pdf-iphone-ipad/
So I'm trying to combine the two into my own PhoneGap plugin for taking some HTML (from my webapp) and generating a PDF on-the-fly.
HEADER:
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#ifdef PHONEGAP_FRAMEWORK
#import <PhoneGap/PGPlugin.h>
#else
#import "PGPlugin.h"
#endif
#interface ExportPlugin : PGPlugin <UIWebViewDelegate> {
NSString* exportHTML;
}
#property (nonatomic, copy) NSString* exportHTML;
//This gets called from my HTML5 app (Javascript):
- (void) exportPdf:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
#end
MAIN:
#import "ExportPlugin.h"
#interface ExportPlugin (Private)
-(void) doExport;
-(void) drawPdf;
#end
#implementation ExportPlugin
#synthesize exportHTML;
- (void) exportPdf:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options{
NSUInteger argc = [arguments count];
if (argc < 1) {
return;
}
self.exportHTML = [arguments objectAtIndex:0];
[self doExport];
}
int imageName = 0;
double webViewHeight = 0.0;
- (void) doExport{
//Set the base URL to be the www directory.
NSString *dbFilePath = [[NSBundle mainBundle] pathForResource:#"www" ofType:nil ];
NSURL *baseURL = [NSURL fileURLWithPath:dbFilePath];
//Load custom html into a webview
UIWebView *webViewExport = [[UIWebView alloc] init];
webViewExport.delegate = self;
//[webViewExport loadHTMLString:exportHTML baseURL:baseURL];
[webViewExport loadHTMLString:#"<html><body><h1>testing</h1></body></html>" baseURL:baseURL];
}
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webViewExport
{
webViewHeight = [[webViewExport stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"] integerValue];
CGRect screenRect = webViewExport.frame;
//WHY DO I HAVE TO SET THE SIZE? OTHERWISE IT IS 0
screenRect.size.width = 768;
screenRect.size.height = 1024;
double currentWebViewHeight = webViewHeight;
while (currentWebViewHeight > 0)
{
imageName ++;
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
//[[UIColor blackColor] set];
//CGContextFillRect(ctx, screenRect);
[webViewExport.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png",imageName]];
if(currentWebViewHeight < 960)
{
CGRect lastImageRect = CGRectMake(0, 960 - currentWebViewHeight, webViewExport.frame.size.width, currentWebViewHeight);
CGImageRef imageRef = CGImageCreateWithImageInRect([newImage CGImage], lastImageRect);
newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
}
[UIImagePNGRepresentation(newImage) writeToFile:pngPath atomically:YES];
[webViewExport stringByEvaluatingJavaScriptFromString:#"window.scrollBy(0,960);"];
currentWebViewHeight -= 960;
}
[self drawPdf];
}
- (void) drawPdf
{
CGSize pageSize = CGSizeMake(612, webViewHeight);
NSString *fileName = #"Demo.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
// Mark the beginning of a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
double currentHeight = 0.0;
for (int index = 1; index <= imageName ; index++)
{
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", index]];
UIImage *pngImage = [UIImage imageWithContentsOfFile:pngPath];
[pngImage drawInRect:CGRectMake(0, currentHeight, pageSize.width, pngImage.size.height)];
currentHeight += pngImage.size.height;
}
UIGraphicsEndPDFContext();
}
#end
The first indication something is not right, is above I have to set the UIWebView.frame size:
screenRect.size.width = 768;
screenRect.size.height = 1024;
But why? The PhoneGap PrintPlugin doesn't have to do this. If I don't set it, the size is 0, and then I get lots of context errors.
And then the next problem is that the UIWebView is not rendering anything. A symptom of the first problem perhaps?
How do I go about debugging this and working out what the problem is?
UPDATE
I'm pretty sure that it may be impossible to render the UIWebView layer into the image context, unless that UIWebView is actually visible.
I'm not sure how the PhoneGap PrintPlugin works then. It seems to render it's UIWebView quite fine with it not being visible.
I'm currently experimenting with rendering the actual PhoneGap UIWebView into the PDF (as opposed to my own UIWebView). But this is not ideal.
It means I have to hide all toolbars and whatnot, and then pan the UIWebView around so I capture everything outside the viewport. This is not ideal, because the user will visually see this occurring!
Point 1 above doesn't seem to work anyway, because iPad is too slow to update the screen when dynamically fiddling with the layout. On iPad, if you do visual things very quickly, (like panning the screen around) the iPad is too slow and just wont show it. You end up only seeing the end state. So when I take the screenshots, the screen visually hasn't panned (even though the DOM says it has). (Hope that makes sense).
Agh, frustrating.
I've got a working solution now, but it's not ideal.
What I do is render the phonegap UIWebView into the PDF.
To do this is quite tricky. I have a couple of objective-c functions
- (void) takeScreenshot;
- (void) renderPdf;
that I call from Javascript.
Then I have to write a recursive JS algorithm that pans the screen in every direction whilst calling takeScreenshot.
In between calls to takeScreenshot I use setTimeout which gives a 20 millisecond break in the JS processing - enough time for the iPad to update the screen so the next screenshot can be taken.
It was a royal pain in the arse. Bounty is still open in case someone knows of a better way of dealing with this - I would be very curious to know!
If you want to render a UIWebView into a PDF, I think you could go for this :
1/ use the convertRect:fromView method implemented by your UIWebView object to get the CGRect
2/ see the UIPrintPageRenderer Class Reference to make like a print preview
3/ Use UIGraphicsGetCurrentContext to get the CGContextRef out of it
4/ create the PDF from the CGRect and CGContextRef (you can use the help provided in the Apple sample code zoomingPDFViewer for building PDF using CGPdf
Here is how to render PDF in a UIWebView (webPage being your UIWebView), your delegate (here "self") could implement the UIWebViewDelegate protocol :
- (void)loadingPDFwithURL:(NSURL *)anURL {
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
appFrame.origin.y = 0;
[self.webPage initWithFrame:appFrame];
[self.webPage setScalesPageToFit:YES];
[self.webPage setDelegate:self];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:anURL];
[self.webPage loadRequest:requestObj];
[self.view addSubview:self.webPage];
}
So you're giving the URL of the PDF (if it's in memory, just write the file in your application as the URL can be a filepath).
Another possibility is to dive into CGPdf but it's getting harder.

How do I add views to an UIScrollView and keep it synchronized with a UIPageControl?

I need to dynamically create some views in my app and place some buttons on them dynamically again. If the amount (count) of the buttons is more then ten I want to place the buttons on a new view and the transitions between the views must be with UIPageControl. Even though I googled and searched from Apple's Developer page I couldn't find a way of solving my problem. Can someone please help?
Add your views as side-by-side subviews of an UIScrollView, using the addSubview method. Then use the UIPageControl with the UIScrollView, as in this example.
I made a class that manages a UIScrollView, a UIPageControl, and an array of UIViews. It is a simplified version of what I use in my own code. It does the following:
Sets up the scroll view to display an array of UIViews. It doesn't care if the views have been generated dynamically or not.
Handles scroll and page control events.
Synchronizes the scroll view with the page control.
PageViewManager.h
#import <Foundation/Foundation.h>
#interface PageViewManager : NSObject <UIScrollViewDelegate>
{
UIScrollView* scrollView_;
UIPageControl* pageControl_;
NSArray* pages_;
BOOL pageControlUsed_;
NSInteger pageIndex_;
}
- (id)initWithScrollView:(UIScrollView*)scrollView
pageControl:(UIPageControl*)pageControl;
- (void)loadPages:(NSArray*)pages;
- (void)loadControllerViews:(NSArray*)pageControllers;
#end
PageViewManager.m
#import "PageViewManager.h"
#interface PageViewManager ()
- (void)pageControlChanged;
#end
#implementation PageViewManager
- (id)initWithScrollView:(UIScrollView*)scrollView
pageControl:(UIPageControl*)pageControl
{
self = [super init];
if (self)
{
scrollView_ = scrollView;
pageControl_ = pageControl;
pageControlUsed_ = NO;
pageIndex_ = 0;
[pageControl_ addTarget:self action:#selector(pageControlChanged)
forControlEvents:UIControlEventValueChanged];
}
return self;
}
/* Setup the PageViewManager with an array of UIViews. */
- (void)loadPages:(NSArray*)pages
{
pages_ = pages;
scrollView_.delegate = self;
pageControl_.numberOfPages = [pages count];
CGFloat pageWidth = scrollView_.frame.size.width;
CGFloat pageHeight = scrollView_.frame.size.height;
scrollView_.pagingEnabled = YES;
scrollView_.contentSize = CGSizeMake(pageWidth*[pages_ count], pageHeight);
scrollView_.scrollsToTop = NO;
scrollView_.delaysContentTouches = NO;
[pages_ enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop)
{
UIView* page = obj;
page.frame = CGRectMake(pageWidth * index, 0,
pageWidth, pageHeight);
[scrollView_ addSubview:page];
}];
}
/* Setup the PageViewManager with an array of UIViewControllers. */
- (void)loadControllerViews:(NSArray*)pageControllers
{
NSMutableArray* pages = [NSMutableArray arrayWithCapacity:
pageControllers.count];
[pageControllers enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop)
{
UIViewController* controller = obj;
[pages addObject:controller.view];
}];
[self loadPages:pages];
}
- (void)pageControlChanged
{
pageIndex_ = pageControl_.currentPage;
// Set the boolean used when scrolls originate from the page control.
pageControlUsed_ = YES;
// Update the scroll view to the appropriate page
CGFloat pageWidth = scrollView_.frame.size.width;
CGFloat pageHeight = scrollView_.frame.size.height;
CGRect rect = CGRectMake(pageWidth * pageIndex_, 0, pageWidth, pageHeight);
[scrollView_ scrollRectToVisible:rect animated:YES];
}
- (void)scrollViewDidScroll:(UIScrollView*)sender
{
// If the scroll was initiated from the page control, do nothing.
if (!pageControlUsed_)
{
/* Switch the page control when more than 50% of the previous/next
page is visible. */
CGFloat pageWidth = scrollView_.frame.size.width;
CGFloat xOffset = scrollView_.contentOffset.x;
int index = floor((xOffset - pageWidth/2) / pageWidth) + 1;
if (index != pageIndex_)
{
pageIndex_ = index;
pageControl_.currentPage = index;
}
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView
{
pageControlUsed_ = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView
{
pageControlUsed_ = NO;
}
#end
To use this class, you embed it inside a UIViewController than contains the UIScrollView and the UIPageControl.
Usage:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Create some views dynamically
UIView* v1 = ...
UIView* v2 = ...
// Put the views inside an NSArray:
NSArray* pages_ = [NSArray arrayWithObjects:v1, v2, nil];
/* Create the PageViewManager, which is a member (or property) of this
UIViewController. The UIScrollView and UIPageControl belong to this
UIViewController, but we're letting the PageViewManager manage them for us. */
pageViewManager_ = [[PageViewManager alloc]
initWithScrollView:self.scrollView
pageControl:self.pageControl];
// Make the PageViewManager display our array of UIViews on the UIScrollView.
[pageViewManager_ loadViews:pages_];
}
My sample code assumes that you're using ARC.

Objective C: How to create a multi-line UITextField? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to create a multiline UITextfield?
How can I implement a multiple line textfield like what I see in the iPhone messaging app?
It seems like the once the length of input exceeds the length, a second line will be auto created.
EDIT: Updated to clarify my challenges in using a UITextView
For me, I would like to model the feel and look of the UITextView to that as shown below. I am not sure how I can do it with UITextView as suggested. For example
1) How to expand the box dynamically if my text needs to overflow to next line
2) How to add the border and styling as shown below
3) How to attach the text box right above the keyboard (instead of in the view)
I know that Instagram have this as well, if someone can point me to the correct direction, that will be great. Thanks in advnce
Check these answers:
How to create a multiline UITextfield?
How to create a multiline UITextfield?
http://brettschumann.com/blog/2010/01/15/iphone-multiline-textbox-for-sms-style-chat
And definitly try Three20 which is a great library used in many app like Facebook.
Edit: Added extract from BrettSchumann blog
#import <uikit uikit.h="">
#interface MultilineTextboxViewController : UIViewController {
IBOutlet UIView *viewTable;
IBOutlet UIView *viewForm;
IBOutlet UITextView *chatBox;
IBOutlet UIButton *chatButton;
}
#property (nonatomic, retain) UIView *viewTable;
#property (nonatomic, retain) UIView *viewForm;
#property (nonatomic, retain) UITextView *chatBox;
#property (nonatomic, retain) UIButton *chatButton;
- (IBAction)chatButtonClick:(id)sender;
#end
</uikit>
With that done and while we are setting everything up lets go and add our items to our main (.m) file at the same time not forgetting to de-allocate them as well.
#synthesize viewTable;
#synthesize viewForm;
#synthesize chatBox;
#synthesize chatButton;
- (void)dealloc{
[viewTable release];
[viewForm release];
[chatBox release];
[chatButton release];
[super dealloc];
}
In the (void)viewDidLoad method we need to add some notification observers so that we can see when the keyboard is shown or hidden and when the user presses a key on the keyboard.
- (void)viewDidLoad {
//set notification for when keyboard shows/hides
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
//set notification for when a key is pressed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector: #selector(keyPressed:)
name: UITextViewTextDidChangeNotification
object: nil];
//turn off scrolling and set the font details.
chatBox.scrollEnabled = NO;
chatBox.font = [UIFont fontWithName:#"Helvetica" size:14];
[super viewDidLoad];
}
When focus is set on the textview the keyboard will be shown. Because the textview and buttons are both in the lower viewForm on the screen, the keyboard is going to ride up and go over these. So what we want to do is adjust the height of viewTable and the position of viewForm. We do this in the following method.
-(void) keyboardWillShow:(NSNotification *)note{
// get keyboard size and loction
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
// get the height since this is the main value that we need.
NSInteger kbSizeH = keyboardBounds.size.height;
// get a rect for the table/main frame
CGRect tableFrame = viewTable.frame;
tableFrame.size.height -= kbSizeH;
// get a rect for the form frame
CGRect formFrame = viewForm.frame;
formFrame.origin.y -= kbSizeH;
// animations settings
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];
// set views with new info
viewTable.frame = tableFrame;
viewForm.frame = formFrame;
// commit animations
[UIView commitAnimations];
}
Now that the keyboard is shown and our views have been adjusted we want to capture the text the user is entering and see if we need to make any adjustments to the chatBox. Lucky for us we can use the CGSize object, pass it some text, and tell the object what size we would want to constrain the text to and from that we can calculate get height.Now this is where a little trial and error comes in. The line varies with the size of the font and your width of your CGSize object will vary with the width of your UITextview so you will have to experiment a little. The value of 12 use in the code below is the difference in height between the starting height of my chatBox and the line height based on the font that I have set.
-(void) keyPressed: (NSNotification*) notification{
// get the size of the text block so we can work our magic
CGSize newSize = [chatBox.text
sizeWithFont:[UIFont fontWithName:#"Helvetica" size:14]
constrainedToSize:CGSizeMake(222,9999)
lineBreakMode:UILineBreakModeWordWrap];
NSInteger newSizeH = newSize.height;
NSInteger newSizeW = newSize.width;
// I output the new dimensions to the console
// so we can see what is happening
NSLog(#"NEW SIZE : %d X %d", newSizeW, newSizeH);
if (chatBox.hasText)
{
// if the height of our new chatbox is
// below 90 we can set the height
if (newSizeH <= 90)
{
[chatBox scrollRectToVisible:CGRectMake(0,0,1,1) animated:NO];
// chatbox
CGRect chatBoxFrame = chatBox.frame;
NSInteger chatBoxH = chatBoxFrame.size.height;
NSInteger chatBoxW = chatBoxFrame.size.width;
NSLog(#"CHAT BOX SIZE : %d X %d", chatBoxW, chatBoxH);
chatBoxFrame.size.height = newSizeH + 12;
chatBox.frame = chatBoxFrame;
// form view
CGRect formFrame = viewForm.frame;
NSInteger viewFormH = formFrame.size.height;
NSLog(#"FORM VIEW HEIGHT : %d", viewFormH);
formFrame.size.height = 30 + newSizeH;
formFrame.origin.y = 199 - (newSizeH - 18);
viewForm.frame = formFrame;
// table view
CGRect tableFrame = viewTable.frame;
NSInteger viewTableH = tableFrame.size.height;
NSLog(#"TABLE VIEW HEIGHT : %d", viewTableH);
tableFrame.size.height = 199 - (newSizeH - 18);
viewTable.frame = tableFrame;
}
// if our new height is greater than 90
// sets not set the height or move things
// around and enable scrolling
if (newSizeH > 90)
{
chatBox.scrollEnabled = YES;
}
}
}
Once we are and the user presses the send button we want to do something with our text, the keyword will disappear and we want to reset our view back as they were. So how do we do that?
- (IBAction)chatButtonClick:(id)sender{
// hide the keyboard, we are done with it.
[chatBox resignFirstResponder];
chatBox.text = nil;
// chatbox
CGRect chatBoxFrame = chatBox.frame;
chatBoxFrame.size.height = 30;
chatBox.frame = chatBoxFrame;
// form view
CGRect formFrame = viewForm.frame;
formFrame.size.height = 45;
formFrame.origin.y = 415;
viewForm.frame = formFrame;
// table view
CGRect tableFrame = viewTable.frame;
tableFrame.size.height = 415;
viewTable.frame = tableFrame;
}
The resignFirstResponder is going to hide the keyboard and then all we have to do is set the views and chatBox back to their original states.
-(void) keyboardWillHide:(NSNotification *)note{
// get keyboard size and loction
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
// get the height since this is the main value that we need.
NSInteger kbSizeH = keyboardBounds.size.height;
// get a rect for the table/main frame
CGRect tableFrame = viewTable.frame;
tableFrame.size.height += kbSizeH;
// get a rect for the form frame
CGRect formFrame = viewForm.frame;
formFrame.origin.y += kbSizeH;
// animations settings
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];
// set views with new info
viewTable.frame = tableFrame;
viewForm.frame = formFrame;
// commit animations
[UIView commitAnimations];
}
And there you go, a textview that starts off as a single line and grows in size as the user enters text to a max size before becoming a scrolling textview.
UITextField doesn't let you to write more than one line... but you can use UITextView instead, and use the NSString -sizeWithFont function to understand how much space you need.
like this:
CGSize size = [yourTextView.text sizeWithFont:yourTextView.font];
CGRect f = yourTextView.frame;
f.size.height = ceil(size.width/f.size.width)*size.height
yourTextView.frame = f;
I didn't tried this code, but I already used a code like that in the past.
I hope my informations may be useful :)