Custom View add SubView layout issue - ios

There is a tableView with 2 sections, which have a customized section header view for each.
I want UI of this effect, and header view of section 0 works fine.
While header view of section 1 has an issue.
I don't know what's wrong.
What Apple mechanism do I miss,
convert rect/point?
Here is the code:
the customized view of section 1
#interface ZBWithDrawHeaderView()
#property (nonatomic, strong ) UIView *renderView;
#end
#implementation ZBWithDrawHeaderView
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor grayColor ];
_renderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, -17)];
//it works OK, I don't know the logic.
// _renderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, 17)];
//it works Wrong ,what mechanism do I miss
[self addSubview: _renderView];
_renderView.backgroundColor = [UIColor redColor ];
}
return self;
}
#end
Where I put it into use?
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
if(section == 0){
......
return view;
}
else if (section == 1) {
ZBWithDrawHeaderView * secondHeaderView = [[ZBWithDrawHeaderView alloc] initWithFrame: CGRectMake(0, 0, KScreenWidth, 25)];
return secondHeaderView;
}
return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return CGFLOAT_MIN;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
return [UIView new ];
}
The two UITableViewDelegate methods added, no strange layout happens. Seems like section header and footer must come in pair.

Related

Add a button on the custom UITableViewHeaderFooterView

I am trying to add a button programmatically on the custom UITableViewHeaderFooterView class as follows, it does not appear.
I wonder what I am missing or doing wrong ?
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
UIButton * clickBtn = [[UIButton alloc]initWithFrame:CGRectMake(self.contentView.bounds.size.width-50, 0, 50, 50)];
clickBtn.imageView.image = [UIImage imageNamed:#"carat-open.png"];
[self.contentView addSubview:clickBtn];
}
return self;
}
Create UIButton inside of this function and add button as a subview to header view
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIButton * clickBtn = [[UIButton alloc]initWithFrame:CGRectMake(self.contentView.bounds.size.width-50, 0, 50, 50)];
clickBtn.imageView.image = [UIImage imageNamed:#"carat-open.png"];
UIView *headerView = [[UIView alloc] init];
[headerView addSubview:clickBtn];
headerView.backgroundColor=[UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1.0];
return headerView;
}
To add custom view in Section Footer:
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 20;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return [self getViewForFooterForSection];
}
- (UIView*)getViewForFooterForSection
{
UIView *sectionFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
sectionFooterView.backgroundColor = [UIColor redColor];
return sectionFooterView;
}
To add custom view for section footer from custom view file
CustomFooter
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
self.frame = frame;
self.backgroundColor = [UIColor blueColor];
}
return self;
}
View Controller
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return [self getViewForFooterForSection];
}
- (UIView*)getViewForFooterForSection
{
CustomFooter *sectionFooterView = [[CustomFooter alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
return sectionFooterView;
}
To add custom view in Table footer View
tbl.tableFooterView = [self getViewForTableView];
- (UIView*)getViewForTableView
{
UIView *tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
tableFooterView.backgroundColor = [UIColor greenColor];
return tableFooterView;
}
you can't set a button image like that .
clickBtn.imageView.image = [UIImage imageNamed:#"carat-open.png"];
imageView property is readonly.
use this one
(void)setImage:(nullable UIImage *)image forState:(UIControlState)state;

CKComponentKit CollectionView issue

I am currently experimenting with ComponentKit and I am having an issue. I want to use the CKCollectionView I created on a main view. When I had the CKCollectionView to its container in my MainView(which contains a ScrollView) I am having 2 issues. The first one, if I manually create the frame of the CollectionView, here what it gives me.(Blue square is the "UICollectionViewControllerWrapperView")
But, what I really want to do is set the frame using Masonry and constraints, allowing me to set the frame equal to its container (the yellow square). But when I do that, it FILLS the entire Blue rectangle height with empty cells, like in the picture below:
So it seems like my actual cells (or Components) have a size of 647 and they are causing problems.But at this point I am lost and was wondering if anybody had a similar issue before :).
Here is my code:
The actual component that creates the cell:
#implementation SSOUpcomingAuctionComponent
+ (instancetype)newWithProduct:(Product *)product {
SSOProductHeaderComponent *head = [SSOProductHeaderComponent newWithTitle:product.time];
SSOProductPictureComponent *body = [SSOProductPictureComponent newWithImage:product.image];
SSOProductShareComponent *footer = [SSOProductShareComponent newWithPrice:product.price];
return [super newWithComponent:[CKInsetComponent newWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)
component:[CKStackLayoutComponent newWithView:{
[UIView class], {
{ #selector(setBackgroundColor:), [UIColor whiteColor] }
, { CKComponentViewAttribute::LayerAttribute(#selector(setCornerRadius:)), #6.0 }
, { #selector(setClipsToBounds:), #YES }
}
} size:{}
style:{
.alignItems = CKStackLayoutAlignItemsCenter, .direction = CKStackLayoutDirectionVertical,
.spacing = 2
} children:{{
head,
},
{body},
{footer}}]]];
}
#pragma mark - ProviderMethod
#end
The CollectionViewController:
#import "UpcomingAuctionViewController.h"
#import "SSOUpcomingAuctionComponent.h"
#import <ComponentKit/ComponentKit.h>
#import "Product.h"
#import "ProductPages.h"
#import "ProductModelController.h"
#import <ChameleonFramework/Chameleon.h>
#import "Masonry.h"
#interface UpcomingAuctionViewController () <CKComponentProvider, UICollectionViewDelegateFlowLayout>
#end
#implementation UpcomingAuctionViewController {
CKCollectionViewDataSource *_dataSource;
CKComponentFlexibleSizeRangeProvider *_sizeRangeProvider;
ProductModelController *_productModelController;
}
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout {
if (self = [super initWithCollectionViewLayout:layout]) {
_sizeRangeProvider = [CKComponentFlexibleSizeRangeProvider providerWithFlexibility:CKComponentSizeRangeFlexibleWidth];
_productModelController = [[ProductModelController alloc] init];
self.automaticallyAdjustsScrollViewInsets = NO;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.collectionView.backgroundColor = [UIColor flatWhiteColorDark];
// [self.collectionView setFrame:CGRectMake(0, 0, 320, 180)];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
[self.collectionView setScrollEnabled:YES];
self.collectionView.delegate = self;
// Creates and sets our dataSource to our CKCollectionViewDataSource, THE MAIN ACTOR/INFRASTRUCTURE that takes our MODEL, creates a COMPONENT STACKS then
// transforms it into a VIEW HIERARCHY.
_dataSource = [[CKCollectionViewDataSource alloc] initWithCollectionView:self.collectionView
supplementaryViewDataSource:nil
componentProvider:[self class]
context:nil
cellConfigurationFunction:nil];
// The following block of code adds a section at 0 and two items at 0 and 1.
CKArrayControllerSections sections;
// insert section 0
sections.insert(0);
[_dataSource enqueueChangeset:{
sections, {}
} constrainedSize:{}];
[self enqueueProductPages:[_productModelController fetchNewUpcomingProductsWithCount:14]];
}
- (void)enqueueProductPages:(ProductPages *)productPage {
NSArray *products = productPage.products;
NSInteger position = productPage.position;
CKArrayControllerInputItems items;
for (NSInteger i = 0; i < products.count; i++) {
items.insert([NSIndexPath indexPathForRow:position + i inSection:0], products[i]);
}
[_dataSource enqueueChangeset:{
{}
, items
} constrainedSize:[_sizeRangeProvider sizeRangeForBoundingSize:self.collectionView.bounds.size]];
}
#pragma mark - CKComponentProvider
// Method that our componentProvider class NEED to implement
+ (CKComponent *)componentForModel:(Product *)product context:(Product *)context {
return [SSOUpcomingAuctionComponent newWithProduct:product];
}
#pragma mark - UICollectionViewDelegateFlowlayout
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return [_dataSource sizeForItemAtIndexPath:indexPath];
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[_dataSource announceWillAppearForItemInCell:cell];
}
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[_dataSource announceDidDisappearForItemInCell:cell];
}
And finally the mainViewController:
/**
* Initialize upcoming auctions container controller
*/
- (void)initializeUpcomingAuctionsContainerView {
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setMinimumInteritemSpacing:0];
[flowLayout setMinimumLineSpacing:10];
flowLayout.sectionInset = UIEdgeInsetsMake(0, 15, 0, 0);
self.upcomingAuctionsVC = [[UpcomingAuctionViewController alloc] initWithCollectionViewLayout:flowLayout];
[self addChildViewController:self.upcomingAuctionsVC];
[self.bottomView addSubview:self.upcomingAuctionsVC.view];
[self.upcomingAuctionsVC didMoveToParentViewController:self];
}
#end
Thanks for the help everyone and have a great day.

UITableview/UIView Delegate Confusion

I am making my own drop down menu because I am not able to find an open source one that does exactly what I need.
I have implemented the dropdown as a UIView and am adding it to the superview of the button that is tapped in order to show it.
Code:
ViewController.m
#import "ViewController.h"
#import "MenuView.h"
#interface ViewController () <MenuViewDelegate>
#property (weak, nonatomic) IBOutlet UIView *fakeHeader;
#property (nonatomic, strong) MenuView *menuView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnTapped:(id)sender {
NSArray *array = #[#"Item 1", #"Item 2", #"Item 3", #"Item 4"];
NSArray *imgArray = nil;
if (self.menuView == nil) {
self.menuView = [[MenuView alloc] showDropDownWith:sender txtArr:array imgArr:imgArray direction:#"down" delegate:self];
self.menuView.delegate = self;
} else {
[self.menuView hideDropDown:sender];
self.menuView = nil;
}
}
- (void) menuDelegateMethod:(MenuView *)sender {
self.menuView = nil;
}
#end
MenuView.h
#import <UIKit/UIKit.h>
#class MenuView;
#protocol MenuViewDelegate
- (void)menuDelegateMethod:(MenuView *)sender;
#end
#interface MenuView : UIView
#property (nonatomic, retain) id <MenuViewDelegate> delegate;
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate;
- (void)hideDropDown:(UIButton *)button;
#end
MenuView.m
#import "MenuView.h"
#import "QuartzCore/QuartzCore.h"
#interface MenuView () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *table;
#property (nonatomic, strong) UIButton *btnSender;
#property (nonatomic, retain) NSString *animationDirection;
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) NSArray *imageList;
#end
#implementation MenuView
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
CGFloat realHeight = 40 * txtArr.count;
self.btnSender = button;
self.animationDirection = direction;
self.table = (UITableView *)[super init];
if (self) {
// Initialization code
CGRect btn = button.frame;
self.list = [NSArray arrayWithArray:txtArr];
self.imageList = [NSArray arrayWithArray:imgArr];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - btn.size.height) , width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
} else if ([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
}
self.layer.masksToBounds = YES;
self.layer.shadowOpacity = 0.2;
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
self.table.delegate = delegate;
self.table.dataSource = self;
self.table.backgroundColor = [UIColor colorWithRed:0.239 green:0.239 blue:0.239 alpha:1];
self.table.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.table.separatorColor = [UIColor lightGrayColor];
self.table.backgroundColor = [UIColor whiteColor];
self.table.userInteractionEnabled = YES;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - realHeight), width, realHeight);
} else if([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, realHeight);
}
self.table.frame = CGRectMake(0, 0, width, realHeight);
[UIView commitAnimations];
[button.superview addSubview:self];
self.table.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
[self addSubview:self.table];
}
return self;
}
- (void)hideDropDown:(UIButton *)button {
CGRect btn = button.frame;
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([self.animationDirection isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, btn.origin.y, width, 0);
} else if ([self.animationDirection isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
}
self.table.frame = CGRectMake(0, 0, width, 0);
[UIView commitAnimations];
}
#pragma mark - Table View DataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.font = [UIFont systemFontOfSize:15];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
}
cell.textLabel.text = [self.list objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f];
cell.textLabel.textColor = [UIColor lightTextColor];
return cell;
}
#pragma mark - Table View Delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 40;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
[self myDelegate];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// Remove seperator inset
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
// Prevent the cell from inheriting the Table View's margin settings
if ([cell respondsToSelector:#selector(setPreservesSuperviewLayoutMargins:)]) {
[cell setPreservesSuperviewLayoutMargins:NO];
}
// Explictly set your cell's layout margins
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
#pragma mark - View Delegate
- (void)myDelegate {
[self.delegate menuDelegateMethod:self];
}
#end
Shows up perfectly but the didSelect method is never called.
I don't have any views over the top of it that would be stealing the touch events.
It seems that UIViews might not be able to be UITableviewDelegates. If that's true I don't know why, when I make the calling view controller the delegate, it still fails to didSelect.
NOTE: I am aware of the animation faux pas by not using newer methods. This is based on old example code. I will update the animation after I get this issue worked out.
Questions:
Is it true that UIViews can not be UITableView Delegates?
If so, how does one make a calling view controller the delegate for the table view that resides in the UIView? Other than the process of setting it up as a UITableViewDelegate and assigning the calling view controller as the delegate at the time of the creation of the table.
Did I miss something in the way I set this up that steals the cell taps so that didSelect does not get called, either in the view or the viewController?
Thanks for the help.
Agree with #Piotr that the menu must be the table's delegate, so replace self.table.delegate = delegate; with self.table.delegate = self; in MenuView.m.
But additionally, MenuView.m never invokes its delegate, which it should upon selection in the tableview....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
// remove this, since it does nothing
//[self myDelegate];
// replace it with
[self.myDelegate menuDelegateMethod:self];
}
That last line says, tell the delegate of the menu that something happened.
Another problem is that the menu doesn't really tell the delegate what happened. Certainly the delegate will be interested in which item is selected. Consider changing the protocol to something like:
#protocol MenuViewDelegate
- (void)menuView:(MenuView *)sender didSelectOptionAtIndex:(NSInteger)index;
#end
// calling it
[self.myDelegate menuView:self didSelectOptionAtIndex:indexPath.row];
Another alternative is to hand back the selected string to the delegate. This can be found in the tableview's datasource at the indexPath.row.
Finally, its good practice to not-retain your delegate since the customer of the Menu might retain it, resulting in a retain cycle. Instead, declare the delegate:
// notice "weak"
#property (nonatomic, weak) id <MenuViewDelegate> delegate;
As I can see, you are passing ViewController (it is MenuViewDelegate) to showDropDownWith method, and then use it as table delegate. This is not correct.
You should pass self there (same as with data source), because you want MenuView to be delegate of table, not ViewController, right?
self.table.delegate = self;

