Set border in UICollectionView - iphone

It is my first time that I want to create a UICollectionView. This is how I want it to look like:
I read some tutorials and I know how it works exactly. The thing is as you see in the image, The UICollection cells have border from up, bottom, left and right. Do you know how can set these kind of border in Collection View?
As you see two of the items are selected by red color. is it possible in UICollectionView to have multiple selected items? if yes, could you please give send me some tutorials.

Small example project here: https://github.com/erikt/ETMultiSelect
First you have to make it possible to select more than one cell in the UICollectionView. This is done by setting the allowsMultipleSelectionproperty to YES on the collection view.
The view controller would look something like this:
#import "ETViewController.h"
#import "ETCellView.h"
#implementation ETViewController
static NSString *cellIdentifier = #"cvCell";
- (void)viewDidLoad {
[super viewDidLoad];
// Register our custom collection view cell
[self.collectionView registerClass:ETCellView.class forCellWithReuseIdentifier:cellIdentifier];
// Make it possible to select multiple cells
self.collectionView.allowsMultipleSelection = YES;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 10;
}
#pragma mark - UICollectionViewDelegate
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ETCellView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
return cell;
}
#end
The UICollectionViewCell is made up of several views. It has a content view, a background view and a selected background view.
There are many ways to achieve something similar to your picture, but I set the border on the selected background layer and add a subview to the content view that's inset so the background border is visible:
#import "ETCellView.h"
#import <QuartzCore/QuartzCore.h>
#implementation ETCellView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.restorationIdentifier = #"cvCell";
self.backgroundColor = [UIColor clearColor];
self.autoresizingMask = UIViewAutoresizingNone;
CGFloat borderWidth = 3.0f;
UIView *bgView = [[UIView alloc] initWithFrame:frame];
bgView.layer.borderColor = [UIColor redColor].CGColor;
bgView.layer.borderWidth = borderWidth;
self.selectedBackgroundView = bgView;
CGRect myContentRect = CGRectInset(self.contentView.bounds, borderWidth, borderWidth);
UIView *myContentView = [[UIView alloc] initWithFrame:myContentRect];
myContentView.backgroundColor = [UIColor whiteColor];
myContentView.layer.borderColor = [UIColor colorWithWhite:0.5f alpha:1.0f].CGColor;
myContentView.layer.borderWidth = borderWidth;
[self.contentView addSubview:myContentView];
}
return self;
}
#end
The result is something like this:
Clone and play with the sample project.
In a real project you would want to keep track of what the user has selected in the view controller, by adding the selected data model entities to some structure (like a NSMutableArray) in the – collectionView:didSelectItemAtIndexPath: method on the UICollectionViewDelegate protocol.

Related

uiImageView showing borders between images in UICollectionViewCell

We have a row of 139x127 images that are displaying with borders in our collection view cell. we tried UIViewContentModeScaleAspectFill (and Fit) but it is still showing the borders.
The code is below:
// uicollectionviewcell
(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:#"ProfilePhotoCell" owner:self options:nil];
if ([arrayOfViews count] < 1) {
return nil;
}
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) {
return nil;
}
self = [arrayOfViews objectAtIndex:0];
}
[self.imageView setContentMode:UIViewContentModeScaleAspectFill];
return self;
}
return self;
}
// uicollectionview/viewcontroller
// 1
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
// 2
CGSize retval = CGSizeMake(100, 66);
return retval;
}
// 3
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(20, 0, 0, 0);
}
Here is a screenshot: ! http://m.mirror.co/collection.png
What is going on with your collectionViewLayout property of the UICollectionView? I am guessing that the UICollectionView is padding the cells it is displaying.
For debugging purposes i have found it priceless to use LLDB by pausing the app and running...
po [[UIWindow keyWindow] _autolayoutTrace]
This will give you the pointers to each view object. Then i will run...
po [0xabcd123 constraints]
...to see if any auto constraints were added at runtime you didn't know about. Also, you can get the frame size of your cell and your view and see if they match. You may need to programmatically delete auto constraints and apply new constraints pinning your view to your cell. I ran into some of this while making a view cell "flip" from view to view.
Don't forget to turn off the auto constraints in your init or IB...
[self.imageview setTranslatesAutoresizingMaskIntoConstraints:NO];
Good luck!

