UITableview/UIView Delegate Confusion - ios

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;

Related

How to locate the layout across view hierarchy?

I have a menuView in a list view controller. The menuView added on the UITableViewCell when a more button in the cell being taped.
I achieved the effect with singleton.
#implementation ProductsOperationMenu
static ProductsOperationMenu *_instance;
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] initWithFrame:CGRectZero];
});
return _instance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
ZBMyProductsCell.m
#implementation ZBMyProductsCell
- (void)awakeFromNib
{
[super awakeFromNib];
_operationMenu = [[ProductsOperationMenu alloc] initWithFrame: CGRectZero];
}
- (IBAction)operationButtonClick:(UIButton *)sender {
if ([self.contentView.subviews containsObject:_operationMenu]) {
_operationMenu.hidden = ![_operationMenu isHidden];
} else{
[self.contentView addSubview:_operationMenu];
_operationMenu.hidden = NO;
}
[_operationMenu mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(205);
make.height.mas_equalTo(60);
make.bottom.mas_equalTo(self.operationButton).offset(0);
make.right.mas_equalTo(self.operationButton.mas_left).offset(-10);
}];
}
Without Singleton, it became this:
So the question come.
I want to put the menuView on the controller's view, because it is unique or hidden, which used to belong to the cell.
How to convert layout of the more button selected to the controller's view?
How to use the methods to calculate?
- convertPoint:toView:
- convertPoint:fromView:
......
I did it in a simple way. Here is the code:
- (void)clickOperationButtonOfProductsCell:(ZBMyProductsCell *)myProductsCell{
NSUInteger * operationIndex = [self.myProductsTableView.visibleCells indexOfObject:myProductsCell];
CGFloat originY = operationIndex.row * 110 + 50 + 40;
CGRect originFrame = CGRectMake(KScreenWidth - 55, originY, 0, 60);
CGRect finalFrame = CGRectMake(KScreenWidth - 260, originY, 205, 60);
self.operationMenuView.frame = originFrame;
[UIView animateWithDuration: 0.5 delay: 0 options: UIViewAnimationOptionCurveEaseIn animations:^{
self.operationMenuView.frame = finalFrame;
} completion:^(BOOL finished) { }];
}
How to achieve it more adaptively?
Maybe you can try it like this:
// here is your cell where the more button belongs to
#interface ZBMyProductsCell: UITableViewCell
#property (nonatomic, copy) void(^moreAction)(ZBMyProductsCell *cell);
#end
#implementation ZBMyProductsCell
- (void)_moreButtonClicked:(UIButton *)sender {
!self.moreAction ?: self.moreAction(self);
}
#end
// here is your view controller where your table view belongs to
// this is a `UITableViewDataSource` delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ZBMyProductsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ZBMyProductsCell" forIndexPath:indexPath];
// Configure the cell...
cell.moreAction = ^(ZBMyProductsCell *cell) {
CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
// write your own code to show/hide the menu
};
return cell;
}
Create a variable in each cell model called cellIndexpath and in cellForRow init it
cell.cellIndexpath = indexpath
have a look of UIPopoverPresnetationController and see if it can do the job. It should be available on iPhone. Putting menu view on the controller’viewwill cause issue when you scroll the table view.
use
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setTargetRect:self.detaiLabel.frame inView:self];

-didSelectRowAtIndexPath: not being called in UITableView