Unrecognized selector sent to instance when adding a UITableView as SubView

When i've created a UITableView in the code en add it as a subview in the code it is all working fine. There is green tableview visible.
Example of the code i've created for that.
- (void)getOverviewTable
{
// Adding the TableView
OverviewTableViewController *overviewTableViewController = [[OverviewTableViewController alloc] init];
UITableView *overviewTableView = [[UITableView alloc] initWithFrame:(CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))];
// Set the datasource and delegete will come here
[overviewTableView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:overviewTableView];
}
But after i've set the DataSource and the Delegete for the TableView i will receive a warning.
The code in total for reaching it is as follows:
- (void)getOverviewTable
{
// Adding the TableView
OverviewTableViewController *overviewTableViewController = [[OverviewTableViewController alloc] init];
UITableView *overviewTableView = [[UITableView alloc] initWithFrame:(CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))];
// Set the datasource and delegete will come here
[overviewTableView setDataSource:overviewTableViewController];
[overviewTableView setDelegate:overviewTableViewController];
[overviewTableView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:overviewTableView];
}
The most of the time it is just an EXC_BAD_ACCESS error, but sometimes it shows me the following error.
[UITransitionView numberOfRowsInSection:]: unrecognized selector sent to instance
The 'OverViewTableViewController' is replaced by just an empty UITableViewController class but the error is still showing up.
(Point of the application is an fullscreen takeover which has to be swiped away to see the tableview underneath it)
Edit:
The function is called from the viewDidLoad function
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"bg.jpg"]]];
// Setting the home takeover
[self setHomeTakeOver];
// Wait one seconde before we load the tableview
[self getOverviewTable];
//[self performSelector:#selector(getOverviewTable) withObject:nil afterDelay:1.0];
}
What I think is you have your project ARC enabled and from your code segment what i understand is that overviewTableViewController is released through ARC as soon as it leaves the scope of the function getOverviewTable. So, better declare the overviewTableViewController in .h file(#property (nonatomic, strong) OverviewTableViewController *overviewTableViewController; ).
If you do this then the code will look like this
in .h file
#property (nonatomic, strong) OverviewTableViewController *overviewTableViewController;
//// I have strong reference rather than assign
in .m file
- (void)getOverviewTable
{
// Adding the TableView
overviewTableViewController = [[OverviewTableViewController alloc] init]; //// I have made overviewTableViewController as member variable.
UITableView *overviewTableView = [[UITableView alloc] initWithFrame:(CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))];
// Set the datasource and delegete will come here
[overviewTableView setDataSource:overviewTableViewController];
[overviewTableView setDelegate:overviewTableViewController];
[overviewTableView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:overviewTableView];
[overviewTableView reloadData];
}
Also be sure that OverviewTableViewController has implemented the datasource methods like
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
Try this.
[overviewTableView setDataSource:self];
[overviewTableView setDelegate:self];
Also confirm to tableviewDelegate and datasource and implement following methods.
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier=#"CellIdentifier";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
You might have forgotten to implement this method.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
}
Try setting the table view controller's tableView property to overviewTableView. I don't know off hand if the UITableViewController must have that property set.
- (void)getOverviewTable
{
// Adding the TableView
OverviewTableViewController *overviewTableViewController = [[OverviewTableViewController alloc] init];
UITableView *overviewTableView = [[UITableView alloc] initWithFrame:(CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))];
// Set the datasource and delegete will come here
[overviewTableView setDataSource:overviewTableViewController];
[overviewTableView setDelegate:overviewTableViewController];
//THIS IS THE NEW LINE
//a UITableViewController needs to know UITableView that it's managing
[overviewTableViewController setTableView:overviewTableView];
[overviewTableView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:overviewTableView];
}

