Identify UIImages Individually - objective-c

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;
}

Related

Format UILabel and UIImages with CoreText

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 ?

UITableViewCell with UITextView height in iOS 7?

How can I calculate the height of an UITableViewCell with an UITextView in it in iOS 7?
I found a lot of answers on similar questions, but sizeWithFont: takes part in every solution and this method is deprecated!
I know I have to use - (CGFloat)tableView:heightForRowAtIndexPath: but how do I calculate the height my TextView needs to display the whole text?
First of all, it is very important to note, that there is a big difference between UITextView and UILabel when it comes to how text is rendered. Not only does UITextView have insets on all borders, but also the text layout inside it is slightly different.
Therefore, sizeWithFont: is a bad way to go for UITextViews.
Instead UITextView itself has a function called sizeThatFits: which will return the smallest size needed to display all contents of the UITextView inside a bounding box, that you can specify.
The following will work equally for both iOS 7 and older versions and as of right now does not include any methods, that are deprecated.
Simple Solution
- (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width {
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:text];
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
This function will take a NSAttributedString and the desired width as a CGFloat and return the height needed
Detailed Solution
Since I have recently done something similar, I thought I would also share some solutions to the connected Issues I encountered. I hope it will help somebody.
This is far more in depth and will cover the following:
Of course: setting the height of a UITableViewCell based on the size needed to display the full contents of a contained UITextView
Respond to text changes (and animate the height changes of the row)
Keeping the cursor inside the visible area and keeping first responder on the UITextView when resizing the UITableViewCell while editing
If you are working with a static table view or you only have a known number of UITextViews, you can potentially make step 2 much simpler.
1. First, overwrite the heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// check here, if it is one of the cells, that needs to be resized
// to the size of the contained UITextView
if ( )
return [self textViewHeightForRowAtIndexPath:indexPath];
else
// return your normal height here:
return 100.0;
}
2. Define the function that calculated the needed height:
Add an NSMutableDictionary (in this example called textViews) as an instance variable to your UITableViewController subclass.
Use this dictionary to store references to the individual UITextViews like so:
(and yes, indexPaths are valid keys for dictionaries)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Do you cell configuring ...
[textViews setObject:cell.textView forKey:indexPath];
[cell.textView setDelegate: self]; // Needed for step 3
return cell;
}
This function will now calculate the actual height:
- (CGFloat)textViewHeightForRowAtIndexPath: (NSIndexPath*)indexPath {
UITextView *calculationView = [textViews objectForKey: indexPath];
CGFloat textViewWidth = calculationView.frame.size.width;
if (!calculationView.attributedText) {
// This will be needed on load, when the text view is not inited yet
calculationView = [[UITextView alloc] init];
calculationView.attributedText = // get the text from your datasource add attributes and insert here
textViewWidth = 290.0; // Insert the width of your UITextViews or include calculations to set it accordingly
}
CGSize size = [calculationView sizeThatFits:CGSizeMake(textViewWidth, FLT_MAX)];
return size.height;
}
3. Enable Resizing while Editing
For the next two functions, it is important, that the delegate of the UITextViews is set to your UITableViewController. If you need something else as the delegate, you can work around it by making the relevant calls from there or using the appropriate NSNotificationCenter hooks.
- (void)textViewDidChange:(UITextView *)textView {
[self.tableView beginUpdates]; // This will cause an animated update of
[self.tableView endUpdates]; // the height of your UITableViewCell
// If the UITextView is not automatically resized (e.g. through autolayout
// constraints), resize it here
[self scrollToCursorForTextView:textView]; // OPTIONAL: Follow cursor
}
4. Follow cursor while Editing
- (void)textViewDidBeginEditing:(UITextView *)textView {
[self scrollToCursorForTextView:textView];
}
This will make the UITableView scroll to the position of the cursor, if it is not inside the visible Rect of the UITableView:
- (void)scrollToCursorForTextView: (UITextView*)textView {
CGRect cursorRect = [textView caretRectForPosition:textView.selectedTextRange.start];
cursorRect = [self.tableView convertRect:cursorRect fromView:textView];
if (![self rectVisible:cursorRect]) {
cursorRect.size.height += 8; // To add some space underneath the cursor
[self.tableView scrollRectToVisible:cursorRect animated:YES];
}
}
5. Adjust visible rect, by setting insets
While editing, parts of your UITableView may be covered by the Keyboard. If the tableviews insets are not adjusted, scrollToCursorForTextView: will not be able to scroll to your cursor, if it is at the bottom of the tableview.
- (void)keyboardWillShow:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, kbSize.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification*)aNotification {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.35];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, 0.0, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
[UIView commitAnimations];
}
And last part:
Inside your view did load, sign up for the Notifications for Keyboard changes through NSNotificationCenter:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
Please don't get mad at me, for making this answer so long. While not all of it is needed to answer the question, I believe that there are other people who these directly related issues will be helpful to.
UPDATE:
As Dave Haupert pointed out, I forgot to include the rectVisible function:
- (BOOL)rectVisible: (CGRect)rect {
CGRect visibleRect;
visibleRect.origin = self.tableView.contentOffset;
visibleRect.origin.y += self.tableView.contentInset.top;
visibleRect.size = self.tableView.bounds.size;
visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
return CGRectContainsRect(visibleRect, rect);
}
Also I noticed, that scrollToCursorForTextView: still included a direct reference to one of the TextFields in my project. If you have a problem with bodyTextView not being found, check the updated version of the function.
There is a new function to replace sizeWithFont, which is boundingRectWithSize.
I added the following function to my project, which makes use of the new function on iOS7 and the old one on iOS lower than 7. It has basically the same syntax as sizeWithFont:
-(CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
if(IOS_NEWER_OR_EQUAL_TO_7){
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
nil];
CGRect frame = [text boundingRectWithSize:size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributesDictionary
context:nil];
return frame.size;
}else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [text sizeWithFont:font constrainedToSize:size];
#pragma clang diagnostic pop
}
}
You can add that IOS_NEWER_OR_EQUAL_TO_7 on your prefix.pch file in your project as:
#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
If you're using UITableViewAutomaticDimension I have a really simple (iOS 8 only) solution. In my case it's a static table view, but i guess you could adapt this for dynamic prototypes...
I have a constraint outlet for the text-view's height and I have implemented the following methods like this:
// Outlets
#property (weak, nonatomic) IBOutlet UITextView *textView;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeight;
// Implementation
#pragma mark - Private Methods
- (void)updateTextViewHeight {
self.textViewHeight.constant = self.textView.contentSize.height + self.textView.contentInset.top + self.textView.contentInset.bottom;
}
#pragma mark - View Controller Overrides
- (void)viewDidLoad {
[super viewDidLoad];
[self updateTextViewHeight];
}
#pragma mark - TableView Delegate & Datasource
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 80;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
#pragma mark - TextViewDelegate
- (void)textViewDidChange:(UITextView *)textView {
[self.tableView beginUpdates];
[self updateTextViewHeight];
[self.tableView endUpdates];
}
But remember: the text view must be scrollable, and you must setup your constraints such that they work for automatic dimension:
setup all the view in the cell in relation to each other, with fixed heights (including the text view height, which you will change programatically)
the top most view has the top spacing and the bottom most view has the bottom spacing to the super view;
The most basic cell example is:
no other views in the cell except the textview
0 margins around all sides of the text view and a predefined height constraint for the text view.
Tim Bodeit's answer is great. I used the code of Simple Solution to correctly get the height of the text view, and use that height in heightForRowAtIndexPath. But I don't use the rest of the answer to resize the text view. Instead, I write code to change the frame of text view in cellForRowAtIndexPath.
Everything is working in iOS 6 and below, but in iOS 7 the text in text view cannot be fully shown even though the frame of text view is indeed resized. (I'm not using Auto Layout). It should be the reason that in iOS 7 there's TextKit and the position of the text is controlled by NSTextContainer in UITextView. So in my case I need to add a line to set the someTextView in order to make it work correctly in iOS 7.
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
someTextView.textContainer.heightTracksTextView = YES;
}
As the documentation said, what that property does is:
Controls whether the receiver adjusts the height of its bounding
rectangle when its text view is resized. Default value: NO.
If leave it with the default value, after resize the frame of someTextView, the size of the textContainer is not changed, leading to the result that the text can only be displayed in the area before resizing.
And maybe it is needed to set the scrollEnabled = NO in case there's more than one textContainer, so that the text will reflow from one textContainer to the another.
Here is one more solution that aims at simplicity and quick prototyping:
Setup:
Table with prototype cells.
Each cell contains dynamic sized UITextView w/ other contents.
Prototype cells are associated with TableCell.h.
UITableView is associated with TableViewController.h.
Solution:
(1) Add to TableViewController.m:
// This is the method that determines the height of each cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// I am using a helper method here to get the text at a given cell.
NSString *text = [self getTextAtIndex:indexPath];
// Getting the height needed by the dynamic text view.
CGSize size = [self frameForText:text sizeWithFont:nil constrainedToSize:CGSizeMake(300.f, CGFLOAT_MAX)];
// Return the size of the current row.
// 80 is the minimum height! Update accordingly - or else, cells are going to be too thin.
return size.height + 80;
}
// Think of this as some utility function that given text, calculates how much
// space would be needed to fit that text.
- (CGSize)frameForText:(NSString *)text sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
{
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
nil];
CGRect frame = [text boundingRectWithSize:size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributesDictionary
context:nil];
// This contains both height and width, but we really care about height.
return frame.size;
}
// Think of this as a source for the text to be rendered in the text view.
// I used a dictionary to map indexPath to some dynamically fetched text.
- (NSString *) getTextAtIndex: (NSIndexPath *) indexPath
{
return #"This is stubbed text - update it to return the text of the text view.";
}
(2) Add to TableCell.m:
// This method will be called when the cell is initialized from the storyboard
// prototype.
- (void)awakeFromNib
{
// Assuming TextView here is the text view in the cell.
TextView.scrollEnabled = YES;
}
Explanation:
So what's happening here is this: each text view is bound to the height of the table cells by vertical and horizontal constraints - that means when the table cell height increases, the text view increases its size as well. I used a modified version of #manecosta's code to calculate the required height of a text view to fit the given text in a cell. So that means given a text with X number of characters, frameForText: will return a size which will have a property size.height that matches the text view's required height.
Now, all that remains is the update the cell's height to match the required text view's height. And this is achieved at heightForRowAtIndexPath:. As noted in the comments, since size.height is only the height for the text view and not the entire cell, there should be some offset added to it. In the case of the example, this value was 80.
One approach if you're using autolayout is to let the autolayout engine calculate the size for you. This isn't the most efficient approach but it is pretty convenient (and arguably the most accurate). It becomes more convenient as the complexity of the cell layout grows - e.g. suddenly you have two or more textviews/fields in the cell.
I answered a similar question with a complete sample for sizing tableview cells using auto layout, here:
How to resize superview to fit all subviews with autolayout?
The complete smooth solution is as follows.
First, we need the cell class with a textView
#protocol TextInputTableViewCellDelegate <NSObject>
#optional
- (void)textInputTableViewCellTextWillChange:(TextInputTableViewCell *)cell;
- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell;
#end
#interface TextInputTableViewCell : UITableViewCell
#property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
#property (nonatomic, readonly) UITextView *textView;
#property (nonatomic) NSInteger minLines;
#property (nonatomic) CGFloat lastRelativeFrameOriginY;
#end
#import "TextInputTableViewCell.h"
#interface TextInputTableViewCell () <UITextViewDelegate> {
NSLayoutConstraint *_heightConstraint;
}
#property (nonatomic) UITextView *textView;
#end
#implementation TextInputTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
_textView = [UITextView new];
_textView.translatesAutoresizingMaskIntoConstraints = NO;
_textView.delegate = self;
_textView.scrollEnabled = NO;
_textView.font = CELL_REG_FONT;
_textView.textContainer.lineFragmentPadding = 0.0;
_textView.textContainerInset = UIEdgeInsetsZero;
[self.contentView addSubview:_textView];
[self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view]-|" options:nil metrics:nil views:#{#"view": _textView}]];
[self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[view]-|" options:nil metrics:nil views:#{#"view": _textView}]];
_heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationGreaterThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 0.0
constant: (_textView.font.lineHeight + 15)];
_heightConstraint.priority = UILayoutPriorityRequired - 1;
[_textView addConstraint:_heightConstraint];
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
self.minLines = 1;
}
- (void)setMinLines:(NSInteger)minLines {
_heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([self.delegate respondsToSelector:#selector(textInputTableViewCellTextWillChange:)]) {
[self.delegate textInputTableViewCellTextWillChange:self];
}
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
if ([self.delegate respondsToSelector:#selector(textInputTableViewCellTextDidChange:)]) {
[self.delegate textInputTableViewCellTextDidChange:self];
}
}
Next, we use it in the TableViewController
#interface SomeTableViewController () <TextInputTableViewCellDelegate>
#end
#implementation SomeTableViewController
. . . . . . . . . . . . . . . . . . . .
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TextInputTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: TextInputTableViewCellIdentifier forIndexPath:indexPath];
cell.delegate = self;
cell.minLines = 3;
. . . . . . . . . .
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (void)textInputTableViewCellWillChange:(TextInputTableViewCell *)cell {
cell.lastRelativeFrameOriginY = cell.frame.origin.y - self.tableView.contentOffset.y;
}
- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[UIView performWithoutAnimation:^{
[self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
}];
CGFloat contentOffsetY = cell.frame.origin.y - cell.lastRelativeFrameOriginY;
self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);
CGRect caretRect = [cell.textView caretRectForPosition:cell.textView.selectedTextRange.start];
caretRect = [self.tableView convertRect:caretRect fromView:cell.textView];
CGRect visibleRect = self.tableView.bounds;
visibleRect.origin.y += self.tableView.contentInset.top;
visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
BOOL res = CGRectContainsRect(visibleRect, caretRect);
if (!res) {
caretRect.size.height += 5;
[self.tableView scrollRectToVisible:caretRect animated:NO];
}
}
#end
Here minLines allows to set minimum height for the textView (to
resist height minimizing by AutoLayout with
UITableViewAutomaticDimension).
moveRowAtIndexPath:indexPath: with the same indexPath starts
tableViewCell height re-calculation and re-layout.
performWithoutAnimation: removes side-effect (tableView content
offset jumping on starting new line while typing).
It is important to preserve relativeFrameOriginY (not
contentOffsetY!) during cell update because contentSize of the
cells before the current cell could be change by autoLayout calculus
in unexpected way. It removes visual jumps on system hyphenation
while typing long words.
Note that you shouldn't set the property estimatedRowHeight! The
following doesn't work
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
Use only tableViewDelegate method.
==========================================================================
If one doesn't mind against weak binding between tableView and tableViewCell and updating geometry of the tableView from tableViewCell, it is possible to upgrade TextInputTableViewCell class above:
#interface TextInputTableViewCell : UITableViewCell
#property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
#property (nonatomic, weak) UITableView *tableView;
#property (nonatomic, readonly) UITextView *textView;
#property (nonatomic) NSInteger minLines;
#end
#import "TextInputTableViewCell.h"
#interface TextInputTableViewCell () <UITextViewDelegate> {
NSLayoutConstraint *_heightConstraint;
CGFloat _lastRelativeFrameOriginY;
}
#property (nonatomic) UITextView *textView;
#end
#implementation TextInputTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
_textView = [UITextView new];
_textView.translatesAutoresizingMaskIntoConstraints = NO;
_textView.delegate = self;
_textView.scrollEnabled = NO;
_textView.font = CELL_REG_FONT;
_textView.textContainer.lineFragmentPadding = 0.0;
_textView.textContainerInset = UIEdgeInsetsZero;
[self.contentView addSubview:_textView];
[self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view]-|" options:nil metrics:nil views:#{#"view": _textView}]];
[self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[view]-|" options:nil metrics:nil views:#{#"view": _textView}]];
_heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationGreaterThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 0.0
constant: (_textView.font.lineHeight + 15)];
_heightConstraint.priority = UILayoutPriorityRequired - 1;
[_textView addConstraint:_heightConstraint];
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
self.minLines = 1;
self.tableView = nil;
}
- (void)setMinLines:(NSInteger)minLines {
_heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
_lastRelativeFrameOriginY = self.frame.origin.y - self.tableView.contentOffset.y;
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
NSIndexPath *indexPath = [self.tableView indexPathForCell:self];
if (indexPath == nil) return;
[UIView performWithoutAnimation:^{
[self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
}];
CGFloat contentOffsetY = self.frame.origin.y - _lastRelativeFrameOriginY;
self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);
CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.start];
caretRect = [self.tableView convertRect:caretRect fromView:self.textView];
CGRect visibleRect = self.tableView.bounds;
visibleRect.origin.y += self.tableView.contentInset.top;
visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
BOOL res = CGRectContainsRect(visibleRect, caretRect);
if (!res) {
caretRect.size.height += 5;
[self.tableView scrollRectToVisible:caretRect animated:NO];
}
}
#end
Put UILabel behind your UITextView.
Use this answer: https://stackoverflow.com/a/36054679/6681462 to UILabel you created
Give them same constraints and fonts
Set them same text;
Your cell's height will calculate by UILabel's content, but all text will be showed by TextField.
UITextView *txtDescLandscape=[[UITextView alloc] initWithFrame:CGRectMake(2,20,310,2)];
txtDescLandscape.editable =NO;
txtDescLandscape.textAlignment =UITextAlignmentLeft;
[txtDescLandscape setFont:[UIFont fontWithName:#"ArialMT" size:15]];
txtDescLandscape.text =[objImage valueForKey:#"imgdescription"];
txtDescLandscape.text =[txtDescLandscape.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[txtDescLandscape sizeToFit];
[headerView addSubview:txtDescLandscape];
CGRect txtViewlandscpframe = txtDescLandscape.frame;
txtViewlandscpframe.size.height = txtDescLandscape.contentSize.height;
txtDescLandscape.frame = txtViewlandscpframe;
i think this way you can count the height of your text view and then resize your tableview cell according to that height so that you can show full text on cell
Swift version
func textViewHeightForAttributedText(text: NSAttributedString, andWidth width: CGFloat) -> CGFloat {
let calculationView = UITextView()
calculationView.attributedText = text
let size = calculationView.sizeThatFits(CGSize(width: width, height: CGFloat.max))
return size.height
}
If you want to automatically adjust UITableViewCell's height based on the height of the inner UITextView's height. See my answer here: https://stackoverflow.com/a/45890087/1245231
The solution is quite simple and should work since iOS 7. Make sure that the Scrolling Enabled option is turned off for the UITextView inside the UITableViewCell in the StoryBoard.
Then in your UITableViewController's viewDidLoad() set the tableView.rowHeight = UITableViewAutomaticDimension and tableView.estimatedRowHeight > 0 such as:
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44.0
}
That's it. UITableViewCell's height will be automatically adjusted based on the inner UITextView's height.
For iOS 8 and above you can just use
your_tablview.estimatedrowheight= minheight you want
your_tableview.rowheight=UItableviewautomaticDimension

Objective C Method Retrieving Image

I'm very sure this is an easy fix, but it's so specific I don't know where to find an answer... I want to create a method that retrieves and uses a UIImage from the object it is performed on.
Here is the line that calls the method with the imageView as the object.
[self performSelector:#selector(method:) withObject:self.imageView afterDelay:0.0];
and this is the method...
- (void) method:(UIImage *) image {
if ([image isEqual:image1]){
x = 1;
}
if ([image isEqual:image2]){
x = 2;
}
if ([image isEqual:image3]){
x = 3;
}
if ([image isEqual:image4]){
x = 4;
}
if ([image isEqual:image5]){
x = 5;
}
...am I going about this in the right way? Thanks!
To retrive UIImage from UIImageView object you should just use
[self.imageView image];
And if you want to create a method that retrieves and uses a UIImage from the object it is performed on. (I will guess you are using an UIImageView object...) You should subclass or add category to the UIImageView and add a method like this to it
- (void) method {
UIImage* image = [self image];
//do whatever you want with the image
//...
}
As an alternative you can pass an UIImageView to the method and let it retrieve the image and then use it for whatever purpose
- (void) method:(UIImageView*)imageView {
UIImage* image = [imageView image];
//do whatever you want with the image
//...
}
Use this method
[self performSelector:#selector(method:) withObject:self.imageView.view afterDelay:0.0];
You are passing UIImageview as parameter rather than UIImage

NSMutableArray objects being lost

I hope this makes sense...I have an NSMutableArray that I'm attempting to store multiple UIScrollView's in. Each UIScrollView is going to have multiple images and the ultimate goal is to be able to allow the user to swipe vertically for categories (each instance of UIScrollView) and horizontally for each image in that category (each instance of UIImageView). For now, until I get this code to work, I'm just creating 1 UIScrollView with 1 image and I'm adding that to scrollViewManager. This seems to add everything correctly, but when I leave this function, game over. My Array is empty. I don't understand. Am I supposed to do some sort of deep copy when I add to the Array so it doesn't get destroyed. Perhaps I'll figure it out when I wake up tomorrow, but for now, I'd like to break everything in sight. Thanks, in advance!
EDIT: Sorry for lack of details, I posted this rather late. My project is using ARC, so I'm not releasing scrollView anywhere. Here is the declaration for scrollViewManager in my header file:
#property (nonatomic, retain) NSMutableArray *scrollViewManager;
Here is the code I use to retrieve my scrollView:
UIScrollView *test = (UIScrollView*) [self.scrollViewManager objectAtIndex: 0];
And here is the code to initialize the scrollView and array:
scrollViewManager = [[NSMutableArray alloc] initWithCapacity: 1];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setCanCancelContentTouches:NO];
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
NSString *imageName = #"pic1.png";//[NSString stringWithFormat:#"pic%d.jpg", i+1];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = 400;
rect.size.width = 300;
imageView.frame = rect;
imageView.tag = 1; // tag our images for later use when we place them in serial fashion
[scrollView addSubview:imageView];
[self.scrollViewManager addObject: scrollView];
There are some things that are kind of vague. For example:
Are you using ARC, if not and you are releasing your scrollView anywhere?
Are you using the auto-create-synthesize thing?
What type of property is your scrollViewManager (weak, strong, etc).
If you are creating your ivars automatically when defining a property, your ivar should be called _scrollViewManager instead of scrollViewManager.
The use of self.scrollViewManager is correct when using this method.
I always do this kind of stuff the other way around. When I have a property like this:
#property (nonatomic, strong) NSMutableArray* myArray;
I always initialize them using the self keyword:
self.myArray = [NSMutableArray new];
And when modifying it, I always use the generated ivar:
[_myArray addObject:myFineObject];
I think this is something I kept using when moving from non-ARC to ARC. Not sure if it still the way to go, but it works like expected.

array crashing at index which is in bounds?

Can anyone help me out with explaining why my array is crashing.
Basically I have two buttons which change the image in the imageView.
h:
#interface CaseStudySecondPageViewController : UIViewController
{
//scroller and back and forward buttons for custom control.
__weak IBOutlet UIScrollView *scroller;
__weak IBOutlet UIButton *back;
__weak IBOutlet UIButton *forward;
//app delegate to return the selected case study from the other controller.
AppDelegate *del;
//variables for displaying the case studies
NSArray *myImageArray;
NSInteger localSelctorInt;
}
//setup a IBOutlet to allow the image to be changed.
#property (weak, nonatomic) IBOutlet UIImageView *logoImage;
#end
m:
update method:
-(void)Updater
{
[logoImage setImage:[myImageArray objectAtIndex:localSelctorInt]];
}
previous and next buttons:
//returns to previous image if back button is clicked.
-(void) back:(id)sender
{
if (localSelctorInt > 0)
{
localSelctorInt--;
}
else
{
localSelctorInt = 0;
}
[self Updater];
}
//returns to next image if forward button is clicked. increase 7 if array size changes.
//1 removed from int as in starts at 1 and array starts at 0.
-(void) forward:(id)sender
{
if (localSelctorInt < 7)
{
localSelctorInt++;
}
else
{
localSelctorInt = 7;
}
[self Updater];
}
and finally my image array is declared in:
- (void)viewDidLoad
{
[super viewDidLoad];
//setup the delegate to allow access to the int which was modified on the page before.
del = [[UIApplication sharedApplication] delegate];
//assign a local variable to the int from the previous page.
localSelctorInt = *(del.selectorInt);
//create back and forward buttons as UIButtons first.
[back addTarget:self action:#selector(back: ) forControlEvents:UIControlEventTouchUpInside];
[forward addTarget:self action:#selector(forward:) forControlEvents:UIControlEventTouchUpInside];
//pass the ui buttons into ui bar items.
UIBarButtonItem *UIBack = [[UIBarButtonItem alloc] initWithCustomView:back];
UIBarButtonItem *UIForward = [[UIBarButtonItem alloc] initWithCustomView:forward];
//add these to an array (notice item"s").
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:UIForward, UIBack, nil];
//initialize the array with all of the images for the case studies logos.
myImageArray = [NSArray arrayWithObjects:
[UIImage imageNamed:#"Logo_Arm.png"],
[UIImage imageNamed:#"Logo_Fife"],
[UIImage imageNamed:#"Logo_Findel.png"],
[UIImage imageNamed:#"Logo_BirkBeck.png"],
[UIImage imageNamed:#"Logo_NHS_Dudley.png"],
[UIImage imageNamed:#"Logo_NHS_Kensignton.png"],
[UIImage imageNamed:#"Logo_Yorkshire_Water.png"],
[UIImage imageNamed:#"Logo_Uni_Hertfordshire.png"],
nil];
//call update method once to load the first image selected from the previous page.
[self Updater];
}
now my app crashes when it trys to index the 4th image (3rd in the array):
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 ..
2]'
It's probably something really simple, but id appreciate your help, cheers.
If you look at your exception, it says that your array contains 3 objects only ( ... bounds [0..2] ... ). So, my guess is that your fourth image doesn't exist ... Check spelling of [UIImage imageNamed:#"Logo_BirkBeck.png"]. Does this image really exist? It returns nil, list of objects is nil terminated, so, your array does contain 3 images only instead of 8 images.
Side Note: Don't use magic constants like localSelctorInt < 7. Always rely on real number of elements in array (_myImageArray.count). It's a way to hell ...
2nd Side Note: Don't declare ivars without underscore prefix. Do use something like this _myImageArray for ivar name.
I guess its due to not allocating the array
myImageArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"Logo_Arm.png"],
[UIImage imageNamed:#"Logo_Fife.png"],
[UIImage imageNamed:#"Logo_Findel.png"],
[UIImage imageNamed:#"Logo_BirkBeck.png"],
[UIImage imageNamed:#"Logo_NHS_Dudley.png"],
[UIImage imageNamed:#"Logo_NHS_Kensignton.png"],
[UIImage imageNamed:#"Logo_Yorkshire_Water.png"],
[UIImage imageNamed:#"Logo_Uni_Hertfordshire.png"],
nil];