I have a UITableView for an Instagram feed. I have implemented UIRefreshControl for pull to refresh functionality.
After drag and release the refresh control, and while the spinner is going, I'm able to drag the tableview down without the app crashing. However, if I scroll the tableview upwards, the app crashes (maybe because of cell 2, 3 etc?)
Here's a video showing the problem: http://www.screenmailer.com/v/DukT4lt2aUGm8c5MLRlGMg/2586/3a23tXo7uXs.mp4
Why is this happening?
Code for the .m file:
#import "InstagramViewController.h"
#import "InstagramCell.h"
#import <InstagramKit/InstagramKit.h>
#import "UIImageView+AFNetworking.h"
#interface InstagramViewController ()
{
NSMutableArray *mediaArray;
}
#property (nonatomic, strong) InstagramPaginationInfo *currentPaginationInfo;
#end
#implementation InstagramViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
//mediaArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)viewDidLoad
{
mediaArray = [[NSMutableArray alloc] init];
[super viewDidLoad];
[self loadMedia];
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(reloadMedia) forControlEvents:UIControlEventValueChanged];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
-(void)reloadMedia
{
self.currentPaginationInfo = nil;
[mediaArray removeAllObjects];
[self loadMedia];
}
-(IBAction)loadMedia
{
// start network indicator
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[[InstagramEngine sharedEngine] getMediaWithTagName:#"AUFsommer" count:10 maxId:self.currentPaginationInfo.nextMaxId withSuccess:^(NSArray *media, InstagramPaginationInfo *paginationInfo) {
if (paginationInfo)
{
self.currentPaginationInfo = paginationInfo;
}
[mediaArray addObjectsFromArray:media];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self reloadData];
} failure:^(NSError *error) {
NSLog(#"Search Media Failed");
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}];
}
-(void)reloadData
{
[self.refreshControl endRefreshing];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] init];
headerView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.9f];
InstagramMedia *media = mediaArray[section];
// create imageview for profile photo
AsyncImageView *profilePhoto = [[AsyncImageView alloc] initWithFrame:CGRectMake(8, 8, 32, 32)];
profilePhoto.layer.borderColor = [[UIColor colorWithRed:204.0/255.0f green:204.0/255.0f blue:204.0/255.0f alpha:1.0f] CGColor];
profilePhoto.layer.borderWidth = 1;
profilePhoto.layer.masksToBounds = YES;
profilePhoto.layer.cornerRadius = 16.0;
[profilePhoto loadImageFromURL:[media.user.profilePictureURL absoluteString]];
// uifont settings
UIFont *labelFont = [UIFont boldSystemFontOfSize:13.0];
// create label for username
UILabel *usernameLabel = [[UILabel alloc] initWithFrame:CGRectMake(48, 0, 210, 48)];
usernameLabel.text = media.user.username;
usernameLabel.font = labelFont;
usernameLabel.textColor = [UIColor colorWithRed:235.0/255.0 green:24.0/255.0 blue:22.0/255.0 alpha:1.0f];
// create label for timestamp
UILabel *timestampLabel = [[UILabel alloc] initWithFrame:CGRectMake(250, 0, 54, 48)];
timestampLabel.textAlignment = NSTextAlignmentRight;
timestampLabel.font = labelFont;
// timestampLabel.text = [self stringForDisplayFromDate:media.createdDate];
// add to view
[headerView addSubview:profilePhoto];
[headerView addSubview:usernameLabel];
[headerView addSubview:timestampLabel];
return headerView;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return mediaArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
InstagramCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[InstagramCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// clear photo
[cell.igPhoto setImage:nil];
if (mediaArray.count >= indexPath.section+1)
{
InstagramMedia *media = mediaArray[indexPath.section];
cell.title.text = media.caption.text;
[cell.igPhoto loadImageFromURL:[media.standardResolutionImageURL absoluteString]];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
InstagramMedia *media = mediaArray[indexPath.section];
CGSize maximumLabelSize = CGSizeMake(304.0f, 20000.0f);
CGSize expectedLabelSize = [media.caption.text sizeWithFont:[UIFont systemFontOfSize:13.0f] constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping];
return (320.0f + expectedLabelSize.height + 20.0f);
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
CGFloat currentOffset = scrollView.contentOffset.y;
CGFloat maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;
if (maximumOffset - currentOffset < 10.0)
{
[self loadMedia];
}
}
#end
The first thing you should do is to add an exception breakpoint in your project, so you will know why and where it crashes.
The reason why your app is crashing is that you try to read in an empty array. In the method reloadMedia, you do this :
self.currentPaginationInfo = nil;
[mediaArray removeAllObjects];
So at this point, your array is empty, but your UITableView is not aware of that. By scrolling before your data is reloaded, the methods cellForRowAtIndexPath will get called and try to access an index in your empty array.
To fix this, you can call [self.tableView reloadData]; after [mediaArray removeAllObjects];. This way, the UITableView will makes its call to know how many rows and sections it should have and will be aware there is no more rows and sections.
Or, if you want the old information to still be in your UITableView during the loading, just don't nil the currentPaginationInfo or empty mediaArray in reloadMedia,
Related
I want to resize the tableviewcell when SDWebImage download finished.
Where should I put [UITableViewCell setNeedsUpdateConstraints]?
Or just send notification to UIViewController and reload tableview data?
ViewController.m
#import "ViewController.h"
#import <Masonry/Masonry.h>
#import <SDWebImage/UIImageView+WebCache.h>
#define WEAKSELF typeof(self) __weak weakSelf = self;
#interface ViewController ()
<UITableViewDataSource, UITableViewDelegate> {
UITableView *myTableView;
}
#property (nonatomic, retain) NSArray *items;
#end
#implementation ViewController
- (UIRectEdge)edgesForExtendedLayout {
return UIRectEdgeNone;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadRows:)
name:#"ReloadRows"
object:nil];
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < 30; i ++) {
CGFloat width = arc4random() % 100 + 20.0f;
CGFloat height = arc4random() % 100 + 20.0f;
CGSize size = CGSizeMake(width, height);
[array addObject:[NSValue valueWithCGSize:size]];
}
self.items = array;
myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
myTableView.dataSource = self;
myTableView.delegate = self;
myTableView.estimatedRowHeight = 60.0f;
myTableView.rowHeight = UITableViewAutomaticDimension;
[self.view addSubview:myTableView];
UIView *superview = self.view;
[myTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.equalTo(superview);
}];
}
- (void)reloadRows:(NSNotification *)notification {
NSIndexPath *ip = notification.object;
if (ip) {
TestCell *cell = [myTableView cellForRowAtIndexPath:ip];
[cell setNeedsUpdateConstraints];
[myTableView reloadRowsAtIndexPaths:#[ip] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
#pragma mark - UITableViewDataSource, UITableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"Contact_People_Cell";
TestCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[TestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
NSValue *value = [self.items objectAtIndex:indexPath.row];
cell.imageSize = [value CGSizeValue];
[cell refreshInterface];
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
}
#end
TestCell
#define SIZE_DEFAULT CGSizeMake(140.0f, 100.0f)
#interface TestCell () {
UIImageView *imv;
}
#end
#implementation TestCell
#synthesize imageSize;
#synthesize indexPath;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupView];
}
return self;
}
- (void)setupView {
imv = [[UIImageView alloc] initWithFrame:CGRectZero];
imv.backgroundColor = [UIColor darkGrayColor];
[self.contentView addSubview:imv];
self.translatesAutoresizingMaskIntoConstraints = NO;
UIView *superView = self.contentView;
[imv mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(#(SIZE_DEFAULT.width));
make.height.equalTo(#(SIZE_DEFAULT.height));
make.left.top.bottom.equalTo(superView).with.offset(10.0f);
}];
}
- (void)updateConstraints {
[super updateConstraints];
UIView *superView = self.contentView;
[imv mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(superView).with.offset(10.0f);
make.bottom.equalTo(superView).with.offset(-10.0f);
make.width.equalTo(#(ceilf(self.imageSize.width)));
make.height.equalTo(#(ceilf(self.imageSize.height)));
}];
}
- (void)refreshInterface {
WEAKSELF
[imv sd_setImageWithURL:[NSURL URLWithString:#"http://www.baidu.com"]
placeholderImage:nil
options:SDWebImageProgressiveDownload
progress:^(NSInteger receivedSize, NSInteger expectedSize) {}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
imv.backgroundColor = [UIColor purpleColor];
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat width = arc4random() % 100 + 140.0f;
CGFloat height = arc4random() % 100 + 140.0f;
CGSize size = CGSizeMake(width, height);
weakSelf.imageSize = size;
[weakSelf setNeedsUpdateConstraints];
// NSIndexPath *ip = [weakSelf.indexPath copy];
// [[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadRows" object:ip];
});
}];
}
#end
Use self-sized cells. After load image, change height constraint of UIImage and call reloadRowsAtIndexPaths: method
implement tableView heightForRowAtIndexPath: method to change the cell height as per downloaded image height
I have a tableView with a list of ViewControllers. At the bottom of this tableview is an ad via iAd. When you visit any of these VCs, then hit "back" on the navigationController, the iAd is just as you left it--except for one of the viewcontrollers. After visiting this one, the ad is as you left it for a split second, then goes white for about ten seconds, then finally--sometimes--reloads another ad. I'm not sure what about pushing this certain VC causes the ad to fail when you go back to it. I'm thinking maybe something UI-related on the background thread, or something thread-related, but I'm not sure at all. Here is the VC's code that, after hitting "back" on the navControl, causes the tableView's ad to go white (this VC, another tableView, has it's own iAd and iAd delegate methods):
#import "RosterTableTableViewController.h"
#import "RosterListing.h"
#import "RosterListingCellTableViewCell.h"
#import "PlayerDetailViewController.h"
#import <iAd/iAd.h>
#import "RosterListingModel.h"
#interface RosterTableTableViewController () <ADBannerViewDelegate>
{
RosterListingModel *_homeModel;
BOOL _bannerIsVisible;
ADBannerView *_adBanner;
}
#property (nonatomic, strong) NSMutableArray *rosters;
#property NSCache *imageCache;
#property UIActivityIndicatorView *spinner;
#end
#implementation RosterTableTableViewController
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_rosters = [items copy];
[_spinner stopAnimating];
// Reload the table view
[self.tableView reloadData];
}
- (instancetype)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[RosterListingModel alloc] init];
self.navigationItem.title = #"Roster";
self.imageCache = [[NSCache alloc] init];
self.rosters = [[NSMutableArray alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
}
return self;
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"Failed to retrieve ad");
if (_bannerIsVisible)
{
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
// Assumes the banner view is placed at the bottom of the screen.
banner.frame = CGRectOffset(banner.frame, 0, banner.frame.size.height);
[UIView commitAnimations];
_bannerIsVisible = NO;
}
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear: animated];
[UIView setAnimationsEnabled:YES];
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"RosterDetail" bundle:nil];
PlayerDetailViewController *vc = [sb instantiateViewControllerWithIdentifier:#"PlayerDetailViewController"];
// Give the PlayerViewController its key
RosterListing *rl = [self.rosters objectAtIndex:indexPath.row];
vc.playerNumberKey = rl.playerNumber;
vc.playerNameKey = rl.playerName;
vc.playerImage = rl.image;
// Present vc
vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
//[self.navigationController pushViewController:vc animated:YES];
[UIView beginAnimations:#"animation" context:nil];
[self.navigationController pushViewController: vc animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView setAnimationDuration:0.75];
[UIView commitAnimations];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
spinner.center = CGPointMake(screenWidth/2.0, screenHeight/5.0);
spinner.hidesWhenStopped = YES;
spinner.color = [UIColor blackColor];
[self.view addSubview:spinner];
[spinner startAnimating];
_spinner = spinner;
// Load the Cell NIB file
UINib *nib = [UINib nibWithNibName:#"RosterListingCellTableViewCell" bundle:nil];
// Register this NIB, which contains the cell
[self.tableView registerNib:nib forCellReuseIdentifier:#"RosterCell"];
// Background
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ticketBackground"]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
self.tableView.backgroundView = imageView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 74;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.rosters.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get a new or recycled cell
RosterListingCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"RosterCell" forIndexPath:indexPath];
RosterListing *thisRosterListing = [self.rosters objectAtIndex:indexPath.row];
cell.playerNumberLabel.text = [NSString stringWithFormat:#"#%#",thisRosterListing.playerNumber];
cell.playerNameLabel.text = thisRosterListing.playerName;
cell.imageView.image = [UIImage imageNamed:#"omaha"];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.clipsToBounds = YES;
UIImage *playerImage = [self.imageCache objectForKey:thisRosterListing.playerImageURL];
cell.imageView.image = playerImage;
if (playerImage == nil) {
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
thisRosterListing.playerImageURL = [thisRosterListing.playerImageURL stringByReplacingOccurrencesOfString:#"small" withString:#"medium"];
NSURLSessionDataTask *imageData = [session dataTaskWithURL:[NSURL URLWithString: thisRosterListing.playerImageURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle NSData
UIImage *image = [UIImage imageWithData:data];
thisRosterListing.image = image;
[self.imageCache setObject:image forKey:thisRosterListing.playerImageURL];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
[self.tableView reloadData];
});
}];
[imageData resume];
}
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
imageView.image = [UIImage imageNamed:#"indicator"];
cell.accessoryView = imageView;
cell.backgroundColor = [UIColor clearColor];
// set selection color
UIView *myBackView = [[UIView alloc] initWithFrame:cell.frame];
myBackView.backgroundColor = [UIColor clearColor];
cell.selectedBackgroundView = myBackView;
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 65)];
view.backgroundColor = [UIColor clearColor];
if (_adBanner == nil)
{
_adBanner = [[ADBannerView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
}
_adBanner.delegate = self;
_adBanner.backgroundColor = [UIColor clearColor];
[view addSubview:_adBanner];
return view;
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 65;
}
#end
When I load my tableView, the images change sizes as I filter through the uisegmentedcontrol.
Here is the code for my tableView (I have the fixed constraints for the UIImageView's width and height, but somehow they aren't adhered to):
#import "ScheduleTableViewController.h"
#import "TFHpple.h"
#import "Game.h"
#import "ScheduleCellTableViewCell.h"
#import "GameModel.h"
#define team #"Muskegon"
#interface ScheduleTableViewController ()
{
GameModel *_homeModel;
}
#property (nonatomic, strong) UISegmentedControl *segmentedControl;
#property (nonatomic, strong) NSMutableArray *omahaHomeGames;
#property (nonatomic, strong) NSMutableArray *omahaAwayGames;
#property (nonatomic, strong) NSMutableArray *omahaAllGames;
#property (nonatomic, strong) NSArray *objects;
#property UIActivityIndicatorView *spinner;
#end
#implementation ScheduleTableViewController
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Filter through all the games for the games that contain Omaha as a home team and add them to the property omahaHomeGames
for (Game *game in items) {
if ([game.homeTeam containsString:team]) {
[_omahaHomeGames addObject:game];
}
}
// Filter through all the games for the games that contain Omaha as an away team and add them to the property omahaAwayGames
for (Game *game in items) {
if ([game.awayTeam containsString:team]) {
[_omahaAwayGames addObject:game];
}
}
// Filter through all the games for the games that contain Omaha as a home or away team and add them to the property omahaAllGames
for (Game *game in items) {
if ([game.homeTeam containsString:team] || [game.awayTeam containsString:team]) {
[_omahaAllGames addObject:game];
}
}
[_spinner stopAnimating];
// Reload the table view
[self.tableView reloadData];
}
- (instancetype) initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[GameModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
// add a segmented control to filter by Home/Away
NSArray *divisions = [[NSArray alloc] initWithObjects:#"All", #"Home", #"Away", nil];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:divisions];
segmentedControl.frame = CGRectMake(0, 0, 120, 30);
segmentedControl.tintColor = [UIColor orangeColor];
[segmentedControl addTarget:self action:#selector(filterByHomeAway:) forControlEvents:UIControlEventValueChanged];
segmentedControl.selectedSegmentIndex = 0;
self.navigationItem.titleView = segmentedControl;
self.segmentedControl = segmentedControl;
// alloc and init properties
self.omahaAllGames = [[NSMutableArray alloc] init];
self.omahaHomeGames = [[NSMutableArray alloc] init];
self.omahaAwayGames = [[NSMutableArray alloc] init];
}
return self;
}
- (IBAction)filterByHomeAway:(id)sender
{
NSLog(#"Filter: %ld", (long)self.segmentedControl.selectedSegmentIndex);
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
spinner.center = CGPointMake(screenWidth/2.0, screenHeight/5.0);
spinner.hidesWhenStopped = YES;
[self.view addSubview:spinner];
[spinner startAnimating];
self.spinner = spinner;
// Load the Cell NIB file
UINib *nib = [UINib nibWithNibName:#"ScheduleCellTableViewCell" bundle:nil];
// Register this NIB, which contains the cell
[self.tableView registerNib:nib forCellReuseIdentifier:#"ScheduleCell"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
// Return the number of rows in the section.
if (self.segmentedControl.selectedSegmentIndex == 0) {
return (self.omahaAllGames.count);
}
if (self.segmentedControl.selectedSegmentIndex == 1) {
return (self.omahaHomeGames.count);
}
if (self.segmentedControl.selectedSegmentIndex == 2) {
return self.omahaAwayGames.count;
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get a new or recycled cell
ScheduleCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ScheduleCell" forIndexPath:indexPath];
if (self.segmentedControl.selectedSegmentIndex == 0) {
_objects = _omahaAllGames;
}
if (self.segmentedControl.selectedSegmentIndex == 1) {
_objects = _omahaHomeGames;
}
if (self.segmentedControl.selectedSegmentIndex == 2) {
_objects = _omahaAwayGames;
}
// Configure the cell
Game *thisGame = [_objects objectAtIndex:indexPath.row];
if (self.segmentedControl.selectedSegmentIndex == 0) {
cell.versusLabel.text = [NSString stringWithFormat:#"%# vs. %#", thisGame.homeTeam, thisGame.awayTeam];
}
if (self.segmentedControl.selectedSegmentIndex == 1) {
cell.versusLabel.text = [NSString stringWithFormat:#"%# vs. %#", team, thisGame.awayTeam];
}
if (self.segmentedControl.selectedSegmentIndex == 2) {
cell.versusLabel.text = [NSString stringWithFormat:#"%# vs. %#", team, thisGame.homeTeam];
}
// Trim the white space from before and after the date/time
NSString *day = [thisGame.dayString substringFromIndex: 8];
NSString *dayModified = [day substringWithRange: NSMakeRange(0, 11)];
NSString *time = [thisGame.timeString substringFromIndex:9];
NSString *timeModified = [time substringWithRange: NSMakeRange(0, 8)];
NSString *dateAndTime = [NSString stringWithFormat:#"%# %#", dayModified, timeModified];
cell.dateLabell.text = dateAndTime;
cell.locationLabel.text = thisGame.arena;
// Assign the image of the away team if home, and the home team if away, and the team that isn't Omaha if all
if (self.segmentedControl.selectedSegmentIndex == 0) {
[thisGame assignAllTeamImage];
cell.imageView.image = thisGame.image;
}
if (self.segmentedControl.selectedSegmentIndex == 1) {
[thisGame assignHomeTeamImage];
cell.imageView.image = thisGame.image;
}
if (self.segmentedControl.selectedSegmentIndex == 2) {
[thisGame assignAwayTeamImage];
cell.imageView.image = thisGame.image;
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 70;
}
#end
How do I make them stay a consistent size? I just can't seem to make it work. Thank you.
Instead of displaying the image in the default imageView of cell, try creating a new imageView and adding it to the cell.
UIImageView *imageViewIcon = [[UIImageView alloc] initWithFrame:CGRectMake(5, 0, 50, 50)];
[cell addSubview:imageViewIcon];
if (self.segmentedControl.selectedSegmentIndex == 0) {
[thisGame assignAllTeamImage];
//cell.imageView.image = thisGame.image;
imageViewIcon.image = thisGame.image;
}
if (self.segmentedControl.selectedSegmentIndex == 1) {
[thisGame assignHomeTeamImage];
//cell.imageView.image = thisGame.image;
imageViewIcon.image = thisGame.image;
}
if (self.segmentedControl.selectedSegmentIndex == 2) {
[thisGame assignAwayTeamImage];
//cell.imageView.image = thisGame.image;
imageViewIcon.image = thisGame.image;
}
In the above code change the frame of the imageViewIcon as per your requirement.
And since you are using dequeueReusableCellWithIdentifier don't forget to give the imageViewIcon a tag and remove it at the beginning.
Hope this helps you. :)
I was trying to create a custom section for my tableview.Tableview itself works fine, but for some reason, compiler skips,
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
delegate method, and i tried what is written on the reference , and this . I also added the tableView:heightForHeaderInSection: method as it suggests in reference page but for some reason it just doesn't read these methods. Obviously, I am doing something wrong. If someone can point my mistake out, I would appreciate. Thank you in advance.
profileViewController.h
#import <UIKit/UIKit.h>
#import "PeopleModel.h"
#import <QuartzCore/QuartzCore.h>
#interface profileViewController : UIViewController
<UITableViewDataSource,UITableViewDelegate>
#property(strong,nonatomic)NSMutableArray *People;
#property (weak, nonatomic) IBOutlet UITableView *profileTableView;
#end
profileViewController.m
#import "profileViewController.h"
#interface profileViewController ()
+ (UIImage *)scale:(UIImage *)image toSize:(CGSize)size;
#end
#implementation profileViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.title = #"ProfileView";
self.profileTableView.dataSource = self;
self.profileTableView.dataSource = self;
PeopleModel *person1 = [[PeopleModel alloc]init];
person1.Name =#"mett";
person1.Email= #"mett#gmail.com";
person1.ProfilePicture =[UIImage imageNamed:#"profileImage.jpg"];
self.People = [NSMutableArray arrayWithObjects:person1,person1, nil];
self.profileTableView.layer.cornerRadius = 8.0;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.People count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont systemFontOfSize:19.0];
cell.detailTextLabel.font = [UIFont systemFontOfSize:12];
}
PeopleModel *item =[self.People objectAtIndex:indexPath.row];
cell.textLabel.text = item.Name;
cell.detailTextLabel.text = item.Email;
cell.imageView.image = item.ProfilePicture;
cell.imageView.image =[profileViewController scale:item.ProfilePicture toSize:CGSizeMake(115, 75)];
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 15;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSLog(#"cartman!");
if (section == 0) {
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
UIView* headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenRect.size.width, 44.0)];
//headerView.contentMode = UIViewContentModeScaleToFill;
// Add the label
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0, -5.0, 300.0, 90.0)];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.text = #"section one";
headerLabel.textColor = [UIColor blueColor];
headerLabel.highlightedTextColor = [UIColor blackColor];
//this is what you asked
headerLabel.font = [UIFont boldSystemFontOfSize:17];
headerLabel.shadowColor = [UIColor clearColor];
headerLabel.shadowOffset = CGSizeMake(0.0, 1.0);
headerLabel.numberOfLines = 0;
headerLabel.textAlignment = NSTextAlignmentCenter;
[headerView addSubview: headerLabel];
// Return the headerView
return headerView;
}
else return nil;
}
+ (UIImage *)scale:(UIImage *)image toSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You have a typo in the viewDidLoad method:
self.profileTableView.dataSource = self;
self.profileTableView.dataSource = self;
One of these should be delegate.
In iOS for the iPhone I want to make a control with similar appearance and behavior to the android spinner control when configured to behave like a drop down list box. Specifically when pressed a modal list of text options with radio buttons comes up and when one of them is pressed the list disappears and the control updates to that choice. Example:
So far I have seen a full-screen option using [self presentViewController...] with a custom ViewController but I want a partial screen (like pictured above) solution. Does anyone know how to do this or could point in the right direction.
The native solution to this will be a UIActionSheet which on iPhone will appear from the bottom and be partial screen or on iPad be very similar to the android version.
You can find the documentation here: UIActionSheet
if you didnt want to use the UIActionSheet and you wanted to make it reusable rather than adding a whole bund of UIViews to your current XIB, you could create a custom UIView with whatever interface you would need to populate it and use the interface builder to help make it look ok.
that view could have a message handler that posts the response that you would need to listen for.
then just init and load the view into your subviews and populate it
then post a message from the custom view to the handler you registered
so for your custom view you would have something like this.
#implementation SomeCustomView
+(SomeCustomView*)viewFromNibNamed:(NSString *)nibName{
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
SomeCustomView *customView = nil;
NSObject* nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[AADropDown class]]) {
customView = (SomeCustomView*)nibItem;
break;
}
}
return customView;
}
-(void)someInitializationWith:(NSArray*)repeatableData andNotificationId:(NSString*)noteId{
//set your stuff up for the view here and save the notification id
}
...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[[NSNotificationCenter defaultCenter] postNotificationName:Your_Notification_Id object:somevalue];
}
#end
and include other things, like in this case the tableview stuff or any other logic.
then in your viewcontroller you could call it like
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:#"customViewAction" object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
//deal with notification here
[[NSNotificationCenter defaultCenter] removeObserver: observer];
}];
SomeCustomView *cv =(SomeCustomView*) [SomeCustomView viewFromNibNamed:#"SomeCustomView"];
[cv someInitializationWith:arrayOptions andNotificationId:#"customViewAction"];
[self.view addSubview:cv];
and in your interface builder you will just need to make sure that the class of the view is set to your class type.
then you can easily reuse this code again whenever a user needs to select something else in the same manner.
Here is a variation on the solution suggested by AtomRiot.
On your view (xib or storyboard) make a button and assign this graphic to it. Don't worry if it appears stretched out in the editor. The code will make it a realizable graphic.
2X version
Then include the following files in your project (copied below):
DDLBHelper.h
DDLBHelper.m
Then in your ViewController's .h file make links to the button:
#property (weak, nonatomic) IBOutlet UIButton *ddlbB;
- (IBAction)ddlbBClick:(id)sender;
In you ViewController's .m file make the following calls:
#synthesize ddlbB, choiceLabel;
DDLBHelper *mDDLBH;
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *strings = [[NSArray alloc] initWithObjects:#"Item 1", #"Item 2", #"Item 3", nil];
mDDLBH = [[DDLBHelper alloc] initWithWithViewController:self button:ddlbB stringArray:strings currentValue:1];
}
- (IBAction)ddlbBClick:(id)sender {
[mDDLBH popupList];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[mDDLBH adjustToRotation];
}
Works just like android.
Here are the files:
DDLBHelper.h
// DDLBHelper.h
// Created by MindSpiker on 9/27/12.
#import <Foundation/Foundation.h>
#protocol DDLBHelperDelegate <NSObject>
#required
- (void) itemSelected: (int)value;
#end
#interface DDLBHelper : UIViewController <UITableViewDelegate, UITableViewDataSource>{
id <DDLBHelperDelegate> delegate;
}
#property (retain) id delegate;
// external interface
- (id) init;
- (id) initWithWithViewController:(UIViewController *)viewController button:(UIButton *)button stringArray:(NSArray *)values currentValue:(int) currentValue;
- (void) popupList;
- (BOOL) isShown;
- (void) adjustToRotation;
- (int) getValue;
- (NSString *)getValueText;
#end
DDLBHelper.m
// DDLBHelper.m
// Created by MindSpiker on 9/27/12.
#import "DDLBHelper.h"
#import <QuartzCore/QuartzCore.h>
#interface DDLBHelper () {
#private
UIViewController *mVC;
UIButton *mButton;
NSArray *mValues;
int mValue;
UITableView *mTV;
UIView *mBackgroundV;
}
#end
#implementation DDLBHelper
#synthesize delegate;
- (id) init {
self = [super init];
mVC = nil;
mButton = nil;
mValues = nil;
mValue = -1;
return self;
}
- (id) initWithWithViewController:(UIViewController *)viewController button:(UIButton *)button stringArray:(NSArray *)values currentValue:(int) currentValue {
self = [super init];
// save pointers
mVC = viewController;
mButton = button;
mValues = values;
mValue = currentValue;
[self setupButton];
return self;
}
- (void) popupList{
if (mBackgroundV == nil){
mBackgroundV = [self setupBackgroundView];
[mVC.view addSubview:mBackgroundV];
}
if (mTV == nil){
mTV = [self setupTableView];
[mVC.view addSubview:mTV];
}
[mTV reloadData];
[mBackgroundV setHidden:NO];
[mTV setHidden:NO];
}
- (BOOL) isShown{
return !mTV.isHidden;
}
- (void) adjustToRotation{
BOOL isShown = [self isShown];
// remove the controls
if (mBackgroundV != nil){
[mBackgroundV removeFromSuperview];
mBackgroundV = nil;
}
if (mTV != nil){
[mTV removeFromSuperview];
mTV = nil;
}
if (isShown){
[self popupList];
}
}
- (int) getValue{
return mValue;
}
- (NSString *) getValueText{
if (mValues != nil && mValue > -1) {
if (mValues.count > mValue){
return [mValues objectAtIndex:mValue];
}
}
return nil;
}
- (void) updateButtonTitle{
NSString *title = [NSString stringWithFormat:#" %#", [self getValueText]];
[mButton setTitle:title forState:UIControlStateNormal];
}
- (void) setupButton {
UIImage *buttonBG = [UIImage imageNamed:#"sis_proceeds_ddlb.png"];
UIEdgeInsets insets = UIEdgeInsetsMake(8, 8, 8, 45);
UIImage *sizableImg = [buttonBG resizableImageWithCapInsets:insets];
[mButton setBackgroundImage:sizableImg forState:UIControlStateNormal];
[mButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[self updateButtonTitle];
}
- (UIView *) setupBackgroundView{
UIView *v = [[UIView alloc] initWithFrame:mVC.view.bounds];
[[v layer] setOpaque:NO];
[[v layer] setOpacity:0.7f];
[[v layer] setBackgroundColor:[UIColor blackColor].CGColor];
return v;
}
- (UITableView *) setupTableView {
CGRect rect = [self makeTableViewRect];
UITableView *tv = [[UITableView alloc] initWithFrame:rect style:UITableViewStylePlain];
[tv setDelegate:self];
[tv setDataSource:self];
[tv setBackgroundColor:[UIColor whiteColor]];
[[tv layer] setBorderWidth:2];
[[tv layer] setBorderColor:[UIColor lightGrayColor].CGColor];
[[tv layer] setCornerRadius:10];
[mVC.view addSubview:tv];
return tv;
}
- (CGRect) makeTableViewRect {
float l=0.0, t=0.0, w=0.0, h=0.0, maxH=0.0, cellH=0.0, cellsH=0.0;
// get
l = mButton.frame.origin.x;
w = mButton.frame.size.width;
t = mVC.view.bounds.origin.y + 50;
maxH = mVC.view.bounds.size.height - 100;
// get cell height
UITableViewCell *c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cellH = c.bounds.size.height;
// see if list will overlow maxH(eight)
cellsH = cellH * mValues.count;
if (cellsH > maxH) {
h = maxH;
} else {
h = cellsH;
}
return CGRectMake(l, t, w, h);
}
#pragma mark - TableView Delegate functions
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1; // this is a one section table
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return mValues.count; // should be called for only one section
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// try to resuse a cell if possible
static NSString *RESUSE_IDENTIFIER = #"myResuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RESUSE_IDENTIFIER];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RESUSE_IDENTIFIER];
}
cell.textLabel.text = [mValues objectAtIndex:indexPath.row];
if (mValue == indexPath.row){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// save value and hide view
mValue = indexPath.row;
[self updateButtonTitle];
[mBackgroundV setHidden:YES];
[mTV setHidden:YES];
[delegate itemSelected:mValue];
}
#end