I'm trying to put a UITextView inside a custom subclass of UIAlertView that I create using a background image of mine. The UITextView is only for displaying large quantities of text (so the scrolling).
Here is the code of my subclass
- (id)initNarrationViewWithImage:(UIImage *)image text:(NSString *)text{
if (self = [super init]) {
self.backgroundImage = image;
UITextView * textView = [[UITextView alloc] initWithFrame:CGRectZero];
self.textualNarrationView = textView;
[textView release];
self.textualNarrationView.text = text;
self.textualNarrationView.backgroundColor = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0];
self.textualNarrationView.opaque = YES;
[self addSubview:textualNarrationView];
}
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"DRAU");
CGSize imageSize = self.backgroundImage.size;
[self.backgroundImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
}
- (void)layoutSubviews {
CGSize imageSize = self.backgroundImage.size;
self.textualNarrationView.bounds = CGRectMake(0, 0, imageSize.width - 20, imageSize.height - 20);
self.textualNarrationView.center = CGPointMake(320/2, 480/2);
}
- (void)show{
[super show];
NSLog(#"SCIO");
CGSize imageSize = self.backgroundImage.size;
self.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
self.center = CGPointMake(320/2, 480/2);
}
This is my first time subclassing a UIView, I'm surely missing something since the background image appears correctly, the UITextview also but after a fraction of a second it jumps all up (seems like a coordinate problem).
I've finally created my own customized version of UIAlertView and provided a UITextView inside of it. Here is what brought me to do so.
By doing it I experienced a weird behaviour when I was trying to attach the UITextView to a background UIImageView as a subview, it wasn't responsive to touches anymore, the way to make it work as expected was to attach it to the base view and implement the following method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
by making it return YES when encessary.
Related
I am creating a custom UISlider and wonder, how do you increase a thumb image's "click area" ? The code below works great, except the thumb's "click area" remains unchanged. If I am not wrong, I believe the standard thumb area is 19 px ? Lets say I would like to increase it to 35px with the following code below, what steps do I need to take? Keep in mind I am not an expert within this area at all.
EDIT The code below is corrected and ready for copy pasta! Hope it helps you!
main.h
#import "MyUISlider.h"
#interface main : MLUIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UIActionSheetDelegate, UIAlertViewDelegate, MFMailComposeViewControllerDelegate, UIGestureRecognizerDelegate, MLSearchTaskDelegate, MLDeactivateDelegate, MLReceiptDelegate>
{
..........
}
- (void)create_Custom_UISlider;
main.m
- (void)viewDidLoad
{
[self create_Custom_UISlider];
[self.view addSubview: customSlider];
}
- (void)create_Custom_UISlider
{
CGRect frame = CGRectMake(20.0, 320.0, 280, 0.0);
customSlider = [[MyUISlider alloc] initWithFrame:frame];
[customSlider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
// in case the parent view draws with a custom color or gradient, use a transparent color
customSlider.backgroundColor = [UIColor clearColor];
UIImage *stetchLeftTrack = [[UIImage imageNamed:#"blue_slider08.png"]
stretchableImageWithLeftCapWidth: 10.0 topCapHeight:0.0];
UIImage *stetchRightTrack = [[UIImage imageNamed:#"blue_slider08.png"]
stretchableImageWithLeftCapWidth: 260.0 topCapHeight:0.0];
[customSlider setThumbImage: [UIImage imageNamed:#"slider_button00"] forState:UIControlStateNormal];
[customSlider setMinimumTrackImage:stetchLeftTrack forState:UIControlStateNormal];
[customSlider setMaximumTrackImage:stetchRightTrack forState:UIControlStateNormal];
customSlider.minimumValue = 0.0;
customSlider.maximumValue = 1.0;
customSlider.continuous = NO;
customSlider.value = 0.00;
}
-(void) sliderAction: (UISlider *) sender{
if(sender.value !=1.0){
[sender setValue: 0.00 animated: YES];
}
else{
[sender setValue 0.00 animated: YES];
// add some code here depending on what you want to do!
}
}
EDIT (trying to subclass with the code below)
MyUISlider.h
#import <UIKit/UIKit.h>
#interface MyUISlider : UISlider {
}
#end
MyUISlider.m
#import "MyUISlider.h"
#implementation MyUISlider
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
return CGRectInset ([super thumbRectForBounds:bounds trackRect:rect value:value], 10 , 10);
}
#end
You have to override thumbRectForBounds:trackRect:value: in a custom subclass (instead of calling the method yourself like you do in your code, which does nothing except returning a value that you don't even bother to retrieve).
thumbRectForBounds:trackRect:value: is not a method to set the tumb rect, but to get it. The slider will internally call this thumbRectForBounds:trackRect:value: method to know where to draw the thumb image, so you need to override it — as explained in the documentation — to return the CGRect you want (so that it will be 35x35px as you wish).
Updated for Swift 3.0 and iOS 10
private func generateHandleImage(with color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: self.bounds.size.height + 20, height: self.bounds.size.height + 20)
return UIGraphicsImageRenderer(size: rect.size).image { (imageContext) in
imageContext.cgContext.setFillColor(color.cgColor)
imageContext.cgContext.fill(rect.insetBy(dx: 10, dy: 10))
}
}
Calling:
self.sliderView.setThumbImage(self.generateHandleImage(with: .white), for: .normal)
Original answer:
By far the easiest method is just to increase the size of the thumb image with invisible border around it:
- (UIImage *)generateHandleImageWithColor:(UIColor *)color
{
CGRect rect = CGRectMake(0.0f, 0.0f, self.bounds.size.height + 20.f, self.bounds.size.height + 20.f);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, CGRectInset(rect, 10.f, 10.f));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
And than adding this image to slider:
[self.sliderView setThumbImage:[self generateHandleImageWithColor:[UIColor redColor]] forState:UIControlStateNormal];
I also had this problem and solved it by overriding
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
method.
You can return YES for those points, that you want to be touched.
You can check my double slider project
In summary, I implemented the hit test method so the beginTrackingWithTouch is invoked.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(self.leftHandler.frame, point) || CGRectContainsPoint(self.rightHandler.frame, point)) {
return self;
}
return [super hitTest:point withEvent:event];
}
Check the class for further details
why you wanna go for custom slider while you can change the area of a default slider ;)
take a look at this code it worked for me to increase the touch area of my UISlider hope it helps you too
CGRect sliderFrame = yourSlider.frame;
sliderFrame.size.height = 70.0;
[yourSlider setFrame:sliderFrame];
I am creating a custom UISlider and wonder, how do you increase a thumb image's "click area" ? The code below works great, except the thumb's "click area" remains unchanged. If I am not wrong, I believe the standard thumb area is 19 px ? Lets say I would like to increase it to 35px with the following code below, what steps do I need to take? Keep in mind I am not an expert within this area at all.
EDIT The code below is corrected and ready for copy pasta! Hope it helps you!
main.h
#import "MyUISlider.h"
#interface main : MLUIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UIActionSheetDelegate, UIAlertViewDelegate, MFMailComposeViewControllerDelegate, UIGestureRecognizerDelegate, MLSearchTaskDelegate, MLDeactivateDelegate, MLReceiptDelegate>
{
..........
}
- (void)create_Custom_UISlider;
main.m
- (void)viewDidLoad
{
[self create_Custom_UISlider];
[self.view addSubview: customSlider];
}
- (void)create_Custom_UISlider
{
CGRect frame = CGRectMake(20.0, 320.0, 280, 0.0);
customSlider = [[MyUISlider alloc] initWithFrame:frame];
[customSlider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
// in case the parent view draws with a custom color or gradient, use a transparent color
customSlider.backgroundColor = [UIColor clearColor];
UIImage *stetchLeftTrack = [[UIImage imageNamed:#"blue_slider08.png"]
stretchableImageWithLeftCapWidth: 10.0 topCapHeight:0.0];
UIImage *stetchRightTrack = [[UIImage imageNamed:#"blue_slider08.png"]
stretchableImageWithLeftCapWidth: 260.0 topCapHeight:0.0];
[customSlider setThumbImage: [UIImage imageNamed:#"slider_button00"] forState:UIControlStateNormal];
[customSlider setMinimumTrackImage:stetchLeftTrack forState:UIControlStateNormal];
[customSlider setMaximumTrackImage:stetchRightTrack forState:UIControlStateNormal];
customSlider.minimumValue = 0.0;
customSlider.maximumValue = 1.0;
customSlider.continuous = NO;
customSlider.value = 0.00;
}
-(void) sliderAction: (UISlider *) sender{
if(sender.value !=1.0){
[sender setValue: 0.00 animated: YES];
}
else{
[sender setValue 0.00 animated: YES];
// add some code here depending on what you want to do!
}
}
EDIT (trying to subclass with the code below)
MyUISlider.h
#import <UIKit/UIKit.h>
#interface MyUISlider : UISlider {
}
#end
MyUISlider.m
#import "MyUISlider.h"
#implementation MyUISlider
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
return CGRectInset ([super thumbRectForBounds:bounds trackRect:rect value:value], 10 , 10);
}
#end
You have to override thumbRectForBounds:trackRect:value: in a custom subclass (instead of calling the method yourself like you do in your code, which does nothing except returning a value that you don't even bother to retrieve).
thumbRectForBounds:trackRect:value: is not a method to set the tumb rect, but to get it. The slider will internally call this thumbRectForBounds:trackRect:value: method to know where to draw the thumb image, so you need to override it — as explained in the documentation — to return the CGRect you want (so that it will be 35x35px as you wish).
Updated for Swift 3.0 and iOS 10
private func generateHandleImage(with color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: self.bounds.size.height + 20, height: self.bounds.size.height + 20)
return UIGraphicsImageRenderer(size: rect.size).image { (imageContext) in
imageContext.cgContext.setFillColor(color.cgColor)
imageContext.cgContext.fill(rect.insetBy(dx: 10, dy: 10))
}
}
Calling:
self.sliderView.setThumbImage(self.generateHandleImage(with: .white), for: .normal)
Original answer:
By far the easiest method is just to increase the size of the thumb image with invisible border around it:
- (UIImage *)generateHandleImageWithColor:(UIColor *)color
{
CGRect rect = CGRectMake(0.0f, 0.0f, self.bounds.size.height + 20.f, self.bounds.size.height + 20.f);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, CGRectInset(rect, 10.f, 10.f));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
And than adding this image to slider:
[self.sliderView setThumbImage:[self generateHandleImageWithColor:[UIColor redColor]] forState:UIControlStateNormal];
I also had this problem and solved it by overriding
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
method.
You can return YES for those points, that you want to be touched.
You can check my double slider project
In summary, I implemented the hit test method so the beginTrackingWithTouch is invoked.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(self.leftHandler.frame, point) || CGRectContainsPoint(self.rightHandler.frame, point)) {
return self;
}
return [super hitTest:point withEvent:event];
}
Check the class for further details
why you wanna go for custom slider while you can change the area of a default slider ;)
take a look at this code it worked for me to increase the touch area of my UISlider hope it helps you too
CGRect sliderFrame = yourSlider.frame;
sliderFrame.size.height = 70.0;
[yourSlider setFrame:sliderFrame];
I already have a custom UIAlertView and in which i want to add UIImageView for adding custom UIAlertView.
I have already added UIImageView with array and start animating it but it doesn't work. If you want, I can post the code.
Please help me ASAP. I'm stuck :(
Here is code
- (id)initWithImage:(UIImage *)image text:(NSString *)text
{
//CGSize imageSize = self.backgroundImage.size;
CGRect frame = [[UIScreen mainScreen] bounds];
width = frame.size.width;
height = frame.size.height;
if (self = [super init])
{
loadtext = [[UILabel alloc]initWithFrame:CGRectZero];
loadtext.textColor = [UIColor blackColor];
loadtext.backgroundColor = [UIColor whiteColor];
[self addSubview:loadtext];
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"status1.png"];
activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"status1.png"],
[UIImage imageNamed:#"status2.png"],
[UIImage imageNamed:#"status3.png"],
[UIImage imageNamed:#"status4.png"],
[UIImage imageNamed:#"status5.png"],
[UIImage imageNamed:#"status6.png"],
[UIImage imageNamed:#"status7.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 1.0;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectZero;
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self addSubview:activityImageView];
//self.backgroundImage = image;
}
return self;
}
- (void) layoutSubviews{
//lblUserName.transform = CGAffineTransformMake;
//[lblUserName sizeToFit];
CGRect textRect = activityImageView.frame;
textRect.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(textRect))/2;
textRect.origin.y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(textRect))/2;
textRect.origin.x -= 100.0;
textRect.origin.y -= 60.0;
textRect.size.height = 30;
textRect.size.width = self.bounds.size.width - 50;
activityImageView.frame = textRect;
}
This code started working. But now this UIImageView is taking my whole screen and coming in front of UIAlertView. Though i give 10px height and width. It shows same. Can anyone help please?
Thanks,
Anks
Yes, you can add image view - in custom alert-view. instead of taking as UIView custom class, need to take UIImage-View & you can use image-view properties.
Change activityImageView.frame = CGRectZero;
to some custom frame.
eg:
activityImageView.frame = CGRectMake(0,0,100,100);
I have an app that displays custom maps. I use a CATiledView to display the maps.
I would like to be able to draw a route over the top of the maps. To do this, I am creating a UIView then adding it to the scrollView after I add the tiling layer like this:
- (void)displayTiledImageNamed:(NSString *)imageName size:(CGSize)imageSize
{
// clear the previous imageView
[imageView removeFromSuperview];
[imageView release];
imageView = nil;
[linesView removeFromSuperview];
[linesView release];
linesView = nil;
// reset our zoomScale to 1.0 before doing any further calculations
self.zoomScale = 1.0;
// make a new TilingView for the new image
imageView = [[TilingView alloc] initWithImageName:imageName size:imageSize];
linesView = [[LinesView alloc]initWithImageName:imageName size:imageSize];
linesView.backgroundColor = [UIColor clearColor];
[self addSubview:imageView];
[self addSubview:linesView];
[self configureForImageSize:imageSize];
}
The problem, is the line that I create in linesView is not scaling correctly.
It's hard to describe, but the line that is being created is scaled as if it were drawn on the device itself, rather than drawn on the map. See the following code:
#import "LinesView.h"
#implementation LinesView
#synthesize imageName;
- (id)initWithImageName:(NSString *)name size:(CGSize)size
{
if ((self = [super initWithFrame:CGRectMake(0, 0, size.width, size.height)])) {
self.imageName = name;
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
CGContextSetLineWidth(context, 20.0);
CGContextMoveToPoint(context, 1.0f, 220.0f);
CGContextAddLineToPoint(context, 340.0f, 80);
CGContextStrokePath(context);
}
#end
I have tried putting the code to draw the line in the drawRect method of the tilingView and it works perfectly. The line width is 20px relative to the map. In the linesView the line appears to be 20px wide relative to the device and positioned relative to the scrollview.
Sorry I'm trying my best to describe the problem...
Figured this out - I added the linesView to the tilingView instead of the scrollView like this:
- (void)displayTiledImageNamed:(NSString *)imageName size:(CGSize)imageSize
{
// clear the previous imageView
[imageView removeFromSuperview];
[imageView release];
imageView = nil;
[linesView removeFromSuperview];
[linesView release];
linesView = nil;
// reset our zoomScale to 1.0 before doing any further calculations
self.zoomScale = 1.0;
// make a new TilingView for the new image
imageView = [[TilingView alloc] initWithImageName:imageName size:imageSize];
linesView = [[LinesView alloc]initWithImageName:imageName size:imageSize];
linesView.backgroundColor = [UIColor clearColor];
[self addSubview:imageView];
[imageView addSubview:linesView];
[self configureForImageSize:imageSize];
}
I have a UITextView added on my UIView. The textview added is not editable, it is just to display some data. The data displayed in the textview is dynamic. Thats is the number of lines is not fixed. It may vary. So if the number of line increases, the size of the textview also needs to be increased. I have no clue how to do this. Please give me some ideas.
UPDATE:
Here's what I'm doing:
UIView *baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
baseView.backgroundColor = [UIColor grayColor];
[window addSubview:baseView];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, 30)];
textView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
textView.text = #"asdf askjalskjalksjlakjslkasj";
[textView sizeToFit];
[baseView addSubview:textView];
There is an answer posted at How do I size a UITextView to its content?
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;
or better(taking into account contentInset thanks to kpower's comment)
CGRect frame = _textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = _textView.contentSize.height + inset.top + inset.bottom;
_textView.frame = frame;
note: If you are going to reference a property of an object many times(e.g. frame or contentInset) it's better to assign it to a local variable so you don't trigger extra method calls(_textView.frame/[_textView frame] are method calls). If you are calling this code a lot(100000s of times) then this will be noticeably slower(a dozen or so method calls is insignificant).
However... if you want to do this in one line without extra variables it would be
_textView.frame = CGRectMake(_textView.frame.origin.x, _textView.frame.origin.y, _textView.frame.size.width, _textView.contentSize.height + _textView.contentInset.top + _textView.contentInset.bottom);
at the expense of 5 extra method calls.
You can use setFrame: or sizeToFit.
UPDATE:
I use sizeToFit with UILabel, and it works just fine, but UITextView is a subclass of UIScrollView, so I can understand why sizeToFit doesn't produce the desired result.
You can still calculate the text height and use setFrame, but you might want to take advantage of UITextView's scrollbars if the text is too long.
Here's how you get the text height:
#define MAX_HEIGHT 2000
NSString *foo = #"Lorem ipsum dolor sit amet.";
CGSize size = [foo sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
and then you can use this with your UITextView:
[textView setFont:[UIFont systemFontOfSize:14]];
[textView setFrame:CGRectMake(5, 30, 100, size.height + 10)];
or you can do the height calculation first and avoid the setFrame line:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, size.height + 10)];
sizeToFit Does Work
If you call sizeToFit after you set the text the first time it resizes. So after the first time you set it subsequent calls to set text will result in no change in size. Even if you call sizeToFit.
However, you can force it to resize like this:
Set the text.
Change the textView frame height to be CGFLOAT_MAX.
Call sizeToFit.
textView.contentSize.height in the textViewDidChange can only resize after text actually grows. For best visual result better to resize beforehand. After several hours I've figured out how to make it the same perfectly as in Instagram (it has the best algorithm among all BTW)
Initialize with this:
// Input
_inputBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, size.height - _InputBarHeight, size.width, _InputBarHeight)];
_inputBackgroundView.autoresizingMask = UIViewAutoresizingNone;
_inputBackgroundView.contentMode = UIViewContentModeScaleToFill;
_inputBackgroundView.userInteractionEnabled = YES;
[self addSubview:_inputBackgroundView];
[_inputBackgroundView release];
[_inputBackgroundView setImage:[[UIImage imageNamed:#"Footer_BG.png"] stretchableImageWithLeftCapWidth:80 topCapHeight:25]];
// Text field
_textField = [[UITextView alloc] initWithFrame:CGRectMake(70.0f, 0, 185, 0)];
_textField.backgroundColor = [UIColor clearColor];
_textField.delegate = self;
_textField.contentInset = UIEdgeInsetsMake(-4, -2, -4, 0);
_textField.showsVerticalScrollIndicator = NO;
_textField.showsHorizontalScrollIndicator = NO;
_textField.font = [UIFont systemFontOfSize:15.0f];
[_inputBackgroundView addSubview:_textField];
[_textField release];
[self adjustTextInputHeightForText:#""];
Fill UITextView delegate methods:
- (void) textViewDidBeginEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (void) textViewDidEndEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
if ([text isEqualToString:#"\n"])
{
[self performSelector:#selector(inputComplete:) withObject:nil afterDelay:.1];
return NO;
}
else if (text.length > 0)
{
[self adjustTextInputHeightForText:[NSString stringWithFormat:#"%#%#", _textField.text, text]];
}
return YES;
}
- (void) textViewDidChange:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
And the trick is...
- (void) adjustTextInputHeightForText:(NSString*)text {
int h1 = [text sizeWithFont:_textField.font].height;
int h2 = [text sizeWithFont:_textField.font constrainedToSize:CGSizeMake(_textField.frame.size.width - 16, 170.0f) lineBreakMode:UILineBreakModeWordWrap].height;
[UIView animateWithDuration:.1f animations:^
{
if (h2 == h1)
{
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - _InputBarHeight, self.frame.size.width, _InputBarHeight);
}
else
{
CGSize size = CGSizeMake(_textField.frame.size.width, h2 + 24);
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - size.height, self.frame.size.width, size.height);
}
CGRect r = _textField.frame;
r.origin.y = 12;
r.size.height = _inputBackgroundView.frame.size.height - 18;
_textField.frame = r;
} completion:^(BOOL finished)
{
//
}];
}
This works perfectly for me:
#define MAX_HEIGHT 2000
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
[textview setFont:[UIFont systemFontOfSize:14]];
[textview setFrame:CGRectMake(45, 6, 100, size.height + 10)];
textview.text = text;
Do the following:
_textView.text = someText;
[_textView sizeToFit];
_textView.frame.height = _textView.contentSize.height;
Addressing the similar issue I just created a an auto-layout based light-weight UITextView subclass which automatically grows and shrinks based on the size of user input and can be constrained by maximal and minimal height - all without a single line of code.
https://github.com/MatejBalantic/MBAutoGrowingTextView
The answer given by #Gabe doesn't work in iOS7.1 seemingly until after viewDidAppear. See my tests below.
UPDATE: Actually, the situation is even more complicated. If you assign textView.text in the resizeTheTextView method, in iOS7, the resizing amounts to allowing for only a single line of text. Seriously odd.
UPDATE2: See also UITextView content size different in iOS7
UPDATE3: See my code at the very bottom for what I'm using now. Seems to do the job.
#import "ViewController.h"
#interface ViewController ()
{
UITextView *textView;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textView = [[UITextView alloc] initWithFrame:CGRectMake(50, 50, 200, 1)];
[self.view addSubview:textView];
CALayer *layer = textView.layer;
layer.borderColor = [UIColor blackColor].CGColor;
layer.borderWidth = 1;
textView.text = #"hello world\n\n";
// Calling the method directly, after the view is rendered, i.e., after viewDidAppear, works on both iOS6.1 and iOS7.1
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Change size" forState:UIControlStateNormal];
[button addTarget:self action:#selector(resizeTheTextView) forControlEvents:UIControlEventTouchUpInside];
[button sizeToFit];
CGRect frame = button.frame;
frame.origin.y = 400;
button.frame = frame;
[self.view addSubview:button];
// Works on iOS6.1, but does not work on iOS7.1
//[self resizeTheTextView];
}
- (void) viewWillAppear:(BOOL)animated
{
// Does not work on iOS7.1, but does work on iOS6.1
//[self resizeTheTextView];
}
- (void) viewDidAppear:(BOOL)animated
{
// Does work on iOS6.1 and iOS7.1
//[self resizeTheTextView];
}
- (void) resizeTheTextView
{
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
// 5) From https://stackoverflow.com/questions/728704/resizing-uitextview
CGRect frame = textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = textView.contentSize.height + inset.top + inset.bottom;
textView.frame = frame;
NSLog(#"inset.top: %f, inset.bottom: %f", inset.top, inset.bottom);
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
UPDATE3:
if ([[UIDevice currentDevice] majorVersionNumber] < 7.0) {
CGRect frame = _abstractTextView.frame;
UIEdgeInsets inset = _abstractTextView.contentInset;
frame.size.height = _abstractTextView.contentSize.height + inset.top + inset.bottom;
_abstractTextView.frame = frame;
}
else {
CGSize textViewSize = [_abstractTextView sizeThatFits:CGSizeMake(_abstractTextView.frame.size.width, FLT_MAX)];
_abstractTextView.frameHeight = textViewSize.height;
}
After you add the UITextView to its parent if you set a Content Mode on it then it seems to resize itself automatically.
This means you don't need to work out the height manually and apply a height contraint. It just seems to work!! Tested in iOS7 and iOS8 on iPad.
e.g.
--
textView.contentMode = UIViewContentMode.Center;
--
If anyone can explain why this works it would be much appreciated.. I found it by accident when messing with options in interface builder.
Just set scrollEnabled to NO, or uncheck Scrolling Enabled in the Scroll View section in IB and the UITextView will self-size.