When I click the UITableViewCell , It have selection effect (grey background in clicked cell),But didSelectRowAtIndexPath is not calling ,what happen?
EDIT
this is my code
tableView.h file
#interface PopCardView : MMPopupView <UITableViewDataSource, UITableViewDelegate>
#end
tableView.m file
#property (nonatomic, strong) NSMutableArray *tagsArray;
#property (nonatomic, strong) UIView *backView;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, assign) NSUInteger lastIndex;
#end
-(id)initWithTags:(NSMutableArray *)tags{
self = [super init];
if (self) {
self.backView = [[UIView alloc] init];
self.backView.backgroundColor = [UIColor whiteColor];
self.backView.layer.cornerRadius = 5;
self.backView.layer.masksToBounds = YES;
[self addSubview:self.backView];
[self.backView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.bottom.right.equalTo(self);
}];
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 324, 300) style:UITableViewStylePlain];
_tableView.tableFooterView =[[UIView alloc] init];
[self.backView addSubview:_tableView];
[_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.right.bottom.equalTo(self.backView).insets(UIEdgeInsetsMake(45,0, 45, 15));
make.size.mas_equalTo(CGSizeMake(324, 200));
}];
[_tableView registerClass:[PopCardTagViewCell class] forCellReuseIdentifier:#"cell"];
_tableView.allowsSelection = YES;
_tableView.allowsSelectionDuringEditing = YES;
[_tableView setUserInteractionEnabled:YES];
_tableView.dataSource = self;
_tableView.delegate = self;
}
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[_tagsArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PopCardData *data = (PopCardData *)obj;
data.selected = #"0";
if (idx == indexPath.row) {
data.selected = #"1";
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifer = #"cell";
PopCardTagViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifer];
if (!cell) {
cell = [[PopCardTagViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
}
[self configureCell:cell forIndexPath:indexPath];
return cell;
}
-(void)configureCell:(PopCardTagViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
PopCardData *data = (PopCardData *)[_tagsArray objectAtIndex:indexPath.row];
//configure cell
[cell setUserInteractionEnabled:YES];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _tagsArray.count;
}
EDIT2
this is my initialize code of PopCardView,it use swift
let pop = PopCardView(tags: self.m_model.getItmes())
pop.show()
The code you show does not set any delegate to the table view. Either this is the reason or you posted an incomplete code snippet.
self.tableView.delegate = self;
and add UITableViewDelegate to your interface like
#interface ClassName ()<UITableViewDelegate>
Make sure you don't have anything in the cell that can swallow the touch event. Things like buttons and textfields can cause this. Strip everything from your cell, test to see if it works, then add things back in slowly to find the culprit.

UITableView skips one indexPath between every one of custom UITableViewCell, disappears when scrolling

The problem in pictures:
Basically, my table view adds an empty cell between each other, and I don't know why. I already tried putting an NSLog statement in cellForRowAtIndexPath:, willDisplayCell:forRowAtIndexPath:, and didSelectRowAtIndexPath:, and the data source in each cell's indexPath is correct. It's just that the displayed cells are skipping in between, and I don't understand why.
My view controller and table view cell subclasses are also very simple. Here's the code:
PrivateMessagesViewController.m
static NSString * const kCellIdentifier = #"kCellIdentifier";
#interface PrivateMessagesViewController () <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSMutableArray *messages;
#property (strong, nonatomic) SCPFInboxQuery *query;
#property (nonatomic) BOOL hasAlreadyFetchedBefore;
#property (strong, nonatomic) UITableView *tableView;
// This is a custom view that either shows a loading animation,
// a "No Results Found" label, or an error message with a Retry button
// at the center of the view of a view controller.
#property (strong, nonatomic) SCPCenterView *centerView;
#end
#implementation PrivateMessagesViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Private Messages";
self.messages = [NSMutableArray array];
self.query = [[SCPFInboxQuery alloc] init];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, [UIScreen mainScreen].bounds.size.height - 113) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.tableView registerClass:[SCPPrivateMessageListCell class] forCellReuseIdentifier:kCellIdentifier];
self.tableView.alpha = 0; // The table view starts out invisible.
[self.view addSubview:self.tableView];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.hasAlreadyFetchedBefore) {
[self.view addSubview:self.centerView];
[self.centerView showLoadingView];
__weak PrivateMessagesViewController *weakSelf = self;
[self.query runInBackgroundWithCompletion:^(NSArray *messages, NSError *error) {
PrivateMessagesViewController *innerSelf = weakSelf;
// If there is an error, handle it.
if (error) {
// ...
return;
}
// If there weren't any messages before and none were found,
// the user has no messages in the inbox.
if (!innerSelf.hasAlreadyFetchedBefore && messages.count == 0) {
[innerSelf.centerView showNoResultsLabel];
}
else {
if (!innerSelf.hasAlreadyFetchedBefore) {
[innerSelf.centerView fadeOutAndRemoveFromSuperview];
innerSelf.tableView.alpha = 1;
} else {
}
[innerSelf.messages addObjectsFromArray:messages];
[innerSelf.tableView reloadData];
}
innerSelf.hasAlreadyFetchedBefore = YES;
}];
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.messages.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SCPPrivateMessageListCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
SCPFMessage *message = self.messages[indexPath.row];
cell.message = message;
return cell;
}
#pragma mark - Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [SCPPrivateMessageListCell heightForMessage:self.messages[indexPath.row]];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
SCPPrivateMessageListCell *theCell = (SCPPrivateMessageListCell *)cell;
NSLog(#"Message at %d: %#", indexPath.row, theCell.message.rawData);
}
#pragma mark - Getters
- (SCPCenterView *)centerView
{
if (!_centerView) {
_centerView = [[SCPCenterView alloc] initWithParent:self.view];
_centerView.noResultsText = #"You don't have any private messages yet.";
}
return _centerView;
}
#end
SCPPrivateMessageListCell.m
static const CGFloat kInnerMargin = 10;
static const CGFloat kSpaceBetweenImageAndRightLabel = 10;
static const CGFloat kImageViewSize = 60;
static const CGFloat kRightLabelX = kInnerMargin + kImageViewSize + kSpaceBetweenImageAndRightLabel;
static const CGFloat kMaxRightLabelWidth = 320 - (kRightLabelX + kInnerMargin);
#interface SCPPrivateMessageListCell ()
#property (strong, nonatomic) UIImageView *thumbnailImageView;
#property (strong, nonatomic) UILabel *dateLabel;
#property (strong, nonatomic) UILabel *senderLabel;
#property (strong, nonatomic) UILabel *subjectLabel;
#property (strong, nonatomic) UILabel *summaryLabel;
#end
#implementation SCPPrivateMessageListCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self) {
_thumbnailImageView = [[UIImageView alloc] init];
_thumbnailImageView.contentMode = UIViewContentModeScaleAspectFill;
_thumbnailImageView.layer.cornerRadius = kImageViewSize / 2;
_thumbnailImageView.clipsToBounds = YES;
_dateLabel = [[UILabel alloc] init];
_dateLabel.font = [UIFont systemFontOfSize:10];
_dateLabel.textAlignment = NSTextAlignmentCenter;
_dateLabel.numberOfLines = 1;
_dateLabel.lineBreakMode = NSLineBreakByTruncatingTail;
_senderLabel = [SCPPrivateMessageListCell senderLabel];
_subjectLabel = [SCPPrivateMessageListCell subjectLabel];
_summaryLabel = [SCPPrivateMessageListCell summaryLabel];
[self.contentView addSubview:_thumbnailImageView];
[self.contentView addSubview:_dateLabel];
[self.contentView addSubview:_senderLabel];
[self.contentView addSubview:_subjectLabel];
[self.contentView addSubview:_summaryLabel];
}
return self;
}
- (void)setMessage:(SCPFMessage *)message
{
_message = message;
[self.thumbnailImageView setImageWithURL:message.thumbnailURL];
self.dateLabel.text = message.dateSent;
self.senderLabel.text = message.nameOfSender;
self.subjectLabel.text = message.subject;
self.summaryLabel.text = message.summary;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
self.thumbnailImageView.frame = CGRectMake(kInnerMargin, kInnerMargin, kImageViewSize, kImageViewSize);
[self.dateLabel sizeToFitWidth:kImageViewSize];
self.dateLabel.frame = CGRectMake(kInnerMargin, kInnerMargin + kImageViewSize, kImageViewSize, self.dateLabel.frame.size.height);
[self.senderLabel sizeToFitWidth:kMaxRightLabelWidth];
self.senderLabel.frame = CGRectMake(kRightLabelX, kInnerMargin, kMaxRightLabelWidth, self.senderLabel.frame.size.height);
CGFloat subjectLabelY = self.senderLabel.frame.origin.y + self.senderLabel.frame.size.height;
[self.subjectLabel sizeToFitWidth:kMaxRightLabelWidth];
self.subjectLabel.frame = CGRectMake(kRightLabelX, subjectLabelY, kMaxRightLabelWidth, self.subjectLabel.frame.size.height);
CGFloat summaryLabelY = self.subjectLabel.frame.origin.y + self.subjectLabel.frame.size.height;
[self.summaryLabel sizeToFitWidth:kMaxRightLabelWidth];
self.summaryLabel.frame = CGRectMake(kRightLabelX, summaryLabelY, kMaxRightLabelWidth, self.summaryLabel.frame.size.height);
CGFloat cellHeight = [SCPPrivateMessageListCell heightForMessage:self.message];
self.contentView.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, cellHeight);
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, cellHeight);
}
#pragma mark - Class methods
+ (UILabel *)senderLabel
{
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont boldSystemFontOfSize:17];
label.textColor = [UIColor colorFromHex:0x0076be];
label.numberOfLines = 1;
label.lineBreakMode = NSLineBreakByTruncatingTail;
return label;
}
+ (UILabel *)subjectLabel
{
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:14];
label.numberOfLines = 1;
label.lineBreakMode = NSLineBreakByTruncatingTail;
return label;
}
+ (UILabel *)summaryLabel
{
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:12];
label.textColor = [UIColor grayColor];
label.numberOfLines = 3;
label.lineBreakMode = NSLineBreakByTruncatingTail;
return label;
}
+ (CGFloat)heightForMessage:(SCPFMessage *)message
{
CGFloat height = kInnerMargin;
UILabel *senderLabel = [SCPPrivateMessageListCell senderLabel];
senderLabel.text = message.nameOfSender;
[senderLabel sizeToFitWidth:kMaxRightLabelWidth];
height += senderLabel.frame.size.height;
UILabel *subjectLabel = [SCPPrivateMessageListCell subjectLabel];
subjectLabel.text = message.subject;
[subjectLabel sizeToFitWidth:kMaxRightLabelWidth];
height += subjectLabel.frame.size.height;
UILabel *summaryLabel = [SCPPrivateMessageListCell summaryLabel];
summaryLabel.text = message.summary;
[summaryLabel sizeToFitWidth:kMaxRightLabelWidth];
height += summaryLabel.frame.size.height;
height += kInnerMargin;
return height;
}
#end
There. I'm not doing anything non-standard. The subviews of the cells also disappear when I scroll, but when I scroll them back in, the content becomes displayed, still with a skipping empty cell. Anyone have any idea why this is happening?
The problem was in my table view cell's layoutSubviews.
self.contentView.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, cellHeight);
That should have been:
self.contentView.frame = CGRectMake(self.contentView.frame.origin.x, self.contentView.frame.origin.y, self.contentView.frame.size.width, cellHeight);
I found out by setting a different color for self.backgroundColor and self.contentView.backgroundColor from inside the init method.