Change size of UIViewTable to accommodate for AdWhirl Ad

I am trying to change the size of my UITableView. I have an ad on the bottom of my view, and when I scroll, the ad scrolls along with it. I was wondering how I can change the size of the UITableView so the ad will always remain on the bottom of the view regardless of whether the UITableView is being scrolled or not. I have tried changing the size of the frame of the TableView, but this doesn't work.
- (void)viewDidAppear:(BOOL)animated
{
tableView.frame = CGRectMake()...
}
I also tried changing it in the scrollViewDidScroll: selector, but no luck. Is there anyway I can change the height so it doesn't conflict with my ad on the bottom? Thanks!
With UITableViewControllers self.view == self.tableView. This is a problem in your case because the desired effect you want requires sibling views (two views added to a common superview) but there is no "superview" for self.tableView.
You have to create a new UIViewController subclass that has a UITableView and your ad view as two subviews. You will need to handle things like setting the data source and delegate for the table view, as well as deselecting table view cells when the controller appears. This is a little more work and requires some care, but is definitely doable.
I've thrown together a quick example below that will get you started:
// Header
#interface CustomTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
- (id)initWithStyle:(UITableViewStyle)tableViewStyle;
#property (nonatomic, readwrite, retain) UITableView* tableView;
#end
// Source
#interface CustomTableViewController()
#property (nonatomic, readwrite, assign) UITableViewStyle tableViewStyle;
#end
#implementation CustomTableViewController
#synthesize tableView;
#synthesize tableViewStyle = _tableViewStyle;
- (id)initWithStyle:(UITableViewStyle)tableViewStyle {
if ((self = [super initWithNibName:nil bundle:nil])) {
_tableViewStyle = tableViewStyle;
}
return self;
}
- (void)loadView {
[super loadView];
self.tableView = [[UITableView alloc] initWithStyle:self.tableViewStyle];
self.tableView.autoresizingMask = (UIViewAutoresizingMaskFlexibleWidth
| UIViewAutoresizingMaskFlexibleHeight);
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
// Create your ad view.
...
adView.autoresizingMask = (UIViewAutoresizingMaskFlexibleWidth
| UIViewAutoresizingMaskFlexibleTopMargin);
[self.view addSubview:adView];
[adView sizeToFit];
self.tableView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - adView.frame.size.height);
adView.frame = CGRectMake(0, self.view.bounds.size.height - adView.frame.size.height, self.view.bounds.size.width, adView.frame.size.height);
[self.tableView reloadData];
}
- (void)viewDidUnload {
self.tableView = nil;
[super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSIndexPath* selectedIndexPath = [self.tableView indexPathForSelectedRow];
if (nil != selectedIndexPath) {
[self.tableView deselectRowAtIndexPath:selectedIndexPath animated:animated];
}
}
#end
Simple way to solve this problem is just use .XIB file for your UITableView and then you change the height very easily using Interface Builder.
If you dont have IB file then please go through this post: How do I resize the UITableView's height dynamically?

iOS SDK: UITableView Styling

I've searched high a low for a solution to this problem. I can't find why the style from my nib isn't loading? If I change anything in the nibs the functioning stops. So I've resorted to overriding the style.
I have:
- (id)initWithCoder:(NSCoder *)inCoder
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization
}
return self;
}
This is on the second view controller, so following from rootviewcontroller. I would rather fix it another way, but if I have to override it I can.
So, the table is now grouped, but how can I programatically change the view background colour?
Any help appreciated.
Ps. For the initial problem, I have 3 nibs: MainWindow.xib, RootViewController.xib and CollectionsViewController.xib created by following this tutorial: http://www.techotopia.com/index.php/Creating_a_Navigation_based_iOS_4_iPhone_Application_using_TableViews#Setting_up_the_Data_in_the_Root_View_Controller
The first table is styled using the RootViewController.xib, but changing CollectionsViewController.xib does nothing.
Cheers,
Rhys
EDIT:
Got it with:
self.tableView.backgroundColor = [UIColor blackColor];
self.parentViewController.view.backgroundColor = [UIColor colorWithRed:0.0 green:0.2 blue:0.5 alpha:0.7];
if you override initWithCoder: method, you must call it's super version, which one is responsible for decoding the nib file data
To change the background color of the Table view you need to redesign the table.
In your header viewcontroller add the following code and on IB connect the IBOutlet:
#interface TableController : UIViewController <UITableViewDelegate> {
UITableView *tableView;
}
#property (nonatomic, retain) IBOutlet UITableView *tableView;
and in your implementation file add on viewDidLoad:
- (void)viewDidLoad {
CGFloat tableBorderLeft = 25;
CGFloat tableBorderRight = 15;
CGFloat tableBorderUP = 10;
CGRect tableRect = self.view.frame;
tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
tableRect.origin.y += tableBorderUP;
tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
tableView.frame = tableRect;
tableView.backgroundColor = [UIColor clearColor];
tableView.separatorColor = [UIColor whiteColor];
tableView.opaque = NO;
tableView.backgroundView = nil;
[super viewDidLoad];
}
Hope that works for you

