I am new in objective c. I want to create a layout design like android app store. Which means, i have a tableview and each row of tableview has a collectionview, and one more thing, i am using xcode7 and i want to create a design with storyboard. When i should create a that type of design each tableview row show collectionview but my problem is that each collectionview have save data. but i want to different data in every row. I go throw many tutorial but i can't understand. Can anyone assist me, how to create that type of design? Now i am create a tableview datasourse and delegate method in viewcontroller.m file and also use collection view's datasourse and delegate method into custom tableview cells.m class. please any one assist me
each collectionview have save data. but i want to different data in every row
because your collection view's datasourse and delegate method in tableViewCell is same.maybe it's not ok to determine dataSource and delegate implementation in tableViewCell ,you can delegate it out to your ViewController, and each tableViewCell's has different implementation.
I have try to implementation a demo , it can work ,but i think may have better way. this is my code
Cell.h
//Cell.h
#import <UIKit/UIKit.h>
#class MTTableViewCell;
#protocol MTTableViewCellDelegate <NSObject>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section InTableViewCell:(MTTableViewCell *)cell;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:( NSIndexPath *)indexPath InTableViewCell:(MTTableViewCell *)cell;
#end
#interface MTTableViewCell : UITableViewCell<UICollectionViewDelegateFlowLayout,UICollectionViewDataSource>
#property (nonatomic, weak) id<MTTableViewCellDelegate> delegate;
#property (nonatomic, strong) UICollectionView *collectionView;
#property (nonatomic, assign) NSInteger index; // [tablview indexPathForCell:] not work before cell is render
#end
Cell.m
//Cell.m
#import "MTTableViewCell.h"
#interface MTTableViewCell()
#end
#implementation MTTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
_collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];
}
return self;
}
#pragma mark -
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (_delegate && [_delegate respondsToSelector:#selector( collectionView:numberOfItemsInSection:InTableViewCell:)]) {
return [_delegate collectionView:collectionView numberOfItemsInSection:section InTableViewCell:self];
}
return 0;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:( NSIndexPath *)indexPath {
if (_delegate && [_delegate respondsToSelector:#selector( collectionView:cellForItemAtIndexPath:InTableViewCell:)]) {
return [_delegate collectionView:collectionView cellForItemAtIndexPath:indexPath InTableViewCell:self];
}
return nil;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *) collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(50, 50);
}
#end
ViewController.m
#import "ViewController.h"
#import "MTTableViewCell.h"
#interface ViewController ()<UITableViewDataSource,UITableViewDelegate,MTTableViewCellDelegate>
#property (nonatomic, strong) UITableView *tableView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.rowHeight = 72.0;
[self.view addSubview:_tableView];
}
#pragma mark - UITableViewDataSource & UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MTTableViewCell *cell = (MTTableViewCell*) [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell) {
cell = [[MTTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.index = indexPath.row;
cell.delegate = self;
[cell.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"CCell"];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:( NSIndexPath *)indexPath {
MTTableViewCell *mtCell = (MTTableViewCell *)cell;
if (!mtCell.collectionView.superview) {
[mtCell addSubview:mtCell.collectionView];
mtCell.collectionView.delegate = mtCell;
mtCell.collectionView.dataSource = mtCell;
}
}
#pragma mark - MTTableViewCellDelegate
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:( NSIndexPath *)indexPath InTableViewCell:(MTTableViewCell *)cell {
UICollectionViewCell *cCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CCell" forIndexPath:indexPath];
cCell.backgroundColor = [UIColor redColor];
return cCell;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section InTableViewCell:(MTTableViewCell *)cell {
[_tableView indexPathForCell:cell];
return cell.index
;
}
#end
Hope this can help you :)
Related
I have a collectionView inside a view. I did setup hte datasource, the delegate and everything.
This cell is working:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [self.collectionAddedMembers dequeueReusableCellWithReuseIdentifier:cellReuseIdentifierAdded forIndexPath:indexPath];
cell.backgroundColor = [colores objectAtIndex:arc4random_uniform(6)];
return cell;
}
But on my custom cell AddedMemberCollectionViewCell it dont adds new cells:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
AddedMemberCollectionViewCell *cell = [self.collectionAddedMembers dequeueReusableCellWithReuseIdentifier:cellReuseIdentifierAdded forIndexPath:indexPath];
NSArray *firstSection = [selectedMembersData objectAtIndex:0];
SelectedContacto *cellData = [firstSection objectAtIndex:indexPath.row];
NSMutableDictionary *dataForConfigure = [[NSMutableDictionary alloc] init];
[dataForConfigure setObject:cellData.contactAvatar forKey:#"contactAvatar"];
[dataForConfigure setObject:cellData.contactName forKey:#"contactName"];
[dataForConfigure setObject:cellData.memberId forKey:#"contactName"];
[cell configure:dataForConfigure];
return cell;
}
This is the code of the custom cell.
.h file:
#interface AddedMemberCollectionViewCell : UICollectionViewCell
#property (nonatomic) IBOutlet UIImageView *avatar;
#property (nonatomic) IBOutlet UILabel *memberName;
#property (nonatomic) IBOutlet UIButton *removeMember;
#property (nonatomic) NSNumber *memberId;
- (void) configure:(NSDictionary*)cellData;
#end
.m file
#import "AddedMemberCollectionViewCell.h"
#implementation AddedMemberCollectionViewCell
- (void) configure:(NSDictionary*)cellData {
self.avatar = [cellData objectForKey:#"contactAvatar"];
self.memberName = [cellData objectForKey:#"contactName"];
self.memberId = [cellData objectForKey:#"memberId"];
}
#end
I could provide more code, but I am thinking would not be necessary. What is happening here?
EDIT:
Registering in viewDidLoad:
self.searchBar.delegate = self;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.collectionAddedMembers.dataSource = self;
self.collectionAddedMembers.delegate = self;
[self.collectionAddedMembers registerClass:AddedMemberCollectionViewCell.class forCellWithReuseIdentifier:cellReuseIdentifierAdded];
Are you sure that the cell is not actually added to your collection view?
However, you were not doing it right, as calling dequeueReusableCellWithReuseIdentifier will NOT create UICollectionViewCell but re-use an existing cell, hence your initialization code in init will never have chance to be called.
A good approach should look like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = (CustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
NSDictionary *cellData = [data objectAtIndex:indexPath.row];
// Call custom cell's configure function to init cell content
[cell configure:cellData];
return cell;
}
#interface CustomCell : UICollectionViewCell
-(void)configure:(NSDictionary *)data;
#end
#implementation CustomCell
-(void)configure:(NSDictionary *)data {
// Init your cell content
}
#end
[update - collection view is nil for some reason...]
None of the collection view delegate methods (cellforitemat, numberofitemsinsection, sizeforitem etc) are being called at all for some reason. I have set the datasource and delegate. I've tried it in interface builder and in code in view did load. Am I missing something??
in the .h:
#interface ViewController : UIViewController<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
#property (nonatomic, strong) NSArray *workoutList;
in the .m (workoutlist.count is 3 on print statement in VDL but not run elsewhere):
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.workoutCV.backgroundColor = [UIColor blackColor];
self.view.backgroundColor = [UIColor blackColor];
self.workoutCV.delegate = self;
self.workoutCV.dataSource = self;
NSLog(#"here %lu", (unsigned long)self.workoutList.count);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
NSLog(#"and here %lu", (unsigned long)self.workoutList.count);
return self.workoutList.count;
}
- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
static NSString * const cellId = #"HomeCell";
HomeMenuCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(CGRectGetWidth(collectionView.frame), CGRectGetHeight(collectionView.frame) / self.workoutList.count);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
I am segue from a different Viewcontroller (HomeController) to get to this new controller. This is the code from the .m of the HomeController:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
ViewController *nextVC = [[ViewController alloc] init];
nextVC.workoutList = #[#"Squats", #"Stiff Leg DL", #"Abs"];
[self.navigationController pushViewController:nextVC animated:true];
}
It pushes the nextVC fine and print statements run but no cells show up and no print statements run from any of the collection view delegate methods
I have this UItableView:
I would like to increment/decrement the concrete UIProgressView at row, when I clicked on minus/plus and than also reload, whole UITableView, because all rows are interdependent. I don't how detect on which row was pressed the minus/plus UIButton. I tried to use rowDidSelect, but it didn't work. Could you please advise me please?
EDITED
I have a class for UITableCell (ActivityCell - class) and class for UITableViewController (lessonActivityTVC - class). At class of Cell I have IBOutlets.
ActivityCell.h
#interface ActivityCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *studentNameLabel;
#property (strong, nonatomic) IBOutlet UIProgressView *progressView;
In ActivityCell.m I have nothing new. I had here a IBActions for buttons, but I moved the actions into lessonActivity.m because, when I press any button I need reload the UITableView and I didnt know how do it in ActivityCell.m. My code of lessonActivityTVC.m is here:
#implementation tchLessonActivityTVC
#define debug 1
- (IBAction)incrementActivity:(id)sender {
NSLog(#"increment");
}
- (IBAction)decrementActivity:(id)sender {
NSLog(#"decrement");
}
-(void)fetchData{
CoreDataHelper *cdh = [(AppDelegate*)[[UIApplication sharedApplication]delegate]cdh];
SchoolClass *schoolClass = (SchoolClass*)[cdh.context existingObjectWithID:[IDsManager getClassID] error:nil];
Lesson *lesson = (Lesson*)[cdh.context existingObjectWithID:[IDsManager getLessonID] error:nil];
if (lesson.activities.count == 0) {
for (Student *student in schoolClass.students) {
Activity *activity = [NSEntityDescription insertNewObjectForEntityForName:#"Activity" inManagedObjectContext:cdh.context];
activity.student = student;
activity.lesson = lesson;
[lesson addActivitiesObject:activity];
[student addActivitiesObject:activity];
}
}
self.activities = [NSArray arrayWithArray:[lesson.activities allObjects]];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
[self fetchData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.activities.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(debug == 1){
NSLog(#"Running %# '%#'",self.class, NSStringFromSelector(_cmd));
}
static NSString *cellIndetifier = #"Activity Cell";
ActivityCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndetifier
forIndexPath:indexPath];
Activity *activity = [self.activities objectAtIndex:indexPath.row];
cell.studentNameLabel.text = activity.student.name;
CGAffineTransform transform = CGAffineTransformMakeScale(1.0f, 5.0f);
cell.progressView.transform = transform;
cell.progressView.progress = 0.32f;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"selected index is %d",indexPath.row);
}
#end
UITableView has a delegate method didSelectRowAtIndexPath: and you get the indexPath.row from there. Don't forget to set your class as a UITableViewDelegate by adding <UITableViewDelegate> on the end of #interface line and setting
tableView.delegate = self;
in code, or do it in Interface Builder. If you are on UITableViewController, you get this for free.
Edit:
Here is the delegate pattern example:
tableviewcell h
#protocol TableViewCellDelegate <NSObject>
- (void)requestToReloadTableViewFromCell:(UITableViewCell *)cell;
#end
#interface TableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *dateLabel;
#property (weak, nonatomic) id <TableViewCellDelegate> cellDelegate;
#end
tableviewcell m
- (IBAction)buttonAction:(UIButton *)sender
{
[self.cellDelegate requestToReloadTableViewFromCell:self];
}
tableviewcontroller m
#interface TableViewController () <TableViewCellDelegate>
#end
#implementation TableViewController
- (void)requestToReloadTableViewFromCell:(UITableViewCell *)cell
{
NSLog(#"%d", [[self.tableView indexPathForCell:cell] row]);
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Configure the cell...
cell.cellDelegate = self;
cell.dateLabel.text = [[NSDate date] description];
return cell;
}
you can use the tag with these buttons programatically in cellForRowAtIndexPath method. Tag should be different for each button (you can use some formula, for example tag will be equal to row no). in this way when button will be pressed you will check tag first and can know which button is pressed at which row.
I've tried many times to create a table view and delete certain rows, I even asked this question here before and implemented what they advised but yet failed to even get my table
ViewController.h
#interface XYZViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) UITableView *myTable;
#property (strong, nonatomic) NSMutableArray *names;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *editButton;
- (IBAction)editMyTable:(id)sender;
#end
ViewController.m
#implementation XYZViewController
#synthesize names, myTable, editButton;
- (void)viewDidLoad
{
[super viewDidLoad];
//input values into the mutable array
names = [[NSMutableArray alloc]initWithObjects:
#"Bob",
#"Chris",
#"Tom"
, nil];
editButton = self.editButtonItem;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"Friends";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.names count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create an identifier
static NSString *identifier;
//create the cell with the identifier
UITableViewCell *cell = [myTable dequeueReusableCellWithIdentifier:identifier];
//check if cell is nil
if (cell == nil) {
//assign cell
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
//assign the names of the array to each cell
cell.textLabel.text = [names objectAtIndex:indexPath.row];
return cell;
}
- (IBAction)editMyTable:(id)sender
{
[editButton setTitle:#"Done"];
[myTable setEditing:YES animated:YES];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[myTable setEditing:editing animated:animated];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
//remove from array
[names removeObjectAtIndex:indexPath.row];
//remove from tableView
[myTable deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Image: http://postimg.org/image/g9wqwuo6v/
Any help is much appreciated! Thank you in advance! :)
It appears that your issue is that you have not wired up your myTable property. It is not an IBOutlet and nowhere in your code do you establish the connection. When you call [myTable setEditing:YES animated:YES]; it is sending it to a nil table. You can verify this by printing out myTable's value before calling the edit method: NSLog(#"%#", myTable);.
Also you should remove your overriding of setEditing:animated: since you are a UIViewController subclass and not a UITableView subclass. Just making your initial call in your IBAction should be enough.
I have an UITableViewController
#interface GenderSelectController : UITableViewController
- (id)initWithPreviousGender:(NSString*)prevGender;
#end
#implementation GenderSelectController
- (id)initWithPreviousGender:(NSString *)_prevGender
{
// It's not loaded from nib but created in code
if ((self = [super initWithStyle:UITableViewStyleGrouped])) {
prevGender = _prevGender;
}
return self;
}
And I have a category method
#interface UITableViewController (RowHeight)
#end
#implementation UITableViewController (RowHeight)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return (indexPath.row) ? tableView.rowHeight : tableView.rowHeight - 1;
}
#end
And it's NOT called until I do it explicitely
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
But another category, for UIViewController which implements UITableViewDelegate method, it working just fine. What's the matter?