Implement a fixed SearchBar and TableView in a Custom UIViewController

The purpose is to implement a fixed search bar just like Contacts in iOS7.
I have a view controller called SearchViewController inherited from UIViewController.
And I add a searchBar and a tableView as its navigationController.view's subView.
But since searchBar and tableView are separated, when I start to search, no dim effect on tableView and result table view is shown in correctly.
I just want it behaves just like Contacts app.
Here is my code:
SearchViewController.h
#import <UIKit/UIKit.h>
#class UWTabBarController;
#class InfoSessionModel;
#interface SearchViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchDisplayDelegate>
SearchViewController.m
#import "SearchViewController.h"
#interface SearchViewController ()
#property (nonatomic, strong) UISearchBar *searchBar;
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) NSArray *data;
#end
#implementation SearchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// initiate search bar
NSInteger statusBarHeight = 20;
NSInteger navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, statusBarHeight + navigationBarHeight, 320, 44)];
// _searchBar.tintColor = [UIColor clearColor];
_searchBar.delegate = self;
_searchBar.barStyle = UIBarStyleDefault;
//NSMutableArray *scopeTitles = [[NSMutableArray alloc] initWithObjects:#"Employer", #"Program", #"Note", nil];
_searchBar.scopeButtonTitles = [[NSArray alloc] initWithObjects:#"Employer", #"Program", #"Note", nil];//[#"Employer|Program|Note" componentsSeparatedByString:#"|"];
// initiate search bar controller
_searchController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
_searchController.delegate = self;
_searchController.searchResultsDataSource = self;
_searchController.searchResultsDelegate = self;
// initiate table view
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, statusBarHeight + navigationBarHeight, 320, [UIScreen mainScreen].bounds.size.height - statusBarHeight - navigationBarHeight)];
[_tableView setContentInset:UIEdgeInsetsMake(_searchBar.frame.size.height, 0, _tabBarController.tabBar.frame.size.height, 0)];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView registerClass:[InfoSessionCell class] forCellReuseIdentifier:#"InfoSessionCell"];
[_tableView registerClass:[LoadingCell class] forCellReuseIdentifier:#"LoadingCell"];
[self.navigationController.view addSubview:_tableView];
[self.navigationController.view addSubview:_searchBar];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (_searchController.searchResultsTableView == tableView) {
return 1;
}
else {
return [data count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_searchController.searchResultsTableView == tableView) {
static NSString *cellIdentifier = #"LoadingCell";
LoadingCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[LoadingCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.loadingLabel.text = #"Test cell for search result";
return cell;
}
else {
//... configure cell and return cell
return cell;
}
}
}
#pragma mark - UISearchDisplayController Delegate Methods
// hasn't been implemented
#pragma mark - UISearchBar Delegate Methods
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
//move the search bar up to the correct location
[UIView animateWithDuration:.3
animations:^{
searchBar.frame = CGRectMake(searchBar.frame.origin.x,
20,// status bar's height
searchBar.frame.size.width,
searchBar.frame.size.height);
}
completion:^(BOOL finished){
//whatever else you may need to do
}];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
//move the search bar down to the correct location
[UIView animateWithDuration:.25
animations:^{
NSInteger statusBarHeight = 20;
NSInteger navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
searchBar.frame = CGRectMake(_searchBar.frame.origin.x,
statusBarHeight + navigationBarHeight,
_searchBar.frame.size.width,
_searchBar.frame.size.height);
}
completion:^(BOOL finished){
//whatever else you may need to do
}];
return YES;
}
This is the effects of my code:
All right, my problem is caused by my misunderstanding of views and navigationController's views.
In view did load method:
before is:
[self.navigationController.view addSubview:_tableView];
[self.navigationController.view addSubview:_searchBar];
but should be:
[self.view addSubview:_tableView];
[self.view addSubview:_searchBar];
In this way, the original table view and result table view will show correctly.
And other things are moving search bar up and down, these things should be done through UISearchBar delegate protocol methods and UISearchDisplayController delegate protocol methods.
This is a right way to implement a fixed searchBar and tableView bellow it.