How to add a custom separator to UITableViewCell?

Please spare sometime as this is a long explanation
I have a UIViewController which consists of a UIButton and a UITableView which loads different types of UITableViewCells with Identifier Cell1 and Cell2, on event touchUpInside of the button. I m using storyboard.
The separator for both cells are customized.
Cell1 has a separator that occupies the entire width of cell and 1 pixel height at the bottom of the cell.
Whereas Cell2 has a separator which has offset of 5 pixels from the cell, both left and right.
Each time the button outside the tableView is clicked the tableViewCells are swapped, based on the cell identifier.
Initially the tableView occupies the complete width of viewController and consists of Cell1, but the the button is tapped , tableViewCells are changed to Cell2 and the frame of the tableView is Changed, The width is reduced by 10 and x-origin is increased by 5.
But when this happens, the separator of Cell2 is 5 pixels away from the cell on right but on the left it is away by 5 pixel.
This happens for all Cell2 which is loaded with data, and the cells which has no data the frame is changed appropriately.
But the cell after that has the width of Cell1 (larger width)
-(void)setSeperatorStyleForTableView :(UITableViewCell *)cell //this is called in cellForRowAtIndex
{
//cell- type of cell(Cell1 or Cell2)
CGRect seperatorFrame;
UIImageView *seperatorImage;
seperatorFrame = [self setSeperatorFrame:cell];
if(firstCellToBeLoaded)//BOOL used to change the button text and load appropriate cells
{
seperatorImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"table_row
2.png"]];
}
else
{
seperatorImage = [[UIImageView alloc] initWithImage:[UIImage
imageNamed:#"table_row.png"]];
}
seperatorImage.frame = seperatorFrame;
seperatorImage.autoresizingMask = YES;
[cell.contentView addSubview:seperatorImage];
}
//set the customized separator frame
-(CGRect)setSeperatorFrame :(UITableViewCell *)cell
{
CGRect seperatorFrame;
seperatorFrame.size.height = 1.0;
seperatorFrame.origin.y = cell.frame.origin.y + (cell.frame.size.height - 1.0);
if(firstCellToBeLoaded)
{
seperatorFrame.origin.x = cell.frame.origin.x ;
seperatorFrame.size.width = cell.frame.size.width;
}
else
{
seperatorFrame.origin.x = cell.frame.origin.x + 5.0;
seperatorFrame.size.width = cell.frame.size.width -10.0;
}
return seperatorFrame;
}
You can add tableView's standard separator line, and add your custom line at the top of each cell.
In following code Change hight/width/color/image of UIView for set your separatorLine.
The easiest way to add custom separator is to add simple UIView of 1px height:
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 1)];/// change size as you need.
separatorLineView.backgroundColor = [UIColor grayColor];// you can also put image here
[cell.contentView addSubview:separatorLineView];
This code might solve your problem :)
Right way to do this would be to have the separator with in the cell class, subclass UITableViewCell if you haven't add a separator image variable there and on each cell creation you can just change the image and the frame rather than adding that on each redraw. If you require a code for this i can supply that as well. Currently when the cell is redrawn it already has the image u added last time and you just adding that again, either you remove that in -prepareForReuse method or just do as i explained above.
***** Custom Cell *****
//
// CustomCell.m
// Custom
//
// Created by Syed Arsalan Pervez on 2/8/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import "CustomCell.h"
#implementation CustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
_separatorImage = [[UIImageView alloc] initWithFrame:CGRectZero];
[[self contentView] addSubview:_separatorImage];
}
return self;
}
- (void)prepareForReuse
{
_separatorImage.image = nil;
}
- (void)dealloc
{
[_separatorImage release];
[super dealloc];
}
#end
Using the above cell in the view controller.
***** Test View Controller *****
//
// TestViewController.m
// Custom
//
// Created by Syed Arsalan Pervez on 2/8/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import "TestViewController.h"
#import "CustomCell.h"
#interface TestViewController ()
#end
#implementation TestViewController
- (void)viewDidLoad
{
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
#warning TODO: set the image name here
_separatorImage1 = [[UIImage imageNamed:#""] retain];
_separatorImage2 = [[UIImage imageNamed:#""] retain];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *_identifier = #"CustomCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:_identifier];
if (!cell)
{
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:_identifier] autorelease];
}
//Set Separator Image Here
//Preload the image so it doesn't effect the scrolling performance
CGRect frame = cell.contentView.frame;
switch (indexPath.row)
{
case 0:
cell.separatorImage.image = _separatorImage1;
cell.separatorImage.frame = CGRectMake(0, CGRectGetMaxY(frame)-1, frame.size.width, 1);
break;
case 1:
cell.separatorImage.image = _separatorImage2;
cell.separatorImage.frame = CGRectMake(frame.origin.x+5, CGRectGetMaxY(frame)-1, frame.size.width-10, 1);
break;
}
return cell;
}
- (void)dealloc
{
[_separatorImage1 release];
[_separatorImage2 release];
[super dealloc];
}
#end
I do it this way... hope this might help.
//
// UITableViewCell+MyAdditions.h
//
// Created by Roberto O. Buratti on 19/02/14.
//
#import <UIKit/UIKit.h>
#interface UITableViewCell (MyAdditions)
#property (nonatomic,assign) UITableViewCellSeparatorStyle cellSeparatorStyle;
#property (nonatomic,strong) UIColor *cellSeparatorColor;
#end
//
// UITableViewCell+MyAdditions.m
//
// Created by Roberto O. Buratti on 19/02/14.
//
#import "UITableViewCell+MyAdditions.h"
NSString *const kUITablewViewCellSeparatorLayerName = #"kUITablewViewCellSeparatorLayerName";
#implementation UITableViewCell (MyAdditions)
-(CALayer *)separatorLayer
{
for (CALayer *sublayer in self.layer.sublayers)
{
if ([sublayer.name isEqualToString:kUITablewViewCellSeparatorLayerName])
return sublayer;
}
return nil;
}
-(CALayer *)newSeparatorLayer
{
CALayer *separatorLayer = [CALayer layer];
separatorLayer.name = kUITablewViewCellSeparatorLayerName;
separatorLayer.frame = CGRectMake(0, self.bounds.size.height - 1, self.bounds.size.width, 1);
separatorLayer.backgroundColor = [UIColor whiteColor].CGColor;
[self.layer addSublayer:separatorLayer];
return separatorLayer;
}
-(UITableViewCellSeparatorStyle)cellSeparatorStyle
{
CALayer *separatorLayer = [self separatorLayer];
if (separatorLayer == nil)
return UITableViewCellSeparatorStyleNone;
else
return UITableViewCellSeparatorStyleSingleLine;
}
-(void)setCellSeparatorStyle:(UITableViewCellSeparatorStyle)separatorStyle
{
CALayer *separatorLayer = [self separatorLayer];
switch (separatorStyle)
{
case UITableViewCellSeparatorStyleNone:
[separatorLayer removeFromSuperlayer];
break;
case UITableViewCellSeparatorStyleSingleLine:
if (separatorLayer == nil)
separatorLayer = [self newSeparatorLayer];
break;
default:
#throw [NSException exceptionWithName:NSStringFromClass([self class]) reason:#"Unsupported separatorStyle" userInfo:nil];
break;
}
}
-(UIColor *)cellSeparatorColor
{
CALayer *separatorLayer = [self separatorLayer];
return [UIColor colorWithCGColor:separatorLayer.backgroundColor];
}
-(void)setCellSeparatorColor:(UIColor *)separatorColor
{
CALayer *separatorLayer = [self separatorLayer];
if (separatorLayer == nil)
separatorLayer = [self newSeparatorLayer];
separatorLayer.backgroundColor = separatorColor.CGColor;
}
#end
Now you can do things like
UITableViewCell *cell = ...
cell.cellSeparatorStyle = UITableViewCellSeparatorStyleSingleLine;
cell.cellSeparatorColor = [UIColor orangeColor];
As others said, usually there's two ways to do this, either create a CALayer or a UIView separator of 1px and add it to contentView.
Over time I've seen multiple projects do this differently, and sometimes, even multiple different ways in the same project. It's also easy to introduce bugs because of cell reuse and also, for proper rendering of pixel lines, one must incorporate the screen scale: (1.0 / [UIScreen mainScreen].scale).
I've created a library that simplifies this to just a single method class, with no subclassing required.
https://github.com/kgaidis/KGViewSeparators
Objective-C:
[view kg_show:YES separator:KGViewSeparatorTop color:[UIColor blackColor] lineWidth:KGViewSeparatorLineWidth(1.0) insets:UIEdgeInsetsMake(0, 15.0, 0, 15.0)];
Swift:
view.kg_show(true, separator: .Bottom, color: UIColor.blackColor(), lineWidth: KGViewSeparatorLineWidth(1.0), insets: UIEdgeInsetsZero)

Resources