Custom UITableViewCell redraw issues

I have a custom UITableView cell that I've added a textbox to for editing, that shows and hides based on the edit mode. I've also tried adding a vertical line that shows when editing, and it does that, but I'm running into some drawing issues. I just added a green checkmark rightView to start working on input validation feedback, and I'm seeing similar issues.
Here is the code for the cell, and part of my cellForRowAtIndexPath.
#import <UIKit/UIKit.h>
#interface EditableCellStyle2 : UITableViewCell {
CGRect editRect;
UITextField *editField;
UIView *lineView;
}
#property (nonatomic, readonly, retain) UITextField *editField;
#property (nonatomic, readonly, retain) UIView *lineView;
#end
#import "EditableCellStyle2.h"
#implementation EditableCellStyle2
#synthesize editField;
#synthesize lineView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code.
editRect = CGRectMake(83, 12, self.contentView.bounds.size.width-83, 19);
editField = [[UITextField alloc] initWithFrame:editRect];
editField.font = [UIFont boldSystemFontOfSize:15];
editField.textAlignment = UITextAlignmentLeft;
editField.textColor = [UIColor blackColor];
editField.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[self.contentView addSubview:editField];
self.editField.enabled = NO;
self.editField.hidden = YES;
lineView = [[UIView alloc] initWithFrame:CGRectMake(80, 0, 1, self.contentView.bounds.size.height)];
self.lineView.backgroundColor = [UIColor lightGrayColor];
[self.contentView addSubview:lineView];
self.lineView.hidden = YES;
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state.
}
-(void)layoutSubviews
{
[super layoutSubviews]; // layouts the cell as UITableViewCellStyleValue2 would normally look like
editRect = CGRectMake(83, 12, self.contentView.frame.size.width-self.detailTextLabel.frame.origin.x-10, 19);
editField.frame = editRect;
}
- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
if (state & UITableViewCellStateEditingMask) {
self.detailTextLabel.hidden = YES;
self.editField.enabled = YES;
self.lineView.hidden = NO;
self.editField.hidden = NO;
}
}
- (void)didTransitionToState:(UITableViewCellStateMask)state {
[super didTransitionToState:state];
if (!(state & UITableViewCellStateEditingMask)) {
self.editField.enabled = NO;
self.editField.hidden = YES;
self.lineView.hidden = YES;
self.detailTextLabel.hidden = NO;
self.editField.text = self.detailTextLabel.text;
}
}
- (void)dealloc {
[editField release];
[lineView release];
[super dealloc];
}
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// handling every section by hand since this view is essentially static. Sections 0, 1, 2, and 4 use a generic editable cell.
// Section 3 uses the multiline address cell.
static NSString *CellIdentifier = #"Cell";
EditableCellStyle2 *cell = (EditableCellStyle2 *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (indexPath.section == 0 || indexPath.section == 1 || indexPath.section == 2 || indexPath.section == 4) {
if (cell == nil) {
cell = [[[EditableCellStyle2 alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
}
// Configure the Odometer
if (indexPath.section == 0) {
NSArray *array = [sectionsArray objectAtIndex:indexPath.section];
NSDictionary *dictionary = [array objectAtIndex:indexPath.row];
cell.textLabel.text = #"Odometer";
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", [dictionary objectForKey:#"Odometer"]];
cell.tag = kOdometer;
cell.editField.text = cell.detailTextLabel.text;
cell.editField.placeholder = #"Odometer";
cell.editField.tag = kOdometer;
cell.editField.keyboardType = UIKeyboardTypeNumberPad;
// Create a view for the green checkmark for odometer input validation and set it as the right view.
UIImage *checkImage = [UIImage imageNamed:#"tick.png"];
UIImageView *checkImageView = [[[UIImageView alloc] initWithImage:checkImage] autorelease];
cell.editField.rightView = checkImageView;
cell.editField.rightViewMode = UITextFieldViewModeAlways;
}
return cell;
}
There is more to it but all the cells are built the same way.
The problems are that, when in edit mode, the vertical lines will display properly. When I leave edit mode, any cells that were off screen when I go to normal mode still have the vertical line (it doesn't get hidden). Also, now that I've added the imageView for the checkmark indicator, any cells that are off screen when switching modes gain the checkmark. (only section 0 sets it up).
I've also noticed that if i do cell.setNeedsDisplay, the text label and detail text label won't update if the data source has been updated. I have to do [self.tableView reloadData] which skips any active animations.
I'm sure these issues are related to me using a custom cell + dequeueReusableCellWithIdentifier, but I can't find exactly what.
Any feedback or a push in the right direction would be appreciated.
Edit:
Not using reusable cells seems to have resolved the above issues. I'm still open to feedback on the cell code.
I forgot one other issue that may or may not be related. One of my cells has a "tap to view list" button. If I enter data into the cells while in edit mode, then hit that button to choose some info from a list (it displays a modal table view), when I dismiss the modal view, all of the cells' edited data has reverted to their original state. I'm not calling reload data when I dismiss the modal view controller. I thought this might be fixed by not using reusable cells but it isn't.
You need to prepare the cell for reuse. Try adding this to the EditableCellStyle2 implementation:
- (void)prepareForReuse {
[super prepareForReuse];
[self didTransitionToState:UITableViewCellStateDefaultMask];
}
Maybe you trimmed too much for your post, but in the posted code your reusable cell handling is all wrong.
First of all, each different type of cell needs its own CellIdentifier. In your case (judging from your code comment), that means at least a different identifier for section 3 versus sections 0, 1, 2, and 4. You may also want to do a separate identifier for section 0, so you don't have to keep removing and readding that checkmark. The different identifier needs to be used for both the dequeueReusableCellWithIdentifier: and initWithStyle:reuseIdentifier:` for the appropriate sections.
The second problem is that you are not resetting the cells correctly. There are two "kinds" of initialization that must be done to a UITableViewCell: initialization that is the same for every cell of its type, and initialization that depends on the specific row being displayed. The first kind can (and should) only be done once, when a new cell is allocated. The second kind must be done every time through tableView:cellForRowAtIndexPath:. You seem to be doing the first correctly for your EditableTableCell2 class in its init method, but I see nowhere in there where you do the per-row initialization: you never reset selected, or the cell state, or the contents of the edit field, or remove the checkImageView since you are using the same kind of cell for section 0 versus the other sections. If you want, the reset selected, state, and clearing out the checkbox image and field contents can be done in prepareForReuse on your EditableTableCell2 class.
The third problem, which is almost certainly due to over-trimming, is that you never create this "multiline address" cell for section 3. You'll end up maybe reusing a random EditableTableCell2, or maybe crashing on an exception from the framework when you return nil from tableView:cellForRowAtIndexPath:.

How to implement a TableView where the background scrolls at the same time?

Imagine you have a normal table view where each row is an item on a conveyor belt. You will put the items in each cell of the table view but when you scroll you also want the background image (the conveyor belt) to scroll as well. How can you do this?
You should be able to accomplish this by setting the background color of the table view:
tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
Have the UITableView background transparent, and add a UIScrollView behind it with the UIImageView inside it. Add a listener for when the UITableView scrolls (since it is a subclass of UIScrollView it has all the same delegate methods). Then, when it scrolls, set the scroll position of the UIScrollView behind it to the same programmatically.
You could technically do it without a second UIScrollView behind the UITableView, just with a plain UIIImageView, if you want to reverse the offset values.
I haven't tried this, so I'm not sure what the best approach would be, but one option would be to add your background image as a UIImageView to each of your cells so that every cell has a full-sized copy of your background image. Set clipsToBounds to NO on your cell, and give the bounds of the UIImageView a negative y value equal to the offset from your cell to the top of the table.
You may also want to consider using UIScrollView instead of UITableView.
UITableView is itself a UIScrollView, so you could try just adding your background image as a subview of your UITableView, but I'd be surprised if that worked. I'm guessing the UITableView implementation won't play nice with foreign subviews.
** EDIT **
While I still suspect that UIScrollView may be a more appropriate base class to use here, I decided to try the UIImageView trick I described above. It's fairly simple and doesn't consume excessive memory as long as all your UIImageViews share a single UIImage. Here's my sample code:
// LadderCell.h
#import <UIKit/UIKit.h>
#interface LadderCell : UITableViewCell {
UIImageView *backgroundImageView;
UILabel *titleLabel;
}
#property (nonatomic, retain) UIImageView *backgroundImageView;
#property (nonatomic, retain) UILabel *titleLabel;
- (void)setIndexPath:(NSIndexPath *)indexPath;
- (id)initWithImage:(UIImage *)theImage;
+ (NSString *)reuseIdentifier;
+ (CGFloat)height;
#end
// LadderCell.m
#import "LadderCell.h"
#implementation LadderCell
#synthesize backgroundImageView, titleLabel;
- (void)dealloc {
self.backgroundImageView = nil;
self.titleLabel = nil;
[super dealloc];
}
- (id)initWithImage:(UIImage *)theImage {
if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[LadderCell reuseIdentifier]]) {
self.frame = CGRectMake(0.0, 0.0, 320.0, [LadderCell height]);
self.clipsToBounds = YES;
self.backgroundImageView = [[[UIImageView alloc] initWithImage:theImage] autorelease];
backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
self.titleLabel = [[[UILabel alloc] initWithFrame:self.bounds] autorelease];
titleLabel.textAlignment = UITextAlignmentCenter;
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
titleLabel.backgroundColor = [UIColor colorWithWhite:1 alpha:0];
[self addSubview:backgroundImageView];
[self addSubview:titleLabel];
}
return self;
}
- (void)setIndexPath:(NSIndexPath *)indexPath {
backgroundImageView.frame = CGRectMake(0.0,
-(CGFloat)indexPath.row * [LadderCell height] + 100.0,
backgroundImageView.frame.size.width,
backgroundImageView.frame.size.height);
}
+ (NSString *)reuseIdentifier {
return #"LadderCell";
}
+ (CGFloat)height {
return 30;
}
#end
// TableBackgroundTestViewController.h
#import
#interface TableBackgroundTestViewController : UITableViewController {
UIImage *backgroundImage;
}
#property (nonatomic, retain) UIImage *backgroundImage;
#end
// TableBackgroundTestViewController.m
#import "TableBackgroundTestViewController.h"
#import "LadderCell.h"
#implementation TableBackgroundTestViewController
#synthesize backgroundImage;
- (void)dealloc {
self.backgroundImage = nil;
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.backgroundImage = [UIImage imageNamed:#"background.png"];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
return 1000;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [LadderCell height];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LadderCell *cell = (LadderCell *)[tableView dequeueReusableCellWithIdentifier:[LadderCell reuseIdentifier]];
if (!cell) {
cell = [[[LadderCell alloc] initWithImage:self.backgroundImage] autorelease];
}
[cell setIndexPath:indexPath];
cell.titleLabel.text = [NSString stringWithFormat:#"Row %d", indexPath.row];
return cell;
}
#end
My suggestion is similar to Ed Marty (what he suggests "without a second UIScrollView"):
Place the UITableView inside a simple UIView. Make the cell backgrounds transparent so background from below the tableview would show
Below the UITableView, place an UIImageView with your desired background. Both the UITableView and UIImageView now sit inside the same enclosing UIView.
Listen to scroll events of UITableView. When detecting a scroll, simply change the background UIImageView position (frame.origin.y) appropriately, so that it would "stick with" the tableview.
You can have the background as one gigantic image, or have a series of them so you do tiling. You can have an array of the images and add to the array from top/bottom when needed, and also remove the images that have "scrolled away" from screen to conserve memory. You will need to calculate the positions for all these background images yourself, but there's nothing complicated in that.