How does one draw wrappped text in the drawRect method? I'm trying to use atebits' ABTableViewCell class to do custom drawing of a table view cell. I'd like to draw a small picture and then text next to it which may or may not need to line wrap. Think of the table cells in a Twitter client like Tweetie which show a profile picture and a tweet. How is that done?
I tried using the various NSString drawAtPoint methods, but they don't seem to wrap the text into multiple lines.
I think you need one of the drawInRect:withFont: methods. They are documented as performing word wrapping. If you think about it, it makes sense that drawAtPoint wouldn't work, because it doesn't know the rectangle the text needs to fit into.
Related
In HTML, with CSS, one can do "float" for images, just like this
the image is surrounded by text.
How can I do it WITHOUT UIWebView?
I can't use UIWebview, because
UIWebView has a slight lag after you loadHTML. Its content doesn't show immediately, not like UILabel or UIImage
The image is async loaded, so I need a white space holder there with UIActivityView spinning
Is that possible I just use a UIImageView & UILabel to do float?
Thanks
The "easy" way is probably to use two labels and an image. If your text/images are static, then you can split the text at the right place beforehand. If not, you'll have to do some text size measurements; see UIStringDrawing.h. Figuring out where all the line breaks should be is non-trivial.
The "hard" way is to create your own subclass of UIView with text and image properties and have it do all the string/image layout and drawing. It's not actually that much harder than figuring out where the line breaks should be dynamically.
The "harder" way is to use Core Text (iOS 3.2+); the Columnar Layout example in the Core Text Programming Guide should work, but also note that CTFramesetterCreateFrame() can take an arbitrary CGPath, so you can just pass it a path of the available area. This might actually be slightly easier than using UIKit string-sizing and trying to find out where all the line breaks are (you just need to write a lot of Core Text boilerplate instead).
I’m parsing a twitter rss feed, and I just need to show tweets, so I don't need MGTwitterEngine.
I have already set it up so I can see the complete tweet, the only thing I want it to colorize hashtags and urls.
So I would need to slice up the string in different substrings, colorize the hashtags and urls and glue it together in various UILabels
Is there an easier way to accomplish this?
In short I need some parts of a string colored differently than others.
This question should answer your question: iphone-uilabel-containing-text-with-multiple-fonts-at-the-same-time
Use two UILabel IBOutlets, each with a
different format (font/color/etc), as
you desire.. Move the second one over
the first based on where the first
one's text ends. You can get that via
sizeWithFont:forWidth:lineBreakMode:
Alternatively, you can subclass
UILabel, and draw the text yourself in
drawRect. If you do it this way, just
add an instance variable to tell you
how much of the string to draw in one
format, and draw the rest in another.
I am using the ABTableViewCell subclass by Atebits to optimize scrolling for rather large potential data sources depending on what my search returns. (Either way though, I'd highly recommend the subclass for even the simplest tableviews, they scroll like butter!)
I have an NSString drawn in a view:
[nameLabel drawInRect:nameRect withFont:nameLabelFont lineBreakMode:UILineBreakModeWordWrap];
I thought it would be a cool feature that the searched word (which is "Name CONTAINS searchText") be highlighted. Is this as simple as changing the background color behind a substring of nameLabel?
EDITS:
NSAttributedString seems to be what I'm looking for, but turns out it doesn't exist for the iPhone.
From further reading, it would seem that HTML and a UIWebView is the way to go for this... but that seems awfully expensive. Is this really the solution?
Hey, check out TTStyledLabel - it might do what you want.
How about using an NSAttributedString? http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/Reference/Reference.html
Wild idea - is your text a single line? If so you could compute the width of "Name " and then the width of "CONTAINS" and so you would know where exactly it's drawn on screen. Once you do simply drop a semi-transparent colored UIView on top of the text.
I didn't try it myself, but that's what I would do if I had to make it work.
I am creating a syntax highlighter for the iPhone and in order to display text with multiple formats, I have sub-classed UIView and modified the drawRect: method so that each line is displayed with the proper syntax highlighting (highlighting is done earlier with RegEx, text is drawn with CGContextShowTextAtPoint() one line at a time). Everything works ok, I store each line of text as an NSString in an NSMutableArray, I handle the keyboard through a hidden UITextField and its delegate methods, the cursor is a blinking CALayer that can be moved around with touches and I have a custom scroll view that handles scrolling. However, I have two problems that I can't seem to wrap my head around:
Word wrap, right now the text just keeps going off the left end of the screen. To keep things fast I only redraw the portions of the view that have changed (usually just the line being edited, but sometimes the lines below as well e.g. if you press return halfway through the document) with setNeedsDisplayInRect:. This makes word wrap complicated because then you have to draw more than one line on the screen, even though it still is only one object in the array.
UIViews have a maximum content size of 1024x1024 which equates to about 64 lines. I need the ability to display more than that. I am thinking about using multiple CALayers one after another, but I am having trouble drawing content to the layers (using drawLayer:inContext: and drawInContext:).
So my questions are:
Does anyone have any, even general, suggestions about how to accomplish either of these two points. Or,
Has someone already written a custom text-drawing view that handles these things that I could use instead.
Thanks,
Kyle
EDIT: The scrolling problem is pretty much solved, however I am still having trouble with word-wrap. My trouble is that everything is done by line: the view updates one line at a time, the text is stored as an array of lines, the highlighter highlights one line at a time, etc. and having a single index in the array (one line of text) take up multiple lines on the screen raises some problems, for example, I had to implement my own movable cursor and when you move the cursor it needs to be able to turn a display line (found by dividing touch.x by the line height) into a text line (an index in the array). Any ideas?
You should first spend some quality time understanding how this problem was solved on Mac:
http://developer.apple.com/documentation/Cocoa/Conceptual/TextArchitecture/Tasks/AssembleSysByHand.html
http://developer.apple.com/documentation/Cocoa/Conceptual/TextLayout/TextLayout.html
In particular, you should become familiar with line fragment generation, which is the problem you're trying to solve for word-wrap, and you should understand glyph generation in order to do rich text well. You don't need to implement all of the flexibility of NSTypesetter or NSLayoutManager of course, but the text system on Mac is incredibly powerful, and you should learn from its example. Implementing something similar to NSAttributedString for iPhone may be valuable to you and improve your performance.
Unless you're moving things around a lot (for which this would best work in a UIScrollView), I don't think you should need to use CALayers here. That seems overkill for the problem, and may actually adversely impact the optimizations already provided by UIScrollView. If you're seeing performance problems, first make sure you're not doing redundant calculations within your drawRect:.
Check out TTStyledText in Three20 library. Not sure how well it matches your goals, but might serve you as an example. (The library itself is a bit bloated, but is a wonderful source to look at.)
It may be best to draw your text within a CATiledLayer hosted within your UIView, in order to get around the 1024x1024 texture size limit (which appears to actually be 2048x2048 on all existing devices). For an example of text drawing within a CALayer, I'd refer you to the CPTextLayer class within the Core Plot framework. This layer (which inherits from the CPLayer class within that same framework) does cross-platform (Mac and iPhone) text rendering in a CALayer. You might be able to extend it to work as a CATiledLayer for longer text blocks.
One thing to be aware of is that platform-specific drawAtPoint: methods are used in this layer, instead of the CGContextShowTextAtPoint() function you are using. The reason for this is that CGContextShowTextAtPoint() only works on ASCII text, meaning that you can't do Unicode text rendering with it. There is an example of using CGContextShowTextAtPoint() to draw text, within a #define'd out portion at the bottom of the renderAsVectorInContext: method.
I want to read the string displayed on the screen with finger touch, means as my finger moves over the text displayed on screen, the text below the finger should get highlighted. Is there any way to do this using UITextView or any other class. Also i want to play the sound associated with that word from a sound file which has the sentence already recorded.
If anyone knows it kindly reply.
Thanks in advance.
So that's two questions.
To know where your finger is pointing in the text you need to know where the UITextView has laid out the characters. This is not something the UITextView is documented to support, so next up is drawing text yourself. This stackoverflow question on embedding a custom font has a few links and even some code that will do the trick. What you need to add is keeping track of character advances so you know exactly where each character is rendered (or you could even use other core graphics functions to get exact bounding boxes of each character).
Clearly once you know what character has been tapped you also know the word it is part of. You'll would probably have the best quality if the sentence is recorded both in full and as single words, so that you can play back either when needed. This other stackoverflow question on playing sounds loaded from the internet has a few links and snippets which relate to loading and playing back audio.
I am doing this in an iPhone text editor I am writing. For multiple reasons, I have subclassed a UIView to draw the text (instead of a UITextView) and I had to implement something like this to provide a movable cursor like a text view has. What I did was use a fixed-width font (Courier to be exact), then I took the x-coordinate of the touch and divided it by the width of the character and rounded the answer down. This gives you a character index.
However, if it is possible, find a way to do this without drawing the text yourself (if the text is user entered or really anything other than hardcoded) because Apple provides a lot of functionality in its UITextView class that is a pain to replicate: editing, cursor, word wrap, scrolling, etc.
It might be possible, if you can't get touch events from a subclassed UITextView, to put a transparent view/layer over the top of the text and get touch events from that, then you would only have to figure out a way to turn this on and off for editing.
Kyle
I came up with a handy trick to do this. In drawRect, mimic the text that the UITextView will draw, but don't actually draw the text. Set the font to Helvetica 17-point, and the only semi-tricky part is handling word wrapping. You already have the text to be displayed, and you can get the size of each word in that default font by calling sizeWithFont. Then save the rectangles for each word, and when the user touches the view, find which of the stored rectangles contains the touched point. To test and calibrate the geometry (line spacing, etc), draw the text yourself in a different color. When you get it so that you can only see one of the font colors, you've got it perfect.
I can post my code for this if someone wants it.