Each time I press a button, mainController is calling [self.view addSubview: createCustomView.view]. Everything works fine here. The problem is that I need to put a tag on each subview I create in order to retrieve them later. I've already tried this :
MainController.m
NSNumber *i;
createCustomView.view.tag = i; //readonly
And what I actually wanna do is :
int i;
[createCustomView.view setTag:i];
But the setTag method doesn't exist. My question is : Is there a way I can do this other than using identifier string which brings some problems in my case?
Thanks in advance
Here's the .h file of the Controller
#import <Foundation/Foundation.h>
#import "TransactionButtonView.h"
#class TransactionButtonController;
#interface TransactionViewController : NSViewController
{
TransactionButtonController *transactionButtonController;
}
-(IBAction)createOnPushButton:(id)sender;
-(void)recalculatePositionOnRemove:(long)tag;
#property (nonatomic,assign) TransactionButtonController *transactionButtonController;
#end
Here's the .m file of the Controller
#import "TransactionViewController.h"
#import "TransactionButtonController.h"
#import "MainController.h"
#implementation TransactionViewController
#synthesize transactionButtonController;
-(IBAction)createOnPushButton:(id)sender
{
transactionButtonController = [[TransactionButtonController alloc] initWithNibName:#"TransactionButton" bundle:nil];
NSPoint originPoint;
for (int i=1; i <= [[self.view subviews]count]; i++) {
originPoint.y = transactionButtonController.view.bounds.origin.y + self.view.bounds.size.height - transactionButtonController.view.bounds.size.height*i;
transactionButtonController.view.tag = i; // Here's the PROBLEM!!!
[[transactionButtonController view]setIdentifier:[[NSNumber numberWithInt:i]stringValue]]; //here's the not good option
}
originPoint.x = transactionButtonController.view.bounds.origin.x;
[[transactionButtonController view] setFrameOrigin:originPoint];
[self.view addSubview:transactionButtonController.view];
[transactionButtonController sendVarsToButton:#"xxx" :#"591" :5 :87456356472456];
}
-(void)recalculatePositionOnRemove:(long)tag
{
NSPoint originPoint;
for (long i = tag; i<[[self.view subviews]count]; i++) {
originPoint.y = transactionButtonController.view.bounds.origin.y +self.view.bounds.size.height - transactionButtonController.view.bounds.size.height*i;
originPoint.x = transactionButtonController.view.bounds.origin.x;
[[transactionButtonController.view viewWithTag:i+1] setFrameOrigin:originPoint];
}
}
#end
If you want to add a tag to a view do this:
theView.tag = 1;
To remove it:
[[myParentView viewWithTag:1] removeFromSuperview]
Related
I am developing a watchkit application and i have got a table view which must have functionality to delete rows. I saw a tutorial using content menu. But no idea how to delete row from the table. Please help me.
#import "HomeInterfaceController.h"
#import "HomeTableRowController.h"
#import "DetailHomeInterfaceController.h"
#interface HomeInterfaceController ()
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceTable *homeTable;
#property (nonatomic,strong) NSArray *nameArr;
#end
#implementation HomeInterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
self.nameArr = [NSArray arrayWithObjects: #"Jill Valentine", #"Peter Griffin", #"Meg Griffin", #"Jack Lolwut",
#"Mike Roflcoptor", #"Cindy Woods", #"Jessica Windmill", #"Alexander The Great",
#"Sarah Peterson", #"Scott Scottland", #"Geoff Fanta", #"Amanda Pope", #"Michael Meyers",
#"Richard Biggus", #"Montey Python", #"Mike Wut", #"Fake Person", #"Chair",
nil];
[self setupTable];
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
- (void)setupTable {
[self.homeTable setNumberOfRows:[self.nameArr count] withRowType:#"HomeTableRowController"];
for (NSInteger i = 0; i < self.nameArr.count; i++)
{
HomeTableRowController *row = [self.homeTable rowControllerAtIndex:i];
NSString *thisBook = [self.nameArr objectAtIndex:i];
[row.reminderHeadlineLabel setText:thisBook];
// [row.imageRow setImage:[UIImage imageNamed:#"abc.jpg"]];
}
}
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
{
NSDictionary *d=[NSDictionary dictionaryWithObject:#"hi" forKey:#"nm"];
[self presentControllerWithName:#"DetailHome" context:d];
}
- (IBAction)deleteButtonAction {
}
#end
You need to call removeRowsAtIndexes in your deleteButtonAction function:
// first create an index set with the index of the row you want to delete
NSIndexSet *indexes = [[NSIndexSet alloc] initWithIndex:index];
// then call the function
[self.homeTable removeRowsAtIndexes:indexes];
There is more info in the docs
Another newbie question. I am trying to understand how to use scope effectively to organise my projects using classes to hold the data instead of having everything on the view controller. So, I am working on a couple of versions of a simple project to understand how scope works.
I have a view controller hooked to a view. In that view there are buttons that when clicked show images. I want to add another button that randomizes the images. I also have a class called "Cards" to hold the cards and the methods for creating and shuffling the cards. I have duplicated the project, so I have one that works and one that doesn't.
First project. These are the files:
view controller h file:
#import <UIKit/UIKit.h>
#import "Cards.h"
#interface ViewController : UIViewController
- (IBAction)buttonPressed:(id)sender;
#end
view contoller m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Cards *instance = [[Cards alloc] init];
instance.images = [instance createImages];
NSLog(#"I've got %lu Images", (unsigned long)instance.images.count);
instance.shuffled = [instance shuffleImages];
NSLog(#"Image numbers shuffled: %#", instance.shuffled);
}
- (IBAction)buttonPressed:(id)sender {
//Nothing hooked to this yet
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Cards h file:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface Cards : NSObject
// Creating Images
#property NSMutableArray *images;
- (NSMutableArray*) createImages;
//Shuffling Images
#property NSMutableArray *shuffled;
- (NSMutableArray*) shuffleImages;
#end
Cards m file:
#import "Cards.h"
#implementation Cards
- (NSMutableArray*) createImages{
self.images = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Image1.png"],
[UIImage imageNamed:#"Image2.png"],
[UIImage imageNamed:#"Image3.png"],
[UIImage imageNamed:#"Image4.png"], nil];
return self.images;
}
- (NSMutableArray*) shuffleImages{
NSUInteger imageCount = [self.images count];
NSMutableArray *localvar = [[NSMutableArray alloc]init];
for (int tileID = 0; tileID < imageCount; tileID++){
[localvar addObject:[NSNumber
numberWithInt:tileID]];
}
for (NSUInteger i = 0; i < imageCount; ++i) {
NSInteger nElements = imageCount - i;
NSInteger n = (arc4random() % nElements) + i;
[localvar exchangeObjectAtIndex:i
withObjectAtIndex:n];
}
return localvar;
}
#end
This works and I get the expected output on the console:
2015-12-31 23:43:44.885 VCScope[2138:533369] I've got 4 Images
2015-12-31 23:43:44.886 VCScope[2138:533369] Image numbers shuffled: (
0,
2,
3,
1
)
Second project:
What I want to do, is put a button to randomize the images only when the button is pressed and not as part of viewDidLoad. So, in my second project, I have the same files for the view controller.h and for both the Cards.h and Cards.m, but on the view controller.m I move the calling of the method for the shuffling of the cards to a UIButton method, like so:
new View controller m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Cards *instance = [[Cards alloc] init];
instance.images = [instance createImages];
NSLog(#"I've got %lu Images", (unsigned long)instance.images.count);
}
- (IBAction)buttonPressed:(id)sender {
Cards *instance = [[Cards alloc] init];
instance.shuffled = [instance shuffleImages];
NSLog(#"Image numbers shuffled: %#", instance.shuffled);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This outputs to the console the following:
2015-12-31 23:32:07.495 4StackVCScope[2029:486608] I've got 4 Images
2015-12-31 23:32:11.924 4StackVCScope[2029:486608] Image numbers: (
)
So it's not working and I am guessing it's to do with scope. Can someone throw some light into this? thanks
Welcome to Stack Overflow. You mention you're a "newbie", but it would be helpful to know what background you have so I know how much detail is needed here.
Cards *instance = [[Cards alloc] init];
creates a fresh Cards instance in a local variable. You are doing this separately inside -viewDidLoad and in -buttonPressed:.
If you want one Cards object per ViewController, then the view controller needs to have per-instance storage for it. There are several possible ways to do this. Which one you pick is a question of code style and API design.
If the Cards instance is for internal use only, you can declare an ivar in your #implementation block:
#implementation ViewController {
Cards *_cards;
}
- (void)viewDidLoad { _cards = ... }
- (IBAction)buttonPressed:(id)sender { access _cards }
#end
(Ivars can be declared in the public #interface as well, but I wouldn't recommend that as it leaks implementation details.)
Or you can use a property in the public interface:
// in your .h file:
#interface ViewController
#property (nonatomic) Cards *cards;
#end
// in your #implementation:
- (void)viewDidLoad { self.cards = ... }
- (IBAction)buttonPressed:(id)sender { access self.cards }
A property can also be privately declared in a class extension:
// in your .m file:
#interface ViewController ()
#property (nonatomic) Cards *cards;
#end
I am trying to create a subclass of UICollectionViewLayout, but I cant call its original functionality such as for example itemSize and I get the error saying
Property 'itemSize' not found on object 'SubClass *'
What am I missing?
My code looks like this.
.h
#import <UIKit/UIKit.h>
#interface SubClass : UICollectionViewLayout
#end
.m
#import "SubClass.h"
#implementation SubClass
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *answer = [self layoutAttributesForElementsInRect:rect];
for(int i = 1; i < [answer count]; ++i) {
UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
CGFloat maximumSpacing = 20.0f;
CGFloat origin = CGRectGetMaxX(prevLayoutAttributes.frame);
if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
CGRect frame = currentLayoutAttributes.frame;
frame.origin.x = origin + maximumSpacing;
currentLayoutAttributes.frame = frame;
}
}
return answer;
}
#end
You inherited from the wrong class. The itemSize property is part of UICollectionViewFlowLayout, not UICollectionViewLayout.
Also, you probably want to call [super layoutAttributesForElementsInRect:rect]. Sending that message to self will cause a stack overflow due to unbounded recursion.
I have done the Collection View Programming Topics three times now. Step by step, but there is no way I am getting any content in the collection view.
The thing I have noted for the bindings of the NSText Labels when I bind them to representedObject. is that they get an exclamation mark.
I have tried to set model key paths, xcode identifier, and title to no avail.
I also get an extra view in addition to the collection view item.
I hope anybody has an idea.
I have asserted that the labels have fonts, and that the gridview has 0 rows and 1 column.
Here is AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property NSMutableArray *personModelArray;
#property (assign) IBOutlet NSWindow *window;
// To see if personModelArray is populated.
- (IBAction) showElements:(id) sender ;
#end
Here is AppDelegate.m
//
// AppDelegate.m
// Occupations
#import "AppDelegate.h"
#import "PersonModel.h"
#implementation AppDelegate
#synthesize personModelArray;
- (void)awakeFromNib {
PersonModel * pm1 = [[PersonModel alloc] init];
pm1.name = #"John Appleseed";
pm1.occupation = #"Doctor";
PersonModel * pm2 = [[PersonModel alloc] init];
pm2.name = #"Jane Carson";
pm2.occupation = #"Teacher";
PersonModel * pm3 = [[PersonModel alloc] init];
pm3.name = #"Ben Alexander";
pm3.occupation = #"Student";
NSMutableArray * tempArray = [NSMutableArray arrayWithObjects:pm1, pm2, pm3, nil];
[self setPersonModelArray:tempArray];
/*
for (PersonModel *obj in personModelArray) {
NSLog(#"%# %#", obj.name, obj.occupation) ;
}
*/
// NSLog(#"Awoken from nib %#",personModelArray) ;
}
// Convenience: hooked up with a button to see that
// the contents of the array is still there!
-(void)showElements:(id) sender {
for (PersonModel *obj in personModelArray) {
NSLog(#"%# %#", obj.name, obj.occupation) ;
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
-(void)insertObject:(PersonModel *)p inPersonModelArrayAtIndex:(NSUInteger)index {
[personModelArray insertObject:p atIndex:index];
}
-(void)removeObjectFromPersonModelArrayAtIndex:(NSUInteger)index {
[personModelArray removeObjectAtIndex:index];
}
-(void)setPersonModelArray:(NSMutableArray *)a {
personModelArray = a;
}
-(NSArray*)personModelArray {
return personModelArray;
}
#end
Here is PersonModel.h
//
// PersonModel.h
// Occupations
//
#import <Foundation/Foundation.h>
#interface PersonModel : NSObject {
NSString * name;
NSString * occupation;
}
#property NSString * name;
#property NSString * occupation;
#end
Here is PersonModel.m
//
// PersonModel.m
// Occupations
#import "PersonModel.h"
#implementation PersonModel
#synthesize name;
#synthesize occupation;
#end
The answer to my greatest blunder in 2013 was that I didn't completely understood how the control (NSContentsView) was supposed to be assembled.
I just stumbled into the tutorial from Cocoa Bindings Programming Topics, and just couldn't make it work. To my defense, first I fickled a little with the bindings, so I supsected the error to still be there somewhere, but nada. I had placed the controls I had bound into the contentsview, and not into the "gridview".
I'm attempting to make a basic game in cocos2d, and I've gotten to the point where I'm attempting to scroll the background depending on the hero sprite's position. The hero's created alongside the controls in a class called GameplayLayer, and all works fine for non-scrolling backgrounds.
The book I'm following has given me some sample code to get the scrolling working based on when my character passes the half-way point, which seems perfect, but it's not executing, I believe this is because it's creating another instance of my Hero class, and there's no link between that instance, and the one that's displayed onscreen.
I'm assuming that I can fix this by making the working instance of my hero accessible from within the background scrolling class (which is called YrrasCoastBackgroundLayer), but I'm having a brain-block and can't get it to see it. I've tried a #property, but Xcode just won't see it in the other class.
Here's the .h file from the GameplayLayer class, where the hero is created / hooked up to controls, etc.:
#import "CCLayer.h"
#import "cocos2d.h"
#import "CommonProtocols.h"
#import "SneakyJoystick.h"
#import "SneakyButton.h"
#import "SneakyJoystickSkinnedBase.h"
#import "SneakyButtonSkinnedBase.h"
#import "HeroMale.h"
#import "GameCharacter.h"
#interface GameplayLayer : CCLayer <GameplayLayerDelegate> {
CCSprite *playerSprite;
SneakyJoystick *playerJoystick;
SneakyButton *jumpButton;
CCSpriteBatchNode *sceneSpriteBatchNode;
HeroMale *heroMale;
}
#property (nonatomic, readonly) SneakyJoystick *playerJoystick;
#property (nonatomic, readonly) SneakyButton *jumpButton;
#property (nonatomic, assign) HeroMale *heroMale;
#end
The YrrasCoastBackgroundLayer.h file definitely imports GameplayLayer.h, and here's the contents of the method in YrrasCoastBackgroundLayer.m file which I want to be able to access that *heroMale ivar:
- (void) adjustLayer {
float heroXPosition = heroMale.position.x;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfOfTheScreen = screenSize.width / 2.0f;
CGSize levelSize = [[GameManager sharedGameManager] getDimensionsOfCurrentScene];
if ((heroXPosition > halfOfTheScreen) && (heroXPosition < (levelSize.width - halfOfTheScreen))) {
float newXPosition = halfOfTheScreen - heroXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
I'm getting an Xcode error on the float heroXPosition = heroMale.position.x line, stating that heroMale is an undeclared identifier. How can I make it usable here, and will that even solve the problem?
UPDATE:
Here's the .h file for YrrasCoastBackgroundLayer:
#import "CCLayer.h"
#import "cocos2d.h"
#import "HeroMale.h"
#import "GameplayLayer.h"
#interface YrrasCoastBackgroundLayer : CCLayer {
// Web Tutorial
CCParallaxNode *backgroundNode;
CCSprite *backgroundImage;
// Book Tutorial
CCSpriteBatchNode *sceneSpriteBatchNode;
CCParallaxNode *parallaxNode;
}
#property (nonatomic, assign) GameplayLayer *gameplayLayer;
#end
And here's the YrrasCoastBackgroundLayer.m:
#import "YrrasCoastBackgroundLayer.h"
#implementation YrrasCoastBackgroundLayer
#synthesize gameplayLayer;
- (id) init {
self = [super init];
if (self != nil) {
// Web Tutorial
backgroundNode = [CCParallaxNode node];
[self addChild:backgroundNode z: -1];
backgroundImage = [CCSprite spriteWithFile:#"yrras-coast-ipad-hd.png"];
CGPoint dustSpeed = ccp(0.1, 0.1);
[backgroundNode addChild:backgroundImage z:0 parallaxRatio:dustSpeed positionOffset:ccp(1024, [[CCDirector sharedDirector]winSize].height / 2)];
self.gameplayLayer = gameplayLayer;
[self scheduleUpdate];
}
return self;
}
- (void)update:(ccTime)deltaTime {
// Web Tutorial
// CGPoint backgroundScrollVel = ccp(-1000, 0);
// backgroundNode.position = ccpAdd(backgroundNode.position, ccpMult(backgroundScrollVel, deltaTime));
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
[self adjustLayer];
}
// Book Tutorial
- (void) adjustLayer {
float heroXPosition = gameplayLayer.heroMale.position.x;
CCLOG(#"heroXPosition is %f", gameplayLayer.heroMale.position.x);
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfOfTheScreen = screenSize.width / 2.0f;
CGSize levelSize = [[GameManager sharedGameManager] getDimensionsOfCurrentScene];
if ((heroXPosition > halfOfTheScreen) && (heroXPosition < (levelSize.width - halfOfTheScreen))) {
float newXPosition = halfOfTheScreen - heroXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
#end
You need an instance of your GameplayLayer to be passed to the YrrasCoastBackgroundLayer layer.
You can declare an assignable property in YrrasCoastBackgroundLayer :
#property (nonatomic, assign) GameplayLayer gamplayLayer;
and when you initialize your YrrasCoastBackgroundLayer pass the instance of the game play layer and in your adjustLayer method do :
float heroXPosition = gameplayLayer.heroMale.position.x;
EDIT :
assuming this is how you create your scene :
MyScene *scene = [MyScene scene];
gameplayLayer = [[GameplayLayer alloc] init];
bgLayer = [[YrrasCoastBackgroundLayer alloc] init];
bgLayer.gameplayLayer = gameplayLayer; // This is where you assign the gameplay Layer to the background layer;
[scene addChild:bgLayer];
[scene addChild:gameplayLayer];
// Release both layers if you dont need them anymore here (scene addChild retains them when they are added)
[gameplayLayer release];
[bgLayer release];
I am not sure how can you access heroMale as it is not member of YrrasCoastBackgroundLayer but of GameplayLayer. You would have to have object/instance of class GameplayLayer in order for you to access heroMale. That is the reason why you are getting undeclared identifier (heroMale is not property of class YrrasCoastBackgroundLayer).
Kind regards,
Bo