Hi I am trying to draw strings in my UITableViewCell in iOS 7 with the following code
-(void)drawRect:(CGRect)rect{
[super drawRect:rect];
CGRect playerNameRect = CGRectMake(0, kCellY, kPlayerNameSpace, kCellHeight);
NSDictionary*dictonary = [NSDictionary
dictionaryWithObjectsAndKeys:
[UIColor hmDarkGreyColor], NSForegroundColorAttributeName,
kFont, NSFontAttributeName,
nil];
[self.playerName drawInRect:playerNameRect withAttributes:dictonary];
}
However I can not get anything to appear... self.playerName is not nil, and the playerNameRect is correct.
I was previously using the following code to do the same thing but was recently deprecated in iOS 7
[self.playerName drawInRect:playerNameRect withFont:kFont lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentCenter];
What is also strange is I can not get anything to draw in drawRect on a UITableViewCell... The deprecated code works when I am drawingRect on just a UIView.
You shouldn't use UITableViewCell's drawRect method to perform custom drawing. The proper way to do it is to create a custom UIView and add it as a subview of your cell (as a subview of the contentView property). You can add the drawing code to this custom view and everything will work fine.
Hope this helps!
Check out these posts too:
Table View Cell custom drawing 1
Table View Cell custom drawing 2
Table View Cell custom drawing 3
As others said, don't use UITableViewCell's drawRect selector directly. By doing that, you're relying on implementation details of UITableViewCell, and Apple made no guarantee that such behaviour won't break in future versions, just as it did in iOS 7... Instead, create a custom UIView subclass, and add it as a subview to the UITableViewCell's contentView, like this:
#implementation CustomTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView addSubview:[[CustomContentView alloc]initWithFrame:self.contentView.bounds]];
}
return self;
}
#end
And the CustomContentView:
#implementation CustomContentView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSDictionary * attributes = #{
NSFontAttributeName : [UIFont fontWithName:#"Helvetica-bold" size:12],
NSForegroundColorAttributeName : [UIColor blackColor]
};
[#"I <3 iOS 7" drawInRect:rect withAttributes:attributes];
}
#end
Works like charm!
Try setting cell.backgroundColor = [UIColor clearColor] in init.
While I agree with the accepted answer, here's my take on it for the records:
If you don't need any of the builtin UITableViewCell functionality (swiping, removing, reordering, ...) and just use it as a container to draw your custom stuff, then you might want to consider removing all of the cells subviews in tableview:willDisplayCell:ForRowAtIndexPath. This will make your drawing be visible again and will get you maximum performance (since you get rid of the subviews you don't need).
Related
I am working on a project for iOS 7.0+ with a storyboard, using Size Classes with AutoLayout and I'm using a UIView subclass backed by a xib file of the same name.
What I'am trying to do is I'am instantiating a UIView from xib programmatically and adding it to a ViewController from a Storyboard. This ViewController has AutoLayout up and running but the UIView I am adding doesn't respect the frame of the ViewController.
I'm instantiating my UIView subclass like this:
tabBarView = [[SHDTabBarView alloc] initWithFrame:CGRectMake(0, self.view.height-50, self.view.width, 50)];
[self.view addSubview:tabBarView];
And inside the subclass I'm using a set up of creating a UIView IBOutlet called container to instantiate it form code like this:
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self == nil) return nil;
[self initalizeSubviews];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self == nil) return nil;
[self initalizeSubviews];
return self;
}
-(void)initalizeSubviews{
NSString *nibName = NSStringFromClass([self class]);
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
[nib instantiateWithOwner:self options:nil];
//Add the view loaded from the nib into self.
[self addSubview:self.container];
}
This is how my xib looks in the Interface Builder (notice the width of the canvas is 320 px):
And that's how it looks on the iPhone 6 (notice how it's getting cut off from the right side):
I've tried to use a multitude of solutions, including doing it all in code with an open-source solution PureLayout, using a manual constraint set up, etc.
None of my findings seem to work right. Ideally, I want to set up everything in Interface Builder, then just add the view to the superview of the ViewController with according frame and let AutoLayout do its magic.
How should I approach this task? Any advices are more than welcome.
Try to set the frame of your subview in the viewDidLayoutSubviews(). Looks like you init your subview before view fully layouted
I have an iPad app, using Storyboards, XCode 4.6 and iOS 6.1. I have a scene that contains a UIViewController. Inside that UIViewController, I have a UIScrollController, all created using IB. Programmatically, in viewDidLoad I created two (2) UIViews (one called subViewGrid, the other called subViewData) and added them to the UIViewController; they both display correctly in the Simulator. Here's the code:
- (void)viewDidLoad {
[super viewDidLoad];
// notify me when calendar has been tapped and CFGregorianDate has been updated
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(calendarTapNotification:)
name:#"calendarDateSelected" object:nil ];
// UIScrollVIew settings
CGSize scrollableSize = CGSizeMake(760, 1379); // set size of scheduleView
[self.schedScrollView setContentSize:scrollableSize];
self.schedScrollView.contentInset = UIEdgeInsetsMake(0,0,44,44); // allow for scroll bar
self.schedScrollView.directionalLockEnabled = YES; // prevents diagonal scrolling
// create a sub-view to hold the appointment GRID
CGRect frame = CGRectMake(0,0,760,1390); // 110,48,760,1390
subViewGrid = [[UIView alloc] initWithFrame:frame];
subViewGrid.tag = 12; // use tag to get correct sub-view
subViewGrid.backgroundColor = [UIColor grayColor];
subViewGrid.alpha = 1.0; // make it opaque
[self.schedScrollView addSubview:subViewGrid];
// create a sub-view to hold the appointment DATA
frame = CGRectMake(110,48,670,750);
subViewData = [[UIView alloc] initWithFrame:frame];
subViewData.tag = 22; // use tag to get correct sub-view
subViewData.backgroundColor = [UIColor redColor];
subViewData.alpha = 0.2; // make it sort of transparent
[self.schedScrollView addSubview:subViewData];
[self.subViewGrid setNeedsDisplay]; // **** UPDATED ****
}
Here is the .h file contents for the UIViewController:
#interface CalendarViewController : UIViewController {
UIView *subViewGrid;
UIView *subViewData;
}
#property (strong, nonatomic) IBOutlet UIScrollView *schedScrollView;
- (void) calendarTapNotification:(NSNotification *) notification;
-(NSDate *)beginningOfDay:(NSDate *)date;
-(NSDate *)endOfDay:(NSDate *)date;
#end
In my drawRect method, I have some code that is supposed to draw a "grid" on the subViewGrid. The problem is drawRect never gets called.`
I have read the UIView Programmer's Guide and looked in SO and did a Google search, but found nothing that addresses the issue, which is: why won't [self.subViewGrid setNeedsDisplay] call drawRect from where I have it placed?
Your view controller needs to call setNeedsDisplay for the view it controls, not for itself. So, you want
[self.subViewGrid setNeedsDisplay]
This is just an error in your reading the documentation. Understanding the documentation is critical for objective-C programming so I'll try to help you get a grasp of it.
If you look at the documentation for setNeedsDisplay you will see that it is either a CALayer or UIView class method. If you then look at inheritance, you will see that UIView is UIResponder:NSObject and CALayer is NSObject. None of these inherit from UIViewController which is why you are getting the error. You need to call [self.subViewGrid setNeedsDisplay]
Background
I am implementing a UICollectionView (for the first time) in an effort to achieve a paged horizontal scroll view of tiles. I'd like each tile to show in the center of the frame with it's sister tiles partially visible to the left and right (something like the page selector in the Safari app). I'm interested in using the UICollectionView to take advantage of built-in cell dequeueing and would rather not use a rotated UITableView.
Issue
The issue I'm finding is that when using pagingEnabled = YES and clipsToBounds = NO, the UICollectionView removes cells outside the collectionView frame (they're not in the visibleCells array) as soon as paging is complete. Can anyone provide advice on how to achieve the effect of displaying previews of the sister tiles while maintaining this basic setup? Or am I approaching this incorrectly?
Screenshots
start
scrolling
end
The scrolling screen is exactly correct. But in the start and end shots I want there to be green visible in the blue margins.
Here's what's in my AppDelegate.m (credit to tutsplus.com for the basic setup here):
#import "AppDelegate.h"
#interface ViewController : UICollectionViewController
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"ID"];
[self.view setBackgroundColor:[UIColor blueColor]];
// pad the collection view by 20 px
UIEdgeInsets padding = UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
[self.collectionView setFrame:UIEdgeInsetsInsetRect(self.view.frame, padding)];
// set pagingEnabled and clipsToBounds off
[self.collectionView setPagingEnabled:YES];
[self.collectionView setClipsToBounds:NO];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"ID" forIndexPath:indexPath];
UILabel *label = [[UILabel alloc] initWithFrame:cell.bounds];
label.textAlignment = NSTextAlignmentCenter;
label.text = [NSString stringWithFormat:#"%d", indexPath.row];
[label setBackgroundColor:[UIColor greenColor]];
[cell.contentView addSubview:label];
return cell;
}
#end
#implementation AppDelegate
{
ViewController *vc;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// setup the UICollectionViewFlowLayout
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(280, 280);
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// add a custom UICollectionViewController to the window
vc = [[ViewController alloc] initWithCollectionViewLayout:layout];
self.window.rootViewController = vc;
self.window.backgroundColor = [UIColor yellowColor];
[self.window makeKeyAndVisible];
return YES;
}
#end
Turns out the solution to this was actually quite simple. I just needed to overlap the UICollectionViewCell cells by enough pixels to have them still show within the collectionView's frame after the paged scrolling finishes. The relevent code was
layout.itemSize = CGSizeMake(300, 300);
layout.minimumLineSpacing = -20.0;
And I subclassed the UICollectionViewFlowLayout and overrode the (CGSize)collectionViewContentSize method to return the non-overlapped size of the cells.
Many thanks for the tip about using a negative minimumLineSpacing. I created a tester application which uses a collection view cell loaded from a xib file. The cell has a transparent background and an “inner” view for the cell's content.
In this way, a custom flow layout is not necessary.
https://github.com/j4johnfox/CollectionViewTester
I'm not an expert in collectionView, but it could be possibly do with this line in cellForItemAtIndexPath:
[cell.contentView addSubview:label];
Everytime it's called, another label subview is added to cell. Either check for an existing label or subclass UICollectionViewCell?
You'll want to also override -pointInside:withEvent: to allow scroll gestures to start outside the frame of the collection view. I do this using a UIEdgeInsets property in my collection view subclass:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGRect extendedBounds = UIEdgeInsetsInsetRect(self.bounds, self.touchAreaInsets);
return CGRectContainsPoint(extendedBounds, point);
}
If you don't need App Store safety, you can override _visibleBounds to avoid negative spacing hacks:
- (CGRect)_visibleBounds {
return UIEdgeInsetsInsetRect(self.bounds, self.touchAreaInsets);
}
If you're not too pressed on code size and need App Store safety you could also subclass PSTCollectionView and possibly override visibleBoundRects for the same effect.
I have a very custom table view that actually serves as a content view, but table view was the obvious choice. I have a section index that i use to scroll the TableView - but there are no sections (well, one is there obviously). For the purpose of the user's orientation, I'd like to fade a view over the table view that is semi-transparent and shows a text in there. It should look like the overlay with the letters when scrolling the new iPod nano's section index. I don't know where i should put the code - because my view has to disappear sometime again too, and I don't really wanna use notifications. I'd init the view inside the tableview: sectionForSectionIndexTitle method. Thanks in advance.
Create a property in your .h file
#property (nonatomic, retain) UILabel *overlayLabel;
And add the following code to your .m file
- (void)viewDidLoad {
[super viewDidLoad];
self.overlayLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f,
0.0f,
self.tableView.frame.size.width,
self.tableView.frame.size.height)] autorelease];
overlayLabel.backgroundColor = [UIColor clearColor];
overlayLabel.alpha = .5f;
overlayLabel.textAlignment = UITextAlignmentCenter;
overlayLabel.text = #"Some text";
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView addSubview:overlayLabel];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[overlayLabel removeFromSuperview];
}
i would like to be able to set the background of my views. Currently i try to do it by overriding -(void)drawRect:(NSRect)dirtyRect like suggested here, so my custom view looks like this:
#implementation ViewWithBackgroundColor
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// ...
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
[[NSColor greenColor] setFill];
NSRectFill(dirtyRect);
}
#end
when i hit build an run the view appears in the default grey background, but when i resize the window the view appears in the desired green color.
does anybody knows what i am missing? I am not sure if it is relevant, but the view is stored in a nib. I already tried to call setNeedsDisplay:YES in awakeFromNib, but it did not help.
thx in advance,
Yevgeniy
There are some issues with your ‑drawRect: method. Firstly, you don't need to (and shouldn't) call super's implementation of ‑drawRect: unless there is a very specific reason to do so. The default implementation of ‑drawRect: does nothing, so in this case it's just a wasted message but you should get out of the habit.
Secondly, when drawing something that covers the whole view, you should normally ignore the dirty rect that's passed in, and draw the background by using [self bounds] as the rect passed to NSRectFill().
If you're linking against the 10.6 SDK or above, you can just set the backgroundColor property of the view instead of drawing the background yourself. Earlier SDKs don't support this property.
Why don't you try setting the backgroundColor property to [NSColor greenColor] at init
self.backgroundColor = [NSColor greenColor];