how to make a view to look like the newsstand bookshelf - iphone

I want to build a view to be look like the bookshelf in NewsStand application ...
is here any ready made open source project that does this job or similar view ?
EDIT: newsstand :

AQGridView with a pattern UIImage of a single book shelf. The example is a springboard, so a modification of the source cell is necessary. If you'll allow me a couple minutes to find my computer, I'll gladly post source code that should help.
EDIT: Download the AQGridView source from Github and unzip the files into the directory of your choice.
Navigate to the project called ‘Image Demo’ and the other project called ‘Springboard.’ You want the springboard project. Copy EVERYTHING except his the pre-existing items in your project (so exclude the main.m and the prefix).
The important set of files is the SpringboardIconCell Class. From here, you modify the cell. This is how I layed it out:
//.h
#import <UIKit/UIKit.h>
#import "AQGridViewCell.h"
#import <Foundation/Foundation.h>
#interface SpringBoardIconCell : AQGridViewCell.
{
UIImageView * _iconView;
UILabel * _title;
UILabel * _titleTwo;
}
#property (nonatomic, retain) UIImage * icon;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, retain) NSString * titleTwo;
//.m
#import "SpringBoardIconCell.h"
#import <QuartzCore/QuartzCore.h>
#implementation SpringBoardIconCell
- (id) initWithFrame: (CGRect) frame reuseIdentifier:(NSString *) reuseIdentifier
{
self = [super initWithFrame: frame reuseIdentifier: reuseIdentifier];
if ( self == nil )
return ( nil );
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0.0, 0.0, 155.0, 250.0)
cornerRadius: 18.0];
_iconView = [[UIImageView alloc] initWithFrame: CGRectMake(0.0, 0.0, 155.0, 250.0)];
_iconView.backgroundColor = [UIColor clearColor];
_iconView.opaque = NO;
_iconView.layer.shadowPath = path.CGPath;
_iconView.layer.shadowRadius = 0.0;
_iconView.layer.shadowOpacity = 0.0;
_iconView.layer.shadowOffset = CGSizeMake( 20.0, 20.0 );
_title = [[UILabel alloc] initWithFrame: CGRectZero];
_title.highlightedTextColor = [UIColor blackColor];
_title.font = [UIFont boldSystemFontOfSize: 12.0];
_title.adjustsFontSizeToFitWidth = YES;
_title.minimumFontSize = 12.0;
_title.backgroundColor = [UIColor clearColor];
_title.textColor = [UIColor whiteColor];
_title.textAlignment = UITextAlignmentCenter;
_title.numberOfLines = 1;
_titleTwo = [[UILabel alloc] initWithFrame: CGRectZero];
_titleTwo.highlightedTextColor = [UIColor blackColor];
_titleTwo.font = [UIFont fontWithName:#"AmericanTypewriter" size:20];
_titleTwo.adjustsFontSizeToFitWidth = YES;
_titleTwo.minimumFontSize = 18.0;
_titleTwo.backgroundColor = [UIColor clearColor];
_titleTwo.textColor = [UIColor blackColor];
_titleTwo.textAlignment = UITextAlignmentRight;
_titleTwo.numberOfLines = 4;
[self.contentView addSubview: _iconView];
[_iconView addSubview: _title];
[self.contentView addSubview:_titleTwo];
[self.contentView bringSubviewToFront:_titleTwo];
self.contentView.backgroundColor = [UIColor clearColor];
self.backgroundColor = [UIColor clearColor];
self.contentView.opaque = NO;
self.opaque = NO;
self.selectionStyle = AQGridViewCellSelectionStyleNone;
return ( self );
}
- (void) dealloc
{
[_title release];
[_iconView release];
[super dealloc];
}
- (UIImage *) icon
{
return ( _iconView.image );
}
- (void) setIcon: (UIImage *) anIcon
{
_iconView.image = anIcon;
[self setNeedsLayout];
}
- (CALayer *) glowSelectionLayer
{
return ( _iconView.layer );
}
- (NSString *) title
{
return ( _title.text );
}
- (NSString*)titleTwo {
return (_titleTwo.text);
}
- (void) setTitle: (NSString *) title
{
_title.text = title;
[self setNeedsLayout];
}
-(void)setTitleTwo:(NSString *)titleTwo {
_titleTwo.text = titleTwo;
[self setNeedsLayout];
}
- (void) layoutSubviews
{
[super layoutSubviews];
[_titleTwo setFrame:CGRectMake(self.contentView.frame.origin.x + 45, 10, 135, 100.0f)];
CGSize imageSize = _iconView.image.size;
CGRect bounds = CGRectInset( self.contentView.bounds, 10.0, 10.0 );
[_title sizeToFit];
CGRect frame = _title.frame;
frame.size.width = 155.0;
frame.origin.y = CGRectGetMaxY(bounds) - frame.size.height;
frame.origin.x = 0;
_title.frame = frame;
// adjust the frame down for the image layout calculation
bounds.size.height = frame.origin.y - bounds.origin.y;
if ( (imageSize.width <= bounds.size.width) &&
(imageSize.height <= bounds.size.height) )
{
return;
}
// scale it down to fit
CGFloat hRatio = bounds.size.width / imageSize.width;
CGFloat vRatio = bounds.size.height / imageSize.height;
CGFloat ratio = MIN(hRatio, vRatio);
[_iconView sizeToFit];
frame = _iconView.frame;
frame.size.width = floorf(imageSize.width * ratio);
frame.size.height = floorf(imageSize.height * ratio);
frame.origin.x = floorf((bounds.size.width - frame.size.width) * 0.5);
frame.origin.y = floorf((bounds.size.height - frame.size.height) * 0.5);
_iconView.frame = frame;
}
#end
Now go into your main view or wherever this bookshelf will be. You need to adopt the AQGridView delegate and datasource
<AQGridViewDelegate, AQGridViewDataSource>
Then the delegate methods
- (NSUInteger) numberOfItemsInGridView: (AQGridView *) gridView
{
return (_array.count);
}
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * EmptyIdentifier = #"EmptyIdentifier";
static NSString * CellIdentifier = #"CellIdentifier";
if ( index == _emptyCellIndex )
{
NSLog( #"Loading empty cell at index %u", index );
AQGridViewCell * hiddenCell = [gridView dequeueReusableCellWithIdentifier: EmptyIdentifier];
if ( hiddenCell == nil )
{
// must be the SAME SIZE AS THE OTHERS
// Yes, this is probably a bug. Sigh. Look at -[AQGridView fixCellsFromAnimation] to fix
hiddenCell = [[[AQGridViewCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 155.0, 250.0)
reuseIdentifier: EmptyIdentifier] autorelease];
}
hiddenCell.hidden = YES;
return ( hiddenCell );
}
SpringBoardIconCell * cell = (SpringBoardIconCell *)[gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil )
{
cell = [[[SpringBoardIconCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 250.0, 250.0) reuseIdentifier: CellIdentifier] autorelease];
}
UIImage * image = [UIImage imageNamed:#"Image.png"];
cell.icon = image;
return ( cell );
}
- (CGSize) portraitGridCellSizeForGridView: (AQGridView *) gridView
{
return ( CGSizeMake(250.0, 250.0) );
}
But when all is said and done, you want a bookshelf, so get a bookshelf image off of the internet. Then, crop it down so only ONE level of the shelf is showing. In AQGridview, the background with a pattern image will work perfectly, so in viewDidLoad, add in:
_gridView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"PatternImage.png"]];
The results produce a 250 X 250 cell with a cell title view, and a bottom title view. The icon for the cell, however, are the golden mean numbers of 155 X 250.