Why my UIScrollView can not scoll updated, add source code. help please

My source code download
------Updated--------
I am trying to implement a sidebar effect using CGAffineTransformMakeTranslate simulate slide-in and slide-out. I want to make my sidebar as a scrollview so it could be add more data but it can not scroll at all.
Here is my code:
SidebarView is a UITableView
#import "SidebarView.h"
#interface SidebarView ()
#property (nonatomic, readwrite) CGFloat offsetX;
#end
#implementation SidebarView
#pragma mark - Initilization
- (void)setup {
// do initilization here
self.offsetX = self.frame.size.width;
[self registerClass:[UITableViewCell class] forCellReuseIdentifier:#"sidebarCell"];
}
- (void)awakeFromNib {
[self setup];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
[self setup];
return self;
}
#pragma mark - Custom function
- (void)show {
self.transform = CGAffineTransformMakeTranslation(-self.offsetX, 0);
}
- (void)hide {
self.transform = CGAffineTransformMakeTranslation(-self.offsetX, 0);
}
And my view controller:
#import "ViewController.h"
#import "SidebarView.h"
#interface ViewController () <UITableViewDataSource>
#property (nonatomic) BOOL isMenuHide;
#property (nonatomic, strong) SidebarView *sidebarView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.isMenuHide = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if (self.sidebarView) {
self.sidebarView = nil;
}
if (self.view) {
self.view = nil;
}
}
- (SidebarView *)sidebarView {
if (!_sidebarView) {
CGRect frame = [[UIScreen mainScreen] bounds];
frame.size.width /= 2;
_sidebarView = [[SidebarView alloc] initWithFrame:frame];
_sidebarView.transform = CGAffineTransformMakeTranslation(-_sidebarView.offsetX, 0);
_sidebarView.contentSize = CGSizeMake(320, 960);
_sidebarView.scrollEnabled = YES;
_sidebarView.showsVerticalScrollIndicator = YES;
_sidebarView.dataSource = self;
}
return _sidebarView;
}
#define ANIMATE_DURATION 0.5
- (IBAction)showMenu:(UIBarButtonItem *)sender {
if (self.isMenuHide) {
[self.view addSubview:self.sidebarView];
[UIView animateWithDuration:ANIMATE_DURATION animations:^{
[self.sidebarView show];
self.view.transform = CGAffineTransformMakeTranslation(self.sidebarView.offsetX, 0);
}];
} else {
[UIView animateWithDuration:ANIMATE_DURATION animations:^{
[self.sidebarView hide];
self.view.transform = CGAffineTransformMakeTranslation(0, 0);
} completion:^(BOOL finished) {
[self.sidebarView removeFromSuperview];
}];
}
self.isMenuHide = !self.isMenuHide;
}
#pragma mark - UITableView Datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 11;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"sidebarCell"];
cell.textLabel.text = #"Bingo";
return cell;
}
#end
Please tell my "why my scrollview cannot scroll" thanks.
contentSize needs to be set to the size of the content being contained, not the size of the frame in which it sits.
I don't know if your code has other problems, but that's the most terminal one.

Resources