UItableView with multiple images and setting onclick on each one of them - ios

I have a scenario where I want to keep "thumbnails" of images on the row of the tableview. I decide on runtime the numbers of images that goes in a row.
Secondly, On click of any of the image I want to launch a viewcontroller with elaborated image of it with some description.
How can I achieve this?
|________________|
| 1 2 3 4 5 6 |
|________________|
| 7 8 9 10 11 12 |
|________________|
| 12 14 15 |
|________________|
Imagine the numbers above as thumbnails. Onclick of any of the number I want to launch a new view controller which gives details about the image.
code snippet:
-(void) populateTableView
{
NSMutableArray* srcArray = [[NSMutableArray alloc] initWithArray:[[ImageDataSource sharedImageDataSource] getThumbnailsSrcArray]];
int noOfImages = srcArray.count;
float rowc = (noOfImages / ROW_ELEM_COUNT) + 0.5;
int rowCount = roundf(rowc);
titleData = [[NSMutableArray alloc]init];
int j=0;
for (int i=0; i<rowCount; i++) {
NSMutableArray* rowArray = [[NSMutableArray alloc] init];
for (int k=0; k < ROW_ELEM_COUNT; k++) {
if (j < noOfImages) {
NSString* imgPath = [srcArray objectAtIndex:j];
UIImage* img = [[UIImage alloc] initWithContentsOfFile:imgPath];
[rowArray addObject:img];
[img release];
imgPath=nil;
}
j++;
}
[titleData addObject:rowArray];
}
titleView = [[UITableView alloc] initWithFrame:CGRectMake(90.0,156.0,590.0,630.0)
style:UITableViewStyleGrouped];
titleView.delegate = self;
[self.view addSubview:titleView];
[titleView release];
}
So basically I have array of array as my DS. index of each array will be row of table view and the array inside will have images. But I am not sure how to populate the tableview.
Any clue anybody?

You have two choices.
• Add a UIButton with the UIImage and add each button as a subview on your tableview cell.
or
• Figure out where on the cell the user tapped and from here calculate which image they tapped and which action to take:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
// do your stuff
//..
}

Related

VFL with Xcode for Auto Layout

