I want to make a Table View Controller that hold a table view and an image view in table view header view. When I scroll down the table view I want to zoom in the image (like in CNN app for iOS). I tried a lot of things but none of them to this as I want. Maybe someone can help me. Thanks!
All screen will look like this image like in this image:
(source: mzstatic.com)
I tried to do this by making the image at the top the header view of the table, but couldn't get that to work.
The way that did work, was to add a scroll view to the top of a UIViewController's view with a image view inside it. I then added a table view, and made it the same size as the controller's view so that it is over top the small scroll view that holds the image (this was done in a storyboard with auto layout turned off). In code, I add a table header view that's transparent and has a label that acts as the caption for the picture. It has the same height as the small scroll view. When you scroll the content of the table view up, I also scroll the small scroll view but at half the rate. If you pull the content down, I change the frame of the small scroll view so that it expands and stays centered. Here's the code:
#interface ViewController ()
#property (strong,nonatomic) NSArray *theData;
#property (weak,nonatomic) IBOutlet UITableView *tableView;
#property (weak,nonatomic) IBOutlet UIScrollView *imageScrollView;
#property (nonatomic) CGRect svFrame;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.imageScrollView.delegate = self;
self.imageScrollView.contentSize = CGSizeMake(500, 500);
self.svFrame = self.imageScrollView.frame;
self.theData = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine"];
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 135)];
headerView.backgroundColor = [UIColor clearColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, headerView.frame.size.width, 24)];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.text = #"This is a Picture Caption";
[headerView addSubview:label];
self.tableView.tableHeaderView = headerView;
[self.tableView reloadData];
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
UIView *bgView = [[UIView alloc] init];
bgView.backgroundColor = [UIColor whiteColor];
cell.backgroundView = bgView;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.theData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.theData[indexPath.row];
return cell;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (! [scrollView isEqual:self.imageScrollView]) {
if (scrollView.contentOffset.y > 0) {
self.imageScrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y/2);
}else{
self.imageScrollView.frame = CGRectMake(scrollView.contentOffset.y, scrollView.contentOffset.y, self.svFrame.size.width - (scrollView.contentOffset.y * 2), self.svFrame.size.height - (scrollView.contentOffset.y * 2));
}
}
}
Related
I have problem with positioning the view at the very top of table view. I have UITableViewController inside the UINavigatonController. I add subview to it's view. I want the subview to be always at the top of the screen, at the same place where navigation bar is. If I setup top constraint which pins the top of the subview to the top of tableView, than it appears under the navigation bar, not at the very top of the screen. How to solve this problem? Here is the code and the picture:
#interface ViewController : UITableViewController
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView* redSquare = [[UIView alloc] initWithFrame:CGRectZero];
redSquare.backgroundColor = [UIColor redColor];
[self.view addSubview:redSquare];
[redSquare setTranslatesAutoresizingMaskIntoConstraints:NO];
[redSquare.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[redSquare.widthAnchor constraintEqualToConstant:100].active = YES;
[redSquare.heightAnchor constraintEqualToConstant:100].active = YES;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* reuseIdentifier = #"ri";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
}
return cell;
}
#end
I have table in main view. Next I added view to above table view with height 100 points, then I have first row with 100 points height. Next I check scrollView contentOffset to recalculate position of my added view. When I scroll my table view all fine, my view is duplicate position with my first cell in table view.
But I have problem is:
When I want to scroll from my view (finger in a view) and scroll down, table view is no interaction with my gestures. Yes, that normal. If I set user interaction to NO, all work fine. But I have some buttons in this view and when I disabled user interaction I can't have actions with my subviews in this view.
How I can scroll and tap simultaneously/together without user interaction or something else method. Thx.
All of code:
#import "ViewController.h"
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
{
UIView *someView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
someView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)];
someView.backgroundColor = [UIColor redColor];
[self.view addSubview:someView];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath{
if (indexPath.row == 0) return 100;
else return 44;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (indexPath.row == 0) cell.backgroundColor = [UIColor greenColor];
}
return cell;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
someView.center = CGPointMake(someView.center.x, -scrollView.contentOffset.y+50);
}
Try to add button on main view
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
someView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 100)];
someView.backgroundColor = [UIColor redColor];
[self.view addSubview:someView];
UIButton *someBotton = [UIButton buttonWithType:UIButtonTypeCustom];
// button initialisation
...
[self.view addSubview:someBotton];
}
In this case table will not interact only below button
Second way is create subclass for someView and override method pointInside:
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event{
for(UIView *subview in self.subviews){
if([subview pointInside:point withEvent:event]){
return YES;
}
}
return NO;
}
I am trying to create a Card Based News feed (Similar to Facebook app) in iOS.I'm implementing programmatically because the height of UITablecell has to increase/decrease based on the data.
Consider i have one UIlabel and one UIImageView added, then the height of UITablecell should be adjusted based on the fields. If one more text feild is added then height has to increase and if imageview is removed then height has to decrease.
The issue here is I couldn't to add CustomView on the UItablecell programmatically but when i create using Interface builder then its working fine but the height remains constant which i don't want to have.
Can some one please help me out.
TableViewCell.h
#interface TableViewCell : UITableViewCell
#property (strong,nonatomic) UIView *customView;
#property (strong,nonatomic) UILabel *customLabel;
#property (strong,nonatomic) UIImageView *customImage;
#end
TableViewCell.m
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// configure control(s)
[self addSubview:self.customView];
[self.customView addSubview:self.customLabel];
[self.customView addSubview:self.customImage];
}
return self;
}
- (UIView *)customView
{
if (!_customView)
{
_customView = [[UIView alloc] initWithFrame:CGRectMake(0, 10, self.contentView.frame.size.width, 50)];
[_customView setTranslatesAutoresizingMaskIntoConstraints:NO];
}
return _customView;
}
- (UIImageView *)customImage
{
if (!_customImage)
{
_customImage = [UIImageView new];
_customImage.clipsToBounds = YES;
[_customImage.layer setBorderColor: [[UIColor grayColor] CGColor]];
[_customImage.layer setBorderWidth: 1.0];
_customImage.contentMode = UIViewContentModeCenter;
[_customImage setTranslatesAutoresizingMaskIntoConstraints:NO];
}
return _customImage;
}
- (UILabel *)customLabel
{
if (!_customLabel)
{
_customLabel = [UILabel new];
[_customLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
}
return _customLabel;
}
UITableVIewController.m
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_stubCell)
{
_stubCell = [tableView dequeueReusableCellWithIdentifier:#"TableCell"];
}
});
CGFloat height = [_stubCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height + 1;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.f;
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"TableCell";
// Similar to UITableViewCell, but
_stubCell = (TableViewCell *)[theTableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (_stubCell == nil) {
_stubCell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
// Just want to test, so I hardcode the data
_stubCell.customLabel.text = #"Testing";
return cell;
}
I think it may be better to create multiple nibs for the same class UITableViewCell, one for each of the possible layouts that you need. Depending on which nib is used then set heightForRowAtIndexPath to the height of the nib.
This will give you predictability of the layout for each situation and you connect only the fields defined in UITableViewCell in cellForRowAtIndexPath.
never mind i found answer by myself. I am calculating the height of each UIView that is added the super view. Based on the total height the UITableview height increases and decrease and also using auto layout adjusting the layout constraints made me simple.
I am still working on the design once its done i'll post the code so that it will be help ful for other developers.
I have a uitableview with a tablefooterview. I would like use cell separators within the tableview but would like to get rid of the full screen width separator placed by default between the tableview and the tablefooterview. I have tried
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
and
cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0);
but neither works
Have a look at this sample implementation.
You can see here that UITableView with plain style does in fact loose the last separator if a footerView is set.
(Orange view is the footerView and there is no separator above him)
Of course in Grouped tableView this does not work as discussed in comments under your question.
https://gist.github.com/bartekchlebek/2b05b5ddcc3efec3f514
#import "ViewController.h"
#interface ViewController () <UITableViewDataSource>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds
style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
footerView.backgroundColor = [UIColor orangeColor];
tableView.tableFooterView = footerView;
[self.view addSubview:tableView];
}
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"ID"];
cell.textLabel.text = [NSString stringWithFormat:#"Row: %#", #(indexPath.row)];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
#end
Edit:
I have found that a much simpler way to avoid the gap between the last section and the table footer is to implement heightForFooterInSection and return a very small value that is not 0. This will result in nothing being visible.
For some reason, returning 0 doesn't suppress the rendering of section footers in a grouped-style table view, and it will try to render a 1-point footer. There is other discussion on StackOverflow about this.
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return CGFLOAT_MIN;
}
I was able to accomplish this in what feels like a slightly hacky way, but it seems to work fine.
My hack is simply to place the actual footer view you want inside of a wrapper view, with a frame position of (0, -1).
Let's say you have a table footer view that you want to use, a class called MyTableFooterView.
In that case you can do something like this:
//This is the view we will actually add as the table footer
//We make it one point shorter since the content will be shifted up
//one point out of its frame
CGRect footerFrame = CGRectMake(0, 0, self.tableView.width, myFooterHeight-1);
UIView* tableFooterWrapper = [[UIView alloc] initWithFrame:footerFrame];
//This is the view that we are trying to use as a table footer.
//We place it 1 point up inside the wrapper so it overlaps that pesky 1-point separator.
CGRect contentFrame = CGRectMake(0, -1, self.tableView.width, myFooterHeight);
UIView* footerContent = [[MyTableFooterView alloc] initWithFrame:contentFrame];
//Make sure the footer expands width for to other screen sizes
footerContent.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[tableFooterWrapper addSubview:footerContent];
self.tableView.tableFooterView = tableFooterWrapper;
This works reliably for me. It's possible that there's a more idiomatic way, but I haven't come across one yet.
Use following code
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
I'm wondering if I could put a page view within a table view cell. Basically I'm trying to get each table view cell to be able to scroll left/right for more content.
Can I do it by putting a page view within a table view cell? Or is there another way to be able to swipe left/right in a table view cell?
It's possible to load another view controller and add its view as a subview of another controller's view.
Although it's not recommended by Apple's guidelines:
Each custom view controller object you create is responsible for
managing all of the views in a single view hierarchy. [...] The
one-to-one correspondence between a view controller and the views in
its view hierarchy is the key design consideration. You should not use
multiple custom view controllers to manage different portions of the
same view hierarchy.
More info on the guidelines click here
A better way to do it is using a UIScrollView and a UIPageControl like this…
cellForRowAtIndexPath
- (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];
}
NSArray *photo = [NSArray arrayWithObjects:[UIImage imageNamed:#"image1.png"], [UIImage imageNamed:#"image2.png"], [UIImage imageNamed:#"image3.png"], nil];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 150)];
self.scrollView.backgroundColor = [UIColor clearColor];
self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleBlack; //Scroll bar style
self.scrollView.showsHorizontalScrollIndicator = NO;
[self.scrollView setDelegate:self];
//Show horizontal scroll bar
self.scrollView.showsVerticalScrollIndicator = YES; //Close vertical scroll bar
self.scrollView.bounces = YES; //Cancel rebound effect
self.scrollView.pagingEnabled = YES; //Flat screen
self.scrollView.contentSize = CGSizeMake(640, 30);
NSLog(#"LOG");
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 155, 320, 40)];
self.pageControl.numberOfPages = photo.count;
self.pageControl.currentPage = 0;
self.pageControl.backgroundColor = [UIColor redColor];
[self.pageControl setTintColor:[UIColor whiteColor]];
[cell.contentView addSubview:self.pageControl];
for(int i = 0; i < photo.count; i++)
{
CGRect frame;
frame.origin.x = (self.scrollView.frame.size.width *i) + 10;
frame.origin.y = 0;
frame.size = CGSizeMake(self.scrollView.frame.size.width - 20, self.scrollView.frame.size.height);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
[imageView setImage:[photo objectAtIndex:i]];
[self.scrollView addSubview:imageView];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width*photo.count, self.scrollView.frame.size.height);
}
[cell.contentView addSubview:self.scrollView];
return cell;
}
update PageControl
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat pageWidth = scrollView.frame.size.width;
//int page = floor((scrollView.contentOffset.x - pageWidth*0.3) / pageWidth) + 1);
self.pageControl.currentPage = (int)scrollView.contentOffset.x / (int)pageWidth;
NSLog(#"CURRENT PAGE %d", self.pageControl.currentPage);
}
Yes, you can.
However, be aware that you can't get rid of the black background to the page control in it if you choose to go that route. I did this a couple days ago but ended up getting rid of the page view controller in favor of using a UIScrollView that has paging enabled and a UIPageControl. I use the -scrollViewWillEndDragging:withVelocity:targetContentOffset: scroll view delegate method to determine when to determine the currentPage in the page control.
If you're going to have quite a few views in the scroll view, it would be more memory efficient to use a collection view instead of a normal scroll view.
Instead of using a UIPageview, try to put another UITableview (rotated by 90 degrees in anticlockwise direction)inside a cell. This will give you more control over touch events and more customization too.
1) Have a class for UITableViewCell and an xib file too, say PageViewTableCell
2) Xib file should have a UITableView inside the cell
3) Rotate the table when awakeFromNib is called
- (void)awakeFromNib
{
horizontalTable.transform = CGAffineTransformMakeRotation(-M_PI_2);
horizontalTable.frame = CGRectMake(0,0,horizontalTable.frame.size.width,horizontalTable.frame.size.height);
}
4) Have the delegate and datasource for horizontalTable according to your design.
5) Load the main table which has the above customized cell
- (void)viewDidLoad
{
[self.mainTable registerNib:[UINib nibWithNibName:#"PageViewTableCell"
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:#"samplecell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PageViewTableCell* cell = [tableView dequeueReusableCellWithIdentifier:#"samplecell"];
return cell;
}