Implementing a volume graph on iOS - 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];
}

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.

Xcode: Remove an array of textiews from screen

I'm currently adding 5 textviews onto the viewcontroller programatically inside the viewDidLoad method.
for (int i = 0; i < 5; i++) {
//Add 5 textviews
UITextView *reqTV = [[UITextView alloc] initWithFrame:CGRectMake(30,30,250,50)];
reqTV.text = #"This is a textview";
[self.view addSubview:reqTV];
}
If later, I want to delete (not hide) these 5 textviews with a button click, how would I do that?
I have thought of using this, but am not sure how to call all 5 textviews to delete them.
- (void)removeTextViewButton:(id)sender {
[reqTV removeFromSuperview]; //remove textview
}
Thank you.
I see two easy ways:
You can save your textViews inside array as ivar of your controller.
And later remove each textView in array.
for (int i = 0; i < 5; i++) {
...
[textViews addObject: reqTV];
...
}
- (void)removeTextViewButton:(UIButton *)sender {
[textViews makeObjectsPerformSelector:#selector(removeFromSuperview)];
}
2. Assign static tag for each textView:
for (int i = 0; i < 5; i++) {
...
reqTV.tag = 1001; // for example
}
- (void)removeTextViewButton:(UIButton *)sender {
NSArray *subs = [NSArray arrayWithArray: self.view.subviews];
for (UIView *sub in subs) {
if (sub.tag == 1001) {
[sub removeFromSuperview];
}
}
}
Used code below to remove UITextViews:
- (void)removeTextViewButton:(id)sender {
NSArray *reqTVViews = [NSArray arrayWithArray: self.view.subviews];
for (UIView *tvView in reqTVViews) {
if ([tvView isKindOfClass:[UITextView class]]) {
[tvView removeFromSuperview];
}
}
}
When you are adding UITextFields on viewController use tag value to uniquely identify each textField.
You can store each tag value in an array for further use, eg.
#property (nonatomic, strong) NSMutableArray *tagArray;
NSMutableArray *tagArray = [NSMutableArray array];
for (int i = 101; i <= 105; i++ ) {
UITextField *txt = [UITextField alloc] init]; //for eg.
...
...
txt.tag = i;
[arr addObject:[NSNumber numberWithInt:i]];
[self.view addSubView:txt];
}
When you want to delete any of the textField or all then...
UIView *view = [self.view viewWithTag:<tag value>];
[view removeFromSuperview];
eg.
for (int i = 0; i < tagArray.count; i++) {
NSInteger tag = [[arr objectAtIndex:i] intValue];
UITextField *txt = (UITextField *)[self.view viewWithTag:tag];
[txt removeFromSuperview];
}
You can remove all subview in one go
- (void)removeTextViewButton:(id)sender
{
for (UIView *subview in views.subviews)
{
[subview removeFromSuperview];
}
}
Happy Coding.. :)

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.

iOS: Dereferencing NSMutableArray

When someone votes down the question... kindly please also comment why you had voted it down.
I have a UIView (myHolderView) on which I want to place 9 or more other views.
I added some UIView (MyView) instances to an NSMutableArray of 9 elements (MyArray).
MyView has a label (UILabel) on it. Can someone please assist how to get label's text using MyArray?
Note:
I am seeing the UIViews added to main holder view and getting labels on it.
initWithFrame:(v,v,v,v) number: (value) is the init method I had overloaded init with.
//These two are declared as global variable
NSMutableArray *tiles;
MyView *tile;
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad
{
[super viewDidLoad];
tiles=[[NSMutableArray alloc]initWithCapacity:9];
for (int xIndex=0; xIndex<3; xIndex++)
{
for(int yIndex=0; yIndex<3; yIndex++)
{
static int label=0;
[tiles addObject:[[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:[[NSString alloc] initWithFormat:#"%d",label+1]]];
[self.myHolderView addSubview:tiles[label]];
//Now - when I want to print the labels onto Console as NSLog messages
//it is printing null
MyView *n=[[MyView alloc]init];
n=(MyView *)[tiles objectAtIndex:label++];
NSLog(#"%#---", n.myLabel.text);
//this also does not work. Definitely wrong dereferencing
// NSLog(#"%#---", (MyView*) [tiles ObjectAtIndex:1].mylabel.text);
//label++;
}
}
}
Please help
how to dereference NSMuableArray objects to the type what we want (the
type we know it exactly)
MyView:
- (id)initWithFrame:(CGRect)frame number:(NSString *)num;
{
self = [super initWithFrame:frame];
if (self) {
MyView *tile = [[[NSBundle mainBundle] loadNibNamed:#"XView" owner:self options:nil] objectAtIndex:0];
tile.myLabel.text=num;
[self addSubview:tile];
}
return self;
}
According to your comment, you want to add some view to your holderview. Later, you want to change text in particular view. So I suggest some way. I think, It will help you..
1) create and Add subview to your holder view
for (int xIndex=0; xIndex<9; xIndex++)
{
MyView *subView = [[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:[[NSString alloc] initWithFormat:#"%d",somecount+1]]]; // doesn't know why do you try this..
subView.tag = xIndex;
[self.myHolderView addSubview: subView];
}
2) Get particular subview with tag value.(No need to maintain any other array in your view controller)
After some time, you want to change text in particular view. Just try it..
MyView *subView = [self.myHolderView viewWithTag:index]; // as like, you access array
subView.myLabel.text = yourUpdateText;
#import "ViewController.h"
#interface MyView : UIView
#property UILabel *myLabel;
- (id) initWithFrame: (CGRect) rect number: (NSString *) string;
#end
#implementation MyView
- (id) initWithFrame: (CGRect) frame number: (NSString *) number {
self = [super initWithFrame:frame];
if (!self) return nil;
// Any things here
// Are you initialized label?
_myLabel = [UILabel new];
[_myLabel setText:number];
[self addSubview:_myLabel];
return self;
}
#end
#interface ViewController ()
#property UIView *myHolderView;
#end
NSMutableArray *tiles;
MyView *tile;
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad {
[super viewDidLoad];
tiles=[[NSMutableArray alloc]initWithCapacity:9];
int value = 1, label = 0;
myHolderView = [UIView new];
for (int xIndex=0; xIndex<3; xIndex++) {
for(int yIndex=0; yIndex<3; yIndex++) {
NSString *number = [[NSString alloc] initWithFormat:#"%d",label+1];
MyView *myView = [[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:number];
[tiles addObject:myView];
[self.myHolderView addSubview:tiles[label]];
//Now - when I want to print the labels onto Console as NSLog messages
//it is printing number
MyView *n=(MyView *)[tiles objectAtIndex:label++];
NSLog(#"%#---", n.myLabel.text);
}
}
// And without tiles array
for (UIView *subView in [myHolderView subviews])
for (UIView *subSubView in [subView subviews])
if ([subSubView isKindOfClass:[UILabel class]])
NSLog(#"%#", [(UILabel *) subSubView text]);
}
#end
Your problem is nothing to do with arrays or dereferencing. It's a logical issue because you are creating multiple views and nesting them.
Change the implementation of MyView:
- (id)initWithFrame:(CGRect)frame number:(NSString *)num;
{
self = [[[NSBundle mainBundle] loadNibNamed:#"XView" owner:self options:nil] objectAtIndex:0];
self.frame = frame;
self.myLabel.text = num;
return self;
}
Try something like this (typed inline so watch for issues):
- (void)viewDidLoad
{
[super viewDidLoad];
tiles = [[NSMutableArray alloc] initWithCapacity:9];
NSUInteger counter = 0;
for (int xIndex=0; xIndex<3; xIndex++)
{
for(int yIndex=0; yIndex<3; yIndex++)
{
MyView *newView = [[MyView alloc] initWithFrame:CGRectMake(value,value,value,value) number:[[NSString alloc] initWithFormat:#"%d", counter+1]]];
[self.myHolderView addSubview:newView];
[tiles addObject:newView];
MyView *logView = (MyView *)[tiles objectAtIndex:counter];
NSLog(#"view %d is %#---", counter, logView.myLabel.text);
counter++;
}
}
}

Return position in NSArray from ScrollView

I'm trying to create a scrollview with an array of clickable UIImageView's. My goal is that when an ImageView is clicked, it returns which position in the array it occupies. The problem is that i don't know how to "catch" the position's number. How do I do that?
So far I have:
- (IBAction)respondToTapGesture:(UITapGestureRecognizer *)recognizer {
NSLog(#"%#",)//here is where i want to return the element's position.
}
-(void) preenchemenu {
[menu setContentSize:CGSizeMake(400, 91)];
int x=0;
imagensmenu=[NSArray arrayWithObjects:[UIImage imageNamed:#"teste2.tiff"],[UIImage imageNamed:#"teste2.tiff"], nil];
for (int i = 0; i <3; i++) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x,0 , 90, 91)];
x=x+90;
imageView.image = [imagensmenu objectAtIndex:i];
imageView.tag = 1000+ i;
imageView.userInteractionEnabled = YES;
imageView.multipleTouchEnabled = YES;
UITapGestureRecognizer *tapRecognizermenu = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(respondToTapGesture:)];
tapRecognizermenu.numberOfTapsRequired = 1;
[imageView addGestureRecognizer:tapRecognizermenu];
[menu addSubview:imageView];
}
}
You can create an array to hold you imageViews and then find the position of your imageView in this array when it is tapped.
Add a property for this
#property (nonatomic, strong) NSMutableArray *imageViews;
initialise it in init
- (id)init...
{
self = [super init...
if (self) {
_imageViews = [NSMutableArray array];
}
return self;
}
Then amend your current method slightly to also add the imageViews to this array as well as a subview of the menu
[self.imageViews addObject:imageView];
[menu addSubview:imageView];
Then in your gesture recognizer call back you can do
- (void)respondToTapGesture:(id)sender;
{
UIView *view = [sender view];
NSLog(#"%d", [self.imageViews indexOfObject:view]);
}
Just find the index by
- (IBAction)respondToTapGesture:(UITapGestureRecognizer *)recognizer
{
UIView *view = recognizer.view;
NSLog(#"Index of image in array is %d", view.tag-1000);
}

Resources