I don't know of any open source project, but you could use a background image for the newsstand frame itself. Then above that you may place a UIScrollView with a 'shelves' background image. From there I suppose you could add the individual news items as UIButtons or as part of a table view.

For this you can keep tableview where your Newstand title and button will go in header view and each cell will contain a book. Book will be a UIButton and u have to set a single rack image as a background for cell. Creating a custom cell will be beneficial. I dont prefer scroll view since there you need to manage the co-ordinates of each book and if you keep background as single image containing many racks then it will look like your books are floating across the racks.
https://github.com/AlanQuatermain/AQGridView
Try this one if u like. I took the logic from this and implemented my own grid.

UITable/ScrollView with pre-rendered bookshelf background?

Just use a picture of a bookshelf in the background.

Just use UIView (with background like bookshelf) and UITable (use custom (modify) cell for with shelf image). You don't need to use any ready made its very simple to build from scratch.

Related

Does UILabel's sizeThatFits return its height?

I'm trying to implement the following thing in my app:
themeLabel = [[UILabel alloc] init];
themeLabel.backgroundColor = [UIColor redColor];
themeLabel.text = themeString;
[themeLabel sizeThatFits:CGSizeMake(274, 274)];
themeLabel.numberOfLines = 0;
[topThemeView addSubview:themeLabel];
NSLog(#"Height is %f ", themeLabel.frame.size.height);
[themeLabel setFrame:CGRectMake(leftMargin, mainScrollView.frame.origin.y + topPadding, 274, themeLabel.frame.size.height)];
And I end up with the Label's height that is 0.0. Any ideas why?
themeLabel = [[UILabel alloc] init];
themeLabel.backgroundColor = [UIColor redColor];
themeLabel.text = themeString;
themeLabel.numberOfLines = 0;
CGRect labelFrame = CGRectMake(leftMargin, mainScrollView.frame.origin.y + topPadding, 0.0, 0.0);
labelFrame.size = [themeLabel sizeThatFits:CGSizeMake(274, 274)];
[themeLabel setFrame:labelFrame];
[topThemeView addSubview:themeLabel];
sizeThatFits asks the view to calculate and return the size that best fits its subviews. So you are never setting the frame of themeLabel
you should do:
themeLabel.numberOfLines = 0;
CGSize size = [themeLabel sizeThatFits:CGSizeMake(274, 274)];
themeLabel.frame = (CGRect) {0,0, size};
I created a category for handling height for UILabels:
UILabel+TCFlexibleHeight.h:
#import <UIKit/UIKit.h>
#interface UILabel (TCFlexibleHeight)
- (CGFloat)heightForText:(NSString*)text;
- (CGFloat)heightForCurrentText;
- (CGFloat)adjustHeightForCurrentText;
#end
UILabel+TCFlexibleHeight.m:
#import "UILabel+TCFlexibleHeight.h"
static const NSInteger kMaxLines = 1000;
#implementation UILabel (TCFlexibleHeight)
- (CGFloat)heightForText:(NSString*)text {
if (text == nil) {
return 0;
}
NSInteger numberOfLines = self.numberOfLines > 0 ? self.numberOfLines : kMaxLines;
CGSize size = CGSizeMake(self.frame.size.width, self.font.lineHeight * numberOfLines);
return [text sizeWithFont:self.font constrainedToSize:size lineBreakMode:self.lineBreakMode].height;
}
- (CGFloat)heightForCurrentText {
return [self heightForText:self.text];
}
- (CGFloat)adjustHeightForCurrentText {
CGFloat height = [self heightForCurrentText];
CGRect frame = self.frame;
frame.size.height = height;
return height;
}
#end
With this category your code will be something like this:
[themeLabel setFrame:CGRectMake(leftMargin, mainScrollView.frame.origin.y + topPadding, 274, [themeLabel heightForCurrentText])];
Note that this category doesn't handle attributed strings and require the line wrapping set to clip to character.

How to change a rectangle's position and size realtime?

I want to draw an rectangle and can change the rectangle's position and size realtime.
I have tried following two methods, but the view don't show the rectangle.
do you have any advice or example?
appreciate your help.
use view frame (it only show the new rectangle when restart the app)
UIView * drawView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 10, 10)];
[self.view addSubview:drawView];
drawView.layer.borderColor = [[UIColor orangeColor] CGColor];
drawView.layer.borderWidth = 2;
drawView.frame = CGRectMake(r.x, r.y, r.width, r.height);
draw in the drawRect
fav = [[FaceAugmentingView alloc] initWithFrame:[imageView frame]];
fav.backgroundColor = [UIColor clearColor];
[self.view addSubview: fav ];
fav.face = CGRectMake(r.x, r.y, r.width, r.height);
[fav setNeedsDisplay];
I suggest you add a method that reloads the faces based whenever you need to alter it. Declare and array that will help us keep track of all the views we've added.
#interface FacesViewController: UIViewController
[..]
#property (nonatomic, retain) NSMutableArray * faceViews;
- (void)reloadFaces;
[..]
#end
And then implement reloadFaces something like this -
- (void)reloadFaces {
/* remove all current views we've added */
for ( UIView * aView in self.faceViews ) {
[aView removeFromSuperview];
}
[self.faceViews removeAllObjects];
/* Add news ones */
int numberOfFaces = [self numberOfFacesInImage];
for ( int i = 0; i < numberOfFaces; i++ ) {
CGRect faceFrame = [self frameForFaceAtIndex:i];
UIView * drawView = [[[UIView alloc] initWithFrame:faceFrame] autorelease];
drawView.layer.borderColor = [[UIColor orangeColor] CGColor];
drawView.layer.borderWidth = 2;
drawView.frame = CGRectMake(r.x, r.y, r.width, r.height);
[self.view addSubview:drawView];
[faceViews addObject:drawView];
}
}
I would expect reloadFaces to be called when there is a change of some kind. numberOfFacesInImage and frameForFaceAtIndex: are methods that you will have to implement based on how you get the data. You can also replace them to suit your data model. Don't forget to initialize faceViews.

UITextView inside UIAlertView subclass

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.

How can I change the color of pagination dots of UIPageControl?

I am developing an application in which I want to change either color or image of UIPageControl pagination dots. How can I change it? Is it possible to customize UIpageControl on above scenario?
UPDATE:
This answer is 6 years old and very outdated, but it's still attracting votes and comments. Ever since iOS 6.0 you should be using the pageIndicatorTintColor and currentPageIndicatorTintColor properties on UIPageControl.
ORIGINAL ANSWER:
I ran into this problem today and decided to write my own simple replacement class.
It's a sublassed UIView that uses Core Graphics to render the dots in the colors you specify.
You use the exposed properties to customize and control it.
If you want to you can register a delegate object to get notifications when the user taps on one of the little page dots. If no delegate is registered then the view will not react to touch input.
It's completely fresh from the oven, but seems to work. Let me know if you run into any problems with it.
Future improvements:
Resize the dots to fit the current
bounds if there are too many.
Don't redraw the entire view in drawRect:
Example use:
CGRect f = CGRectMake(0, 0, 320, 20);
PageControl *pageControl = [[[PageControl alloc] initWithFrame:f] autorelease];
pageControl.numberOfPages = 10;
pageControl.currentPage = 5;
pageControl.delegate = self;
[self addSubview:pageControl];
Header file:
//
// PageControl.h
//
// Replacement for UIPageControl because that one only supports white dots.
//
// Created by Morten Heiberg <morten#heiberg.net> on November 1, 2010.
//
#import <UIKit/UIKit.h>
#protocol PageControlDelegate;
#interface PageControl : UIView
{
#private
NSInteger _currentPage;
NSInteger _numberOfPages;
UIColor *dotColorCurrentPage;
UIColor *dotColorOtherPage;
NSObject<PageControlDelegate> *delegate;
//If ARC use __unsafe_unretained id delegate;
}
// Set these to control the PageControl.
#property (nonatomic) NSInteger currentPage;
#property (nonatomic) NSInteger numberOfPages;
// Customize these as well as the backgroundColor property.
#property (nonatomic, retain) UIColor *dotColorCurrentPage;
#property (nonatomic, retain) UIColor *dotColorOtherPage;
// Optional delegate for callbacks when user taps a page dot.
#property (nonatomic, retain) NSObject<PageControlDelegate> *delegate;
#end
#protocol PageControlDelegate<NSObject>
#optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
#end
Implementation file:
//
// PageControl.m
//
// Replacement for UIPageControl because that one only supports white dots.
//
// Created by Morten Heiberg <morten#heiberg.net> on November 1, 2010.
//
#import "PageControl.h"
// Tweak these or make them dynamic.
#define kDotDiameter 7.0
#define kDotSpacer 7.0
#implementation PageControl
#synthesize dotColorCurrentPage;
#synthesize dotColorOtherPage;
#synthesize delegate;
- (NSInteger)currentPage
{
return _currentPage;
}
- (void)setCurrentPage:(NSInteger)page
{
_currentPage = MIN(MAX(0, page), _numberOfPages-1);
[self setNeedsDisplay];
}
- (NSInteger)numberOfPages
{
return _numberOfPages;
}
- (void)setNumberOfPages:(NSInteger)pages
{
_numberOfPages = MAX(0, pages);
_currentPage = MIN(MAX(0, _currentPage), _numberOfPages-1);
[self setNeedsDisplay];
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Default colors.
self.backgroundColor = [UIColor clearColor];
self.dotColorCurrentPage = [UIColor blackColor];
self.dotColorOtherPage = [UIColor lightGrayColor];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedRight:)];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
[self addGestureRecognizer:swipeRight];
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedLeft:)];
[swipe setDirection:UISwipeGestureRecognizerDirectionLeft];
[self addGestureRecognizer:swipe];
}
return self;
}
-(void) swipedLeft:(UISwipeGestureRecognizer *) recognizer
{
self.currentPage++;
}
-(void) swipedRight:(UISwipeGestureRecognizer *) recognizer
{
self.currentPage--;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAllowsAntialiasing(context, true);
CGRect currentBounds = self.bounds;
CGFloat dotsWidth = self.numberOfPages*kDotDiameter + MAX(0, self.numberOfPages-1)*kDotSpacer;
CGFloat x = CGRectGetMidX(currentBounds)-dotsWidth/2;
CGFloat y = CGRectGetMidY(currentBounds)-kDotDiameter/2;
for (int i=0; i<_numberOfPages; i++)
{
CGRect circleRect = CGRectMake(x, y, kDotDiameter, kDotDiameter);
if (i == _currentPage)
{
CGContextSetFillColorWithColor(context, self.dotColorCurrentPage.CGColor);
}
else
{
CGContextSetFillColorWithColor(context, self.dotColorOtherPage.CGColor);
}
CGContextFillEllipseInRect(context, circleRect);
x += kDotDiameter + kDotSpacer;
}
}
- (void)dealloc
{
[dotColorCurrentPage release];
[dotColorOtherPage release];
[delegate release];
[super dealloc];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.delegate) return;
CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];
CGFloat dotSpanX = self.numberOfPages*(kDotDiameter + kDotSpacer);
CGFloat dotSpanY = kDotDiameter + kDotSpacer;
CGRect currentBounds = self.bounds;
CGFloat x = touchPoint.x + dotSpanX/2 - CGRectGetMidX(currentBounds);
CGFloat y = touchPoint.y + dotSpanY/2 - CGRectGetMidY(currentBounds);
if ((x<0) || (x>dotSpanX) || (y<0) || (y>dotSpanY)) return;
self.currentPage = floor(x/(kDotDiameter+kDotSpacer));
if ([self.delegate respondsToSelector:#selector(pageControlPageDidChange:)])
{
[self.delegate pageControlPageDidChange:self];
}
}
#end
In iOS 6 you can set the tint color of UIPageControl:
There are 2 new properties:
pageIndicatorTintColor
currentPageIndicatorTintColor
You can also use the appearance API to change the tint color of all page indicators.
If you are targeting iOS 5 make sure it doesn't crash:
if ([pageControl respondsToSelector:#selector(setPageIndicatorTintColor:)]) {
pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}
pageControl.pageIndicatorTintColor = [UIColor redColor];
pageControl.currentPageIndicatorTintColor = [UIColor redColor];
works for iOS6
In case anyone wants an ARC / modern version of it (no need to redefine properties as ivar, no dealloc, and works with Interface Builder) :
#import <UIKit/UIKit.h>
#protocol PageControlDelegate;
#interface PageControl : UIView
// Set these to control the PageControl.
#property (nonatomic) NSInteger currentPage;
#property (nonatomic) NSInteger numberOfPages;
// Customize these as well as the backgroundColor property.
#property (nonatomic, strong) UIColor *dotColorCurrentPage;
#property (nonatomic, strong) UIColor *dotColorOtherPage;
// Optional delegate for callbacks when user taps a page dot.
#property (nonatomic, weak) NSObject<PageControlDelegate> *delegate;
#end
#protocol PageControlDelegate<NSObject>
#optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
#end
PageControl.m :
#import "PageControl.h"
// Tweak these or make them dynamic.
#define kDotDiameter 7.0
#define kDotSpacer 7.0
#implementation PageControl
#synthesize dotColorCurrentPage;
#synthesize dotColorOtherPage;
#synthesize currentPage;
#synthesize numberOfPages;
#synthesize delegate;
- (void)setCurrentPage:(NSInteger)page
{
currentPage = MIN(MAX(0, page), self.numberOfPages-1);
[self setNeedsDisplay];
}
- (void)setNumberOfPages:(NSInteger)pages
{
numberOfPages = MAX(0, pages);
currentPage = MIN(MAX(0, self.currentPage), numberOfPages-1);
[self setNeedsDisplay];
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
// Default colors.
self.backgroundColor = [UIColor clearColor];
self.dotColorCurrentPage = [UIColor blackColor];
self.dotColorOtherPage = [UIColor lightGrayColor];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
self.dotColorCurrentPage = [UIColor blackColor];
self.dotColorOtherPage = [UIColor lightGrayColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAllowsAntialiasing(context, true);
CGRect currentBounds = self.bounds;
CGFloat dotsWidth = self.numberOfPages*kDotDiameter + MAX(0, self.numberOfPages-1)*kDotSpacer;
CGFloat x = CGRectGetMidX(currentBounds)-dotsWidth/2;
CGFloat y = CGRectGetMidY(currentBounds)-kDotDiameter/2;
for (int i=0; i<self.numberOfPages; i++)
{
CGRect circleRect = CGRectMake(x, y, kDotDiameter, kDotDiameter);
if (i == self.currentPage)
{
CGContextSetFillColorWithColor(context, self.dotColorCurrentPage.CGColor);
}
else
{
CGContextSetFillColorWithColor(context, self.dotColorOtherPage.CGColor);
}
CGContextFillEllipseInRect(context, circleRect);
x += kDotDiameter + kDotSpacer;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.delegate) return;
CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];
CGFloat dotSpanX = self.numberOfPages*(kDotDiameter + kDotSpacer);
CGFloat dotSpanY = kDotDiameter + kDotSpacer;
CGRect currentBounds = self.bounds;
CGFloat x = touchPoint.x + dotSpanX/2 - CGRectGetMidX(currentBounds);
CGFloat y = touchPoint.y + dotSpanY/2 - CGRectGetMidY(currentBounds);
if ((x<0) || (x>dotSpanX) || (y<0) || (y>dotSpanY)) return;
self.currentPage = floor(x/(kDotDiameter+kDotSpacer));
if ([self.delegate respondsToSelector:#selector(pageControlPageDidChange:)])
{
[self.delegate pageControlPageDidChange:self];
}
}
#end
The answer provided by Heiberg works really well, however the page control does not behave exactly like the one by apple.
If you want the page control to behave like the one from apple does (always increment the current page by one if you touch the second half, otherwise decrease by one), try this touchesBegan-method instead:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];
CGRect currentBounds = self.bounds;
CGFloat x = touchPoint.x - CGRectGetMidX(currentBounds);
if(x<0 && self.currentPage>=0){
self.currentPage--;
[self.delegate pageControlPageDidChange:self];
}
else if(x>0 && self.currentPage<self.numberOfPages-1){
self.currentPage++;
[self.delegate pageControlPageDidChange:self];
}
}
Add the following code to DidFinishLauch in AppDelegate,
UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
pageControl.backgroundColor = [UIColor whiteColor];
Hope this will help.
In Swift, this code inside the UIPageViewController is getting a reference to the page indicator and setting its properties
override func viewDidLoad() {
super.viewDidLoad()
//Creating the proxy
let pageControl = UIPageControl.appearance()
//Customizing
pageControl.pageIndicatorTintColor = UIColor.lightGrayColor()
pageControl.currentPageIndicatorTintColor = UIColor.darkGrayColor()
//Setting the background of the view controller so the dots wont be on a black background
self.view.backgroundColor = UIColor.whiteColor()
}
use this for coding
if ([pageControl respondsToSelector:#selector(setPageIndicatorTintColor:)]) {
pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}
or from storyboard you can change from current page tint
Adding to existing answers, it can be done like,
You can fix it with ease by adding the following code to your appdelegate.m file in your didFinishLaunchingWithOptions method:
UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor darkGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor orangeColor];
pageControl.backgroundColor = [UIColor whiteColor]
This is worked for me in iOS 7.
pageControl.pageIndicatorTintColor = [UIColor purpleColor];
pageControl.currentPageIndicatorTintColor = [UIColor magentaColor];
It's easy with Swift 1.2:
UIPageControl.appearance().pageIndicatorTintColor = UIColor.lightGrayColor()
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.redColor()
In cased of Swift 2.0 and up, the below code will work:
pageControl.pageIndicatorTintColor = UIColor.whiteColor()
pageControl.currentPageIndicatorTintColor = UIColor.redColor()
#Jasarien I think you can subclass UIPageControll, line picked from apple doc only "Subclasses that customize the appearance of the page control can use this method to resize the page control when the page count changes" for the method sizeForNumberOfPages:
You could also use Three20 Library that contains a styleable PageControl and dozens of other helpful UI Controls and Abstractions.
It's not possible using the iPhone SDK from an official standpoint. You might be able to do it using private methods, but that will be a barrier to getting onto the app store.
The only other safe solution is to create yout own page control which shpuldnt be too difficult given that the page control simply displays what page is currently shown in a scroll view.
myView.superview.tintColor = [UIColor colorWithRed:1.0f
green:1.0f blue:1.0f alpha:1.0f];

Resizing UITextView

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.