I have an existing application that has the positioning of all the elements written in code rather then in auto-layout. I am trying to use that existing code to add Auto-Layout for those elements.
I am trying to have the two buttons xButton and checkButton to show up at the bottom of the screen next to each other, which will work for all iPhone screens. The current behavior is that the buttons are offset towards the bottom of the screen.
Below is my code where my VFL is located - however this is placed in a UIView instead of a ViewController.
I have tried to figure this out on my own but no luck, I would love it if someone could give me a hand with this topic as well as this certain issue.
Thank you
#implementation DraggableViewBackground{
NSInteger cardsLoadedIndex; //%%% the index of the card you have loaded into the loadedCards array last
NSMutableArray *loadedCards; //%%% the array of card loaded (change max_buffer_size to increase or decrease the number of cards this holds)
UIButton* checkButton;
UIButton* xButton;
}
//this makes it so only two cards are loaded at a time to
//avoid performance and memory costs
static const int MAX_BUFFER_SIZE = 2; //%%% max number of cards loaded at any given time, must be greater than 1
static const float CARD_HEIGHT = 386; //%%% height of the draggable card
static const float CARD_WIDTH = 290; //%%% width of the draggable card
#synthesize exampleCardLabels; //%%% all the labels I'm using as example data at the moment
#synthesize allCards;//%%% all the cards
- (void)loadView {
[self setupView];
exampleCardLabels = [[NSArray alloc]initWithObjects:#"Are you in the Technology Industry?",#"Do you have more then 3 years of work experiance?",#"Do you have more then 3 years of work experiance?",#"Are you willing to relocate?",#"Did you enjoy this app?", nil]; //%%% placeholder for card-specific information
loadedCards = [[NSMutableArray alloc] init];
allCards = [[NSMutableArray alloc] init];
cardsLoadedIndex = 0;
[self loadCards];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[super layoutSubviews];
[self setupView];
exampleCardLabels = [[NSArray alloc]initWithObjects:#"Are you in the Technology Industry?",#"Do you have more then 3 years of work experiance?",#"Do you have more then 3 years of work experiance?",#"Are you willing to relocate?",#"Did you enjoy this app?", nil]; //%%% placeholder for card-specific information
loadedCards = [[NSMutableArray alloc] init];
allCards = [[NSMutableArray alloc] init];
cardsLoadedIndex = 0;
[self loadCards];
}
return self;
}
//%%% sets up the extra buttons on the screen
// VFL Visual Formal Language -
- (void)setupView {
#warning customize all of this. These are just place holders to make it look pretty
self.backgroundColor = [UIColor colorWithRed:.92 green:.93 blue:.95 alpha:1]; //the gray background colors
xButton = [[UIButton alloc]initWithFrame:CGRectMake(60, CGRectGetHeight(self.frame)- 100, 59, 59)];
[xButton setImage:[UIImage imageNamed:#"xButton"] forState:UIControlStateNormal];
[xButton addTarget:self action:#selector(swipeLeft) forControlEvents:UIControlEventTouchUpInside];
[xButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[xButton setBackgroundColor:[UIColor redColor]];
checkButton = [[UIButton alloc]initWithFrame:CGRectMake(200, CGRectGetHeight(self.frame)- 100, 59, 59)];
[checkButton setImage:[UIImage imageNamed:#"checkButton"] forState:UIControlStateNormal];
[checkButton addTarget:self action:#selector(swipeRight) forControlEvents:UIControlEventTouchUpInside];
[checkButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[checkButton setBackgroundColor:[UIColor blueColor]];
[self addSubview:xButton];
[self addSubview:checkButton];
}
#warning include own card customization here!
//%%% creates a card and returns it. This should be customized to fit your needs.
// use "index" to indicate where the information should be pulled. If this doesn't apply to you, feel free
// to get rid of it (eg: if you are building cards from data from the internet)
-(DraggableView *)createDraggableViewWithDataAtIndex:(NSInteger)index
{
DraggableView *draggableView = [[DraggableView alloc]initWithFrame:CGRectMake((self.frame.size.width - CARD_WIDTH)/2,(self.frame.size.height - CARD_HEIGHT)/2, CARD_WIDTH, CARD_HEIGHT)];
draggableView.information.text = [exampleCardLabels objectAtIndex:index];//%%% placeholder for card-specific information
draggableView.delegate = self;
return draggableView;
}
//%%% loads all the cards and puts the first x in the "loaded cards" array
-(void)loadCards
{
if([exampleCardLabels count] > 0) {
NSInteger numLoadedCardsCap =(([exampleCardLabels count] > MAX_BUFFER_SIZE)?MAX_BUFFER_SIZE:[exampleCardLabels count]);
//%%% if the buffer size is greater than the data size, there will be an array error, so this makes sure that doesn't happen
//%%% loops through the exampleCardsLabels array to create a card for each label. This should be customized by removing "exampleCardLabels" with your own array of data
for (int i = 0; i<[exampleCardLabels count]; i++) {
DraggableView* newCard = [self createDraggableViewWithDataAtIndex:i];
[allCards addObject:newCard];
if (i<numLoadedCardsCap) {
//%%% adds a small number of cards to be loaded
[loadedCards addObject:newCard];
}
}
//%%% displays the small number of loaded cards dictated by MAX_BUFFER_SIZE so that not all the cards
// are showing at once and clogging a ton of data
for (int i = 0; i<[loadedCards count]; i++) {
if (i>0) {
[self insertSubview:[loadedCards objectAtIndex:i] belowSubview:[loadedCards objectAtIndex:i-1]];
} else {
[self addSubview:[loadedCards objectAtIndex:i]];
}
cardsLoadedIndex++; //%%% we loaded a card into loaded cards, so we have to increment
}
}
}
#warning include own action here!
//%%% action called when the card goes to the left.
// This should be customized with your own action
-(void)cardSwipedLeft:(UIView *)card;
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
#warning include own action here!
//%%% action called when the card goes to the right.
// This should be customized with your own action
-(void)cardSwipedRight:(UIView *)card
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
//%%% when you hit the right button, this is called and substitutes the swipe
-(void)swipeRight
{
DraggableView *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeRight;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView rightClickAction];
}
//%%% when you hit the left button, this is called and substitutes the swipe
-(void)swipeLeft
{
DraggableView *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeLeft;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView leftClickAction];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
To achieve what you want to do with the buttons you can use VFL like this:
self.view.translatesAutoresizingMaskIntoConstraints = NO;
button1.translatesAutoresizingMaskIntoConstraints = NO;
button2.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *viewsDict = #{#"button1": button1, #"button2": button2};
NSArray *horizontalContraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[button1(150)][button2(150)]-|"
options:NSLayoutFormatAlignAllBottom
metrics:nil
views:viewsDict];
NSArray *verticalConstraintsButton1 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button1]-20-|"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:viewsDict];
NSArray *verticalConstraintsButton2 = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button2]-20-|"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:viewsDict];
[self.view addConstraints:horizontalContraints];
[self.view addConstraints:verticalConstraintsButton1];
[self.view addConstraints:verticalConstraintsButton2];
This give the buttons each a width of 150 pt and places them 20 pt from the left, right and bottom of your view.

Reload UIScrollView by tapping a UIButton

Let me explain my project first. I have some data in my SQLIte DB table called "note".
In "note" table I have these fields: id, noteToken, note.
What I am doing here is load all the note in an NSMUtableArray from that table. And create UIButton according to that array content number and add those buttons in a UIScrollView as subView. The number of buttons and width of scrollview generate auto according to the number of content of that array. Now, when some one tap one of those Buttons, it will bring him to a next viewController and show him the corresponding note details in that viewController.
I do the same thing with another NSMUtableArray, but these time it read all the id from the "note" table. It equally generate new delete button in the same UIScrollView. But if some one tap on these delete button it will delete that particular note from the table "note" of SQLIte DB. AND RELOAD THE UIScrollView. All are done except the RELOAD THE UIScrollView part. This is what I want. I tried with all exist solution but don't know why it's not working.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.noteToken = [NSString stringWithFormat:#"%#%#", fairId, exibitorId];
scrollViewNoteWidth = 100;
[scrollViewNote setScrollEnabled:YES];
[scrollViewNote setContentSize:CGSizeMake((noteButtonWidth * countNoteButtonArray) + scrollViewNoteWidth, 100)];
sqLite = [[SQLite alloc] init];
[self.sqLite callDataBaseAndNoteTableMethods];
self.noteButtonArrayy = [[NSMutableArray alloc] init];
noteButtonArrayy = [self.sqLite returnDataFromNoteTable:noteToken];
[self LoadNoteButtonAndDeleteButton:noteButtonArrayy];
}
//////////////*----------------------- Note Section (Down) -----------------------*//////////////
-(void) LoadNoteButtonAndDeleteButton:(NSMutableArray *) noteButtonArray
{
sQLiteClass = [[SQLiteClass alloc] init];
noteButtonArrayToShowNoteButton = [[NSMutableArray alloc] init];
/*--------------- Load the noteButton & pass note (Down)---------------*/
for (int i = 0; i < [noteButtonArray count]; i++)
{
sQLiteClass = [noteButtonArray objectAtIndex:i];
// NSString *ids = [NSString stringWithFormat:#"%d", sQLiteClass.idNum];
NSString *nt = sQLiteClass.note;
[noteButtonArrayToShowNoteButton addObject:nt];
}
[self ShowNoteButtonMethod:noteButtonArrayToShowNoteButton];
/*--------------- Load the noteButton & pass note (Up)---------------*/
/*--------------- Load the deleteButton & pass id (Down)---------------*/
noteButtonArrayToDeleteNoteButton = [[NSMutableArray alloc] init];
for (int i = 0; i < [noteButtonArray count]; i++)
{
sQLiteClass = [noteButtonArray objectAtIndex:i];
// Convert int into NSString
NSString *ids = [NSString stringWithFormat:#"%d", sQLiteClass.idNum];
[noteButtonArrayToDeleteNoteButton addObject:ids];
}
[self ShowNoteDeleteButtonMethod:noteButtonArrayToDeleteNoteButton];
/*--------------- Load the deleteButton & pass id (Down)---------------*/
}
-(void) ShowNoteButtonMethod:(NSMutableArray *) btnarray
{
countNoteButtonArray = [btnarray count];
// For note button
noteButtonWidth = 60;
noteButtonXposition = 8;
for (NSString *urls in btnarray)
{
noteButtonXposition = [self addNoteButton:noteButtonXposition AndURL:urls];
}
}
-(int) addNoteButton:(int) xposition AndURL:(NSString *) urls
{
noteButton =[ButtonClass buttonWithType:UIButtonTypeCustom];
noteButton.frame = CGRectMake(noteButtonXposition, 8.0, noteButtonWidth, 60.0);
[noteButton setImage:[UIImage imageNamed:#"note.png"] forState:UIControlStateNormal];
[noteButton addTarget:self action:#selector(tapOnNoteButton:) forControlEvents:UIControlEventTouchUpInside];
[noteButton setUrl:urls];
noteButton.backgroundColor = [UIColor clearColor];
[self.scrollViewNote addSubview:noteButton];
noteButtonXposition = noteButtonXposition + noteButtonWidth + 18;
return noteButtonXposition;
}
-(void)tapOnNoteButton:(ButtonClass*)sender
{
urlNote = sender.url;
[self performSegueWithIdentifier:#"goToNoteDetailsViewController" sender:urlNote];
}
-(void) ShowNoteDeleteButtonMethod:(NSMutableArray *) btnarray
{
countNoteButtonArray = [btnarray count];
// For delete button
deleteNoteButtonWidth = 14;
deleteNoteButtonXposition = 31;
for (NSString *idNumber in btnarray)
{
deleteNoteButtonXposition = [self addDeleteButton:deleteNoteButtonXposition AndURL:idNumber];
}
}
-(int) addDeleteButton:(int) xposition AndURL:(NSString *) idNumber
{
deleteNoteButton =[ButtonClass buttonWithType:UIButtonTypeCustom];
deleteNoteButton.frame = CGRectMake(deleteNoteButtonXposition, 74.0, deleteNoteButtonWidth, 20.0);
[deleteNoteButton setImage:[UIImage imageNamed:#"delete.png"] forState:UIControlStateNormal];
[deleteNoteButton addTarget:self action:#selector(tapOnDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
[deleteNoteButton setIdNum:idNumber];
deleteNoteButton.backgroundColor = [UIColor clearColor];
[self.scrollViewNote addSubview:deleteNoteButton];
deleteNoteButtonXposition = deleteNoteButtonXposition + deleteNoteButtonWidth + 65;
return deleteNoteButtonXposition;
}
-(void)tapOnDeleteButton:(ButtonClass*)sender
{
idNumb = sender.idNum;
[self.sqLite deleteData:[NSString stringWithFormat:#"DELETE FROM note WHERE id IS '%#'", idNumb]];
// NSLog(#"idNumb %#", idNumb);
//[self.view setNeedsDisplay];
//[self.view setNeedsLayout];
//[self LoadNoteButtonAndDeleteButton];
//[self viewDidLoad];
// if ([self isViewLoaded])
// {
// //self.view = Nil;
// //[self viewDidLoad];
// [self LoadNoteButtonAndDeleteButton];
// }
}
//////////////*----------------------- Note Section (Up) -----------------------*//////////////
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"goToNoteDetailsViewController"])
{
NoteDetailsViewController *noteDetailsViewController = [segue destinationViewController];
[noteDetailsViewController setUrl:sender];
}
}
Here's the screen shot:
Here we can feel the difference between UIScrollView and UICollectionView, however UICollectionView is made up of UIScrollView, UICollectionView can be reload and adjust its content accordingly, where UIScrollView can't.
Ok, now in your case, you've to reload (refresh) your scroll view, which is not possible as we can with UICollectionView or UITableView.
You've two options,
Best option (little tough) : replace UIScrollView with UICollectionView - will take some of your time, but better for reducing code complexity and good performance of your app.
Poor option (easy) : Stay as it with UIScrollView - when you want to reload, delete each subview from it, and then again show and load everything. Highly not recommended.
IMHO, you should go with best option.

Implementing a volume graph on iOS

I'm trying to implement a graph (UIView) like this:
My current approach is to draw the rounded rects with UIBezierPath inside drawRect, store a pointer to every path in a private NSMutableArray and in my View Controller send a message to update the graph with the new value. (https://gist.github.com/gverri/f238ad17f90013bfc832)
But somehow I can't send a message to my view to change the colors of the paths.
.
I'm still a relative beginner and I'm sure I'm trying to reinvent the wheel somehow. Or just missing something obvious.
Should I redraw the graph at every update (1s ~ 2s)?! Should I use a special framework to do this?
Edit:
After digging a little I found out that I can't save a pointer to a path. After drawRect my pointers disappear from the array.
It seems I would need to use Core Graphics do make it work with this approach.
Is there an easier alternative?
I made a test app that uses a column view with 7 rounded rect subviews to implement the graph. Here's the ColumnView class (a subview of UIView),
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSMutableArray *temp = [NSMutableArray new];
_offColor = [UIColor colorWithRed:238/255.0 green:230/255.0 blue:238/255.0 alpha:1];
_onColor = [UIColor colorWithRed:154/255.0 green:102/255.0 blue:155/255.0 alpha:1];
for (int i=0; i<7; i++) {
UIView *roundRect = [[UIView alloc] initWithFrame:CGRectMake(0, i*34, frame.size.width -2, (frame.size.height/7) - 4)];
roundRect.backgroundColor = _offColor;
roundRect.layer.cornerRadius = 3;
[temp insertObject:roundRect atIndex:0];
[self addSubview:roundRect];
}
_columns = [NSArray arrayWithArray:temp];
}
return self;
}
-(void)setNumberOfDarkRects:(NSInteger) num {
[self.columns enumerateObjectsUsingBlock:^(UIView *sub, NSUInteger idx, BOOL *stop) {
if (idx + 1 <= num) {
sub.backgroundColor = self.onColor;
}else{
sub.backgroundColor = self.offColor;
}
}];
}
This is how I create the grid, and test updating the columns,
#interface ViewController ()
#property (strong,nonatomic) NSMutableArray *columns;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.columns = [NSMutableArray new];
for (int i=0; i<21; i++) {
ColumnView *column = [[ColumnView alloc] initWithFrame:CGRectMake(i * 22 + 10, 50, 22, 240)];
[self.view addSubview:column];
[self.columns addObject:column];
}
[self performSelector:#selector(doRandomColoring) withObject:nil afterDelay:2];
}
-(void)doRandomColoring {
for (ColumnView *col in self.columns) {
[col setNumberOfDarkRects:arc4random_uniform(8)];
}
[self performSelector:#selector(doRandomColoring) withObject:nil afterDelay:.2];
}

Add multiple UIImage copies using UIStepper

I want to add/delete images in a view using a UIStepper. So every time you press the '+' the same image is copied within the same viewController, still being able to move/rotate every copy.
I have something like this:
- (IBAction)ValueChanged:(UIStepper *)value
{
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
int j = (int) value.value;
for( int i = 0 ; i < j ; ++i )
{
UIImageView *newImageView = [[UIImageView alloc] init];
newImageView.image = self.myImageView.image;
[imageArray addObject:newImageView];
[self.view addSubview:imageArray[i]];
}
}
but this does not work because it just replaces the previous image.
Is there a proper way to copy the same image multiple times using the UIStepper?

touchmoved not working after displaying uiimageviews from NSMutable array

I am developing a project in which I have created a UIImageView dynamically using an NSMutableArray
The code for this is:
for (int i = 0; i < index ; i++) {
image_name= [NSString stringWithFormat:#"%#%dpng.png",[string_values objectAtIndex:0],i+1];
UIImageView *tempImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:image_name]];
tempImageView.userInteractionEnabled = YES;
CGSize size2=tempImageView.image.size;
int width2=size2.width/2;
int height2 =size2.height/2;
tempImageView.frame = CGRectMake(xvalue,yvalue,width2,height2);
[myArray addObject:tempImageView];
}
After that I add the subview to show it on the screen with this code:
for(int i=0;i<[myArray count];i++) {
[self.view addSubview:[myArray objectAtIndex:i]];
}
All the above code runs well as according to the needs. Now for the touchesBegan method, I am doing this code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
printf("the touch is fired");
for(int i=0;i<[myArray count];i++) {
[self.view addSubview:[myArray objectAtIndex:i]];
touchStart = [[touches anyObject] locationInView:[myArray objectAtIndex:i]];
}
}
The app crashes when the touchesBegan method is called, right after it prints the first line of the touchesBegan method.
I am having difficulties here and have also gone through many question/answers but can't find a working solution. Any help will be appreciated. Thanks for Good Replies
To make your imageviews draggable,
1) Remove all - (void)touches methods
2) Add these two lines just after creating UIImageView *tempImageView (in loop)
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleDrag:)];
[tempImageView addGestureRecognizer:panRecognizer];
3) then, add this method somewhere in your view controller.m file
-(void) handleDrag:(UIPanGestureRecognizer *) panGesture{
CGPoint transition = [panGesture translationInView:self.view];
panGesture.view.center = CGPointMake(panGesture.view.center.x + transition.x, panGesture.view.center.y +transition.y);
[panGesture setTranslation:CGPointMake(0,0) inView:self.view];}

Resources