Filling a grid with buttons - objective-c

I am new to objective c and trying the learn the basics of creating a UI. In my UIView class, I have created a grid along with buttons, yet those buttons don't actually exist (as far as I can tell). Ideally, when I click a button, the image is supposed to change, but that doesn't happen. Where should I be looking at to fix?
- (id)initWithFrame:(CGRect)frame {
if( self = [super init]){
tiles_ = [NSMutableArray array];
tileClosed = NO;
}
return self = [super initWithFrame:frame];
}
- (void) initTile : (Tile *) sender
{
int MINE_COUNT = 16;
for(int i = 0; i < MINE_COUNT; i++){
while(1){
int rand = random() % [tiles_ count];
Tile * tile = [tiles_ objectAtIndex:rand];
if(tile != sender && !tile.isMine){
tile.isMine = YES;
break;
}
}
}
tileClosed = YES;
}
- (void)drawRect:(CGRect)rect
{
NSLog( #"drawRect:" );
CGContextRef context = UIGraphicsGetCurrentContext();
// shrink into upper left quadrant
CGRect bounds = [self bounds]; // get view's location and size
CGFloat w = CGRectGetWidth( bounds ); // w = width of view (in points)
CGFloat h = CGRectGetHeight ( bounds ); // h = height of view (in points)
dw = w/16.0f; // dw = width of cell (in points)
dh = h/16.0f; // dh = height of cell (in points)
NSLog( #"view (width,height) = (%g,%g)", w, h );
NSLog( #"cell (width,height) = (%g,%g)", dw, dh );
// draw lines to form a 16x16 cell grid
CGContextBeginPath( context ); // begin collecting drawing operations
for ( int i = 1; i < 16; ++i )
{
// draw horizontal grid line
CGContextMoveToPoint( context, 0, i*dh );
CGContextAddLineToPoint( context, w, i*dh );
}
for ( int i = 1; i < 16; ++i )
{
// draw vertical grid line
CGContextMoveToPoint( context, i*dw, 0 );
CGContextAddLineToPoint( context, i*dw, h );
}
for(int x=1; x<16;x++){
for(int y=1;y<16;y++){
Tile * tile = [[Tile alloc] init];
[tile setFrame:CGRectMake(x * 16.0f, y * 16.0f, 16.0f, 16.0f)];
[tile addTarget:self action:#selector(clickCell:) forControlEvents:UIControlEventTouchUpInside];
[tiles_ addObject: tile]; }
}
[[UIColor grayColor] setStroke]; // use gray as stroke color
CGContextDrawPath( context, kCGPathStroke ); // execute collected drawing ops
}
- (void) clickCell : (Tile *) sender
{
if(! tileClosed) [self initTile: sender];
[sender open];
}

Your initwithFrame: method is broken: the return line should just be return self;.
What you're doing at the moment in effect clobbers the tiles_ array, so it ends up as nil, hence attempting to store tiles in it does nothing (and hence they aren't retained).

Your init method is incorrect and you can change it simply to
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame]
if( self)
{
tiles_ = [NSMutableArray array];
tileClosed = NO;
}
return self;
}
and for the buttons to be appearing you are not adding it to the subview the
[self.view addSubview:tile];
is missing.

Related

Objective-C append image thumbnails onto the end of a view

I'm trying to append images onto a view and have them display a thumbnail in a nice neat row. The issue I'm having is that it appears all images simply get added on top on each other in the corner as only the last image in the array is dispayed.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.notesField.delegate = self;
NSString *userName = ((ontracAppDelegate*)[UIApplication sharedApplication].delegate).userName;
self.textField.text = [NSString stringWithFormat:#"Date Created: %# \nCreated By: %#",[NSDate date], userName];
if (self.noteText != nil) {
self.notesField.text = self.noteText;
self.notesField.editable = NO;
self.textLabel.hidden = TRUE;
}
[self.view resignFirstResponder];
if ([self.imageArray count] != 0) {
for (UIImage *image in imageArray) {
NSLog(#"Image %#", image);
[self addImageToScreen:image];
}
}
NSLog(#"imageArray %#", imageArray);
NSLog(#"self.textField.text %#", self.textField.text );
NSLog(#"self.notesField.text %#", self.notesField.text);
}
-(void) addImageToScreen:(UIImage*) image;
{
int adjustHeight = 0;
int adjustWidth = 10;
int imagesInARow = 7;
int imageHeight = 75;
int imageWidth = 75;
int count = [self.imageArray count];
self.numberOfPhotosLabel.text = [NSString stringWithFormat:#"%i",count];
if (count > 1 && count < imagesInARow) {
adjustHeight = 0;
adjustWidth = (20 * [self.imageArray indexOfObject:image]) + ([self.imageArray indexOfObject:image] * imageWidth);
}
UIButton* container = [[UIButton alloc] init ];
CGRect frame = CGRectMake(adjustWidth, 5, imageWidth, imageHeight);
[container setTag:[self.imageArray indexOfObject:image]];
[container setImage:image forState:UIControlStateNormal];
[container setFrame:frame];
[container addTarget:self action:#selector(displayFullScreenImage:) forControlEvents:UIControlEventAllTouchEvents];
UIStackView *photosView = [[UIStackView alloc] initWithFrame:frame];
container.frame = photosView.bounds;
[photosView addSubview:container];
[self.view addSubview:photosView];
}
Use addArrangedSubview of UIStackView instead of addSubview.
And you should add all the Buttons to the same UIStackView. So, add a property for the UIStackView to your class, initialize it in viewDidLoad (or in the storyboard) and add the buttons to that stack view in addImageToScreen.

Scrolling images by dragging UISlider

Iam beginner in IOS Dev. asking about a way to make UIScrollView images scrolls by dragging UISlider . This is my case :
Required case :
when dragging slider , images scrolls on UIScrollView . also when scrolling images , slider changes its value accordingly .
Actual case :
when scrolling images , slider changes its value accordingly BUT when dragging slider images DONOT Scroll .
Here is my code , I wish any one tell me how to scrolling when slider drags .
Slider IBAction
- (IBAction)sliding:(UISlider *)sender{
int slider_value = (int)slider.value;
NSString *current_page = [[NSString alloc] initWithFormat:#"%i",slider_value];
current_page_lbl.text = current_page ;
[self loadPage:slider_value];
[self loadVisiblePages];
}
methods for UIScrollview :
- (void)loadPage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
return;
}
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView == [NSNull null]) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
UIImageView *newPageView = [[UIImageView alloc] initWithImage:[self.pageImages objectAtIndex:page]];
newPageView.contentMode = UIViewContentModeScaleAspectFit;
newPageView.frame = frame;
[self.scrollView addSubview:newPageView];
[self.pageViews replaceObjectAtIndex:page withObject:newPageView];
}
}
- (void)purgePage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
// If it's outside the range of what you have to display, then do nothing
return;
}
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView != [NSNull null]) {
[pageView removeFromSuperview];
[self.pageViews replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
To loadVisiblePage
- (void)loadVisiblePages {
CGFloat pageWidth = self.scrollView.frame.size.width;
NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
NSLog(#"page loaded is %d",page);
self.pageControl.currentPage = page;
NSInteger firstPage = page - 1;
NSInteger lastPage = page + 1;
for (NSInteger i=0; i<firstPage; i++) {
[self purgePage:i];
}
for (NSInteger i=firstPage; i<=lastPage; i++) {
[self loadPage:i];
slider.value = 21-i ;
int slider_value = (int)slider.value;
NSString *current_page = [[NSString alloc] initWithFormat:#"%i",21-slider_value];
current_page_lbl.text = current_page ;
}
for (NSInteger i=lastPage+1; i<self.pageImages.count; i++) {
[self purgePage:i];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self loadVisiblePages];
}
uislider is not suitable to use with uiscrollview....for this purpose there is one controll available name UIPagecontrol..
following link is the nice example with code which direct you to how to use it with UIScollview
UIPagecontroll with UIscollview with code Click Here

NSMutableArray in Class Method

I've created a class method that returns a random position as an NSValue object.
I want to store the returned positions in a mutable array so the next time the class method gets called it checks the positions in the mutable array and if it finds one that is too close to the one stored it generates a new one.
What I can't seem to figure out is how to allocate and initialize my mutable array to use with my class method. I've tried allocating it in the class method. but that won't work because it will get initialized on each call therefor the previously stored values are... erased?
then I want to use the generated position to place a number of circles on the view (without them overlapping). right now the randomly generated positions work, but they overlap because of the mutable array not being implemented correctly in my randomposition class.
I'm still a beginner in objective c...and programming in general...
#implementation randomPosition
NSMutableArray *positionsArrayPlayer1;
+(NSValue *) givePosition:(int)player forRadius: (CGFloat) radius
{
if (player == 1)
{
//set range for arc4random
int fromNumberY = 550;
int toNumberY = 950;
//set range for arc4random
int fromNumberX = 0;
int toNumberX = 700;
BOOL far_enough_away = NO;
CGPoint newpoint;
while(!far_enough_away)
{
newpoint = CGPointMake((arc4random()%(toNumberX-fromNumberX+1))+fromNumberX,
(arc4random()%(toNumberY-fromNumberY+1))+fromNumberY);
far_enough_away = YES;
for(NSValue *existing in positionsArrayPlayer1)
{
NSLog(#"test");
CGPoint pointb = [existing CGPointValue];
CGFloat deltay = pointb.y-newpoint.y;
CGFloat deltax = pointb.x-newpoint.x;
CGFloat distance = sqrt(pow(deltax,2) + pow(deltay,2));
//fail if closer than desired radius
if(distance < radius )
{
far_enough_away = NO;
NSLog(#"test");
break;
}
[positionsArrayPlayer1 addObject:[NSValue valueWithCGPoint:newpoint]];
}
NSLog(#"%#",positionsArrayPlayer1);
}
return [NSValue valueWithCGPoint:newpoint];
} else if (player == 2 ){
//more stuff to come here....
} else {
NSLog(#"player invalid");
}
return nil;
}
here's the method I use in my viewcontroller. I also store the circle object that is placed on the view in a separate array for other manipulations.
- (void) positionCirclesPlayer1
{
radius = 70;
CGRect position;
for (int i = 0; i < [colors count];i++)
{
CGRect positionCircleInCenter = CGRectMake(self.view.frame.size.width/2-35, self.view.frame.size.height/2-35, radius, radius);
Circle *myCircle = [[Circle alloc] initWithFrame:positionCircleInCenter radius:radius color:[colors objectAtIndex:i]];
myCircle.label.text = [NSString stringWithFormat:#"%i", i];
myCircle.alpha = 0;
myCircle.userInteractionEnabled = NO;
theNewPointPlayer1 = [randomPosition givePosition:1 forRadius:radius];
position = CGRectMake(theNewPointPlayer1.CGPointValue.x, theNewPointPlayer1.CGPointValue.y, 70, 70);
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
myCircle.frame = position; myCircle.alpha = 1;} completion:nil];
[playerOneCircles addObject:myCircle];
[self.view addSubview:myCircle];
}
}
Before learning Obj-C, you should probably learn something about C :)
+(NSValue*)givePosition:(int)player forRadius:(CGFloat)radius {
//this is executed when the method is called for the first time
static NSMutableArray *positionsArrayPlayer1 = nil;
if (positionsArrayPlayer1 == nil) { //checking if this is the first time execution
positionsArrayPlayer1 = [[NSMutableArray alloc] init];
}
}
or
//global variable accessible only from this file
static NSMutableArray *positionsArrayPlayer1;
#implementation randomPosition
//this method is called when the class is loaded.
+(void)initialize {
positionsArrayPlayer1 = [[NSMutableArray alloc] init];
}
+(NSValue*)givePosition:(int)player forRadius:(CGFloat)radius {
//do something with the array
}
#end
Note that implementing this functionality using class methods is probably not a very good architecture.

Changing the tags during do-while loop

So i got a method to loop through an XML document. The document got a number of answervalues which are being extracted and are printed on the screen in the form of UISegmentedControls.
The code i got right now is:
- (void) traverseElement:(TBXMLElement *)element {
static CGFloat y = 300.0f;
static CGFloat dynamicHeight = 400.0f;
static int segmentId = 1;
do {
if([[TBXML elementName:element] isEqualToString:#"wcqAnswerValues"]) {
NSString *segmentItemsStr = [TBXML textForElement:element];
NSArray *segmentItemsArray = [segmentItemsStr componentsSeparatedByString:#";"];
answer = [[UISegmentedControl alloc] initWithItems:segmentItemsArray];
answer.frame = CGRectMake(50, y, 400, 40);
[answer addTarget:self action:#selector(textpopup:) forControlEvents:UIControlEventValueChanged];
answer.segmentedControlStyle = UISegmentedControlStyleBar;
answer.tag = segmentId;
[scrollView addSubview:answer];
scrollView.contentSize = CGSizeMake(768, dynamicHeight);
[formulierText removeFromSuperview];
[answer release];
y += 40.0f;
dynamicHeight += 40.0f;
segmentId++;
}
if ([[TBXML elementName:element] isEqualToString:#"formdata"]) {
y = 300.0f;
dynamicHeight = 400.0f;
segmentId = 1;
}
// if the element has child elements, process them
if (element->firstChild)
[self traverseElement:element->firstChild];
// Obtain next sibling element
} while ((element = element->nextSibling));
}
-(void)textpopup:(UISegmentedControl *)sender {
if (sender.tag == 1) {
// if the tag of the sender is 1, then do something.
}
}
What i want to achieve is this:
Each UISegmentedControl that is being printed on the screen has to have a unique number or id so i can separately assign actions to them. What is the best way to achieve this?
EDIT:
Fixed it. I will update my code.

How to create a CGGradient for my UIView subclass?

Does anyone know how to create a CGGradient that will fill my view,
my current code is this and it fill the UIView with a red rectangle I want to have a gradient (from black to grey for instance) instead of the rect :
- (void)drawRect:(CGRect)rect {
CGContextRef context=UIGraphicsGetCurrentContext();
CGRect r;
r.origin.x=0.;
r.origin.y=0.;
r.size.width=rect.size.width;
r.size.height=rect.size.height;
CGContextSetRGBFillColor(context, 1., 0., 0., 1.);
CGContextFillRect (context,r);
}
In my answer to this question, I provide code for drawing a gloss gradient within a UIView. The colors and drawing positions can be modified from that to form whatever linear gradient you need.
this is a subclass where you can chose the colors
.h file
#import <UIKit/UIKit.h>
#interface GradientView : UIView {
CGGradientRef gradient;
}
#property(nonatomic, assign) CGGradientRef gradient;
- (id)initWithGradient:(CGGradientRef)gradient;
- (id)initWithColor:(UIColor*)top bottom:(UIColor*)bot;
- (void)setGradientWithColor:(UIColor*)top bottom:(UIColor*)bot;
- (void)getRGBA:(CGFloat*)buffer;
#end
.m file
#import "GradientView.h"
#implementation GradientView
#synthesize gradient;
- (id)initWithGradient:(CGGradientRef)grad {
self = [super init];
if(self){
[self setGradient:grad];
}
return self;
}
- (id)initWithColor:(UIColor*)top bottom:(UIColor*)bot {
self = [super init];
if(self){
[self setGradientWithColor:top bottom:bot];
}
return self;
}
- (void)setGradient:(CGGradientRef)g {
if(gradient != NULL && g != gradient){
CGGradientRelease(gradient);
}
if(g != gradient){
CGGradientRetain(g);
}
gradient = g;
[self setNeedsDisplay];
}
- (void)setGradientWithColor:(UIColor*)top bottom:(UIColor*)bot {
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat clr[8];
[top getRGBA:clr];
[bot getRGBA:clr+4] ;
CGGradientRef grad = CGGradientCreateWithColorComponents(rgb, clr, NULL, sizeof(clr)/(sizeof(clr[0])*4));
[self setGradient:grad];
CGColorSpaceRelease(rgb);
CGGradientRelease(grad);
}
- (void)getRGBA:(CGFloat*)buffer {
CGColorRef clr = [self CGColor];
NSInteger n = CGColorGetNumberOfComponents(clr);
const CGFloat *colors = CGColorGetComponents(clr);
// TODO: add other ColorSpaces support
switch (n) {
case 2:
for(int i = 0; i<3; ++i){
buffer[i] = colors[0];
}
buffer[3] = CGColorGetAlpha(clr);
break;
case 3:
for(int i = 0; i<3; ++i){
buffer[i] = colors[i];
}
buffer[3] = 1.0;
break;
case 4:
for(int i = 0; i<4; ++i){
buffer[i] = colors[i];
}
break;
default:
break;
}
}
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextDrawLinearGradient(c, gradient, CGPointMake(0, 0), CGPointMake(0, rect.size.height), 0);
}
- (void)dealloc {
CGGradientRelease(gradient);
[super dealloc];
}
#end
Use CGGradientCreateWithColorComponents or CGGradientCreateWithColors to create the gradient. (The latter takes CGColor objects.) Then, use CGContextDrawLinearGradient or CGContextDrawRadialGradient to draw it.
A linear gradient will extend infinitely in at least the two directions perpendicular to the line of the gradient. A radial gradient extends infinitely in every direction. To prevent the gradient from spilling outside your view, you'll probably need to add the view's bounds to the clipping path using CGContextClipToRect.