iOS - Accessing the name of IBOutlet - view

I am having several IBOutlets of UIView; several subviews within my parent view.
I am trying to loop over all the subviews which are UIView.
I want to loop over all subviews that have the name of their IBOutlet starting with "view_"
Can somebody help me with this?
Question is I really want to access the name of the IBOutlet.
Here's my code for clarification:
#property (weak, nonatomic) IBOutlet UIView *view_player1;
#property (weak, nonatomic) IBOutlet UIView *view_player2;
#property (weak, nonatomic) IBOutlet UIView *view_referee1;
- (void)listSubviewsOfView:(UIView *)view {
// Get the subviews of the view
NSArray *subviews = [view subviews];
// Return if there are no subviews
if ([subviews count] == 0) return;
for (UIView *subview in subviews) {
NSLog(#"my subview name is: %#", subview.class); //?? is only giving my the type eg UIView, i want it to return view_player2
// List the subviews of subview
[self listSubviewsOfView:subview];
}
}
How can I actually get the name of the IBOutlet of the subview and compare it if it only starts with the word "view_"
Thanks

Related

During my SKProductRequest my UIButtons and UILabels become nil. (XCODE 7)

First time using XCODE and Objective C. Having an issue with my UIButtons and UILabels becoming nil during my SKProductRequest method.
Below is my ShopViewController.m, where initially call my 3 UIButtons and my UILabel. In the viewDidLoad my UIButtons and Label have content.
My Shop page loads correctly but once the ProductsRequest method is called the UIButtons and UILabels become nil, not sure why. All the items are (strong, nonatomic) properties in the .h file and have been linked to the referencing Outlets in the ShopViewController. Any help is really appreciated.
ShopViewController.m below the product request is made with the GetProductInfo method. Which is called from my *homeViewController when I press the buy button on the main game viewController.
#import "ShopViewController.h"
#import "ViewController.h"
#interface ShopViewController ()
#property (strong, nonatomic) ViewController *homeViewController;
#end
#implementation ShopViewController
#synthesize fvProductTitle;
- (void)viewDidLoad {
[super viewDidLoad];
_fvBuyButton.enabled = YES;
_ddBuyButton.enabled = NO;
_bundleBuyButton.enabled = NO;
fvProductTitle.text = #"don't read this";
}
- (IBAction)buyFVProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_fvProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)buyDDProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_ddProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (IBAction)buyBundleProduct:(id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_bundleProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void) getProductInfo:(ViewController *) viewController {
_homeViewController = viewController;
if ([SKPaymentQueue canMakePayments])
{
SKProductsRequest *request = [[SKProductsRequest alloc]
initWithProductIdentifiers:
[NSSet setWithObjects:self.bundleProductID, self.ddProductID, self.fvProductID, nil]];
request.delegate = self;
[request start];
}
else{
_fvProductDescription.text = #"please enable app purchases";
}
}
#pragma mark -
#pragma mark SKProductsRequestDelegate
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
if (products.count != 0)
{
_fvProduct = products[2];
_fvBuyButton.enabled = YES;
fvProductTitle.text = _fvProduct.localizedTitle;
_ddProduct = products[1];
_ddBuyButton.enabled = YES;
_ddProductTitle.text = _ddProduct.localizedTitle;
_bundleProduct = products[0];
_bundleBuyButton.enabled = YES;
_bundleProductTitle.text = _bundleProduct.localizedTitle;
} else {
fvProductTitle.text = #"Full Version Product not found";
_ddProductTitle.text = #"Dirty Dozen Product not found";
_bundleProductTitle.text = #"Bundle Product not found";
}
products = response.invalidProductIdentifiers;
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self unlockFullVersion];
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Transaction Failed");
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
break;
default:
break;
}
}
}
Here is my ShopViewController.h to see properties I have set up.
#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
#interface ShopViewController : UIViewController <SKPaymentTransactionObserver, SKProductsRequestDelegate> {
}
#property (nonatomic) BOOL *isFullVersionAvailable;
// Store Kit
#property (strong, nonatomic) SKProduct *fvProduct;
#property (strong, nonatomic) SKProduct *ddProduct;
#property (strong, nonatomic) SKProduct *bundleProduct;
#property (strong, nonatomic) NSString *fvProductID;
#property (strong, nonatomic) NSString *ddProductID;
#property (strong, nonatomic) NSString *bundleProductID;
#property (strong, nonatomic) IBOutlet UIButton *fvBuyButton;
#property (strong, nonatomic) IBOutlet UIButton *ddBuyButton;
#property (strong, nonatomic) IBOutlet UIButton *bundleBuyButton;
#property (strong, nonatomic) IBOutlet UILabel *fvProductTitle;
#property (strong, nonatomic) IBOutlet UILabel *ddProductTitle;
#property (strong, nonatomic) IBOutlet UILabel *bundleProductTitle;
#property (strong, nonatomic) IBOutlet UITextView *fvProductDescription;
- (IBAction)buyFVProduct:(id)sender;
- (IBAction)buyDDProduct:(id)sender;
- (IBAction)buyBundleProduct:(id)sender;
- (void)getProductInfo:(UIViewController *)viewController;
#end
My ViewController.m below, my purchaseItem is what the user presses to transition to the shop page and calls the getProductInfo method.
- (IBAction)purchaseItem:(id)sender {
shopViewController.fvProductID = #"BurnsyBadges.FullVersion";
shopViewController.ddProductID = #"BurnsyBadges.DirtyDozen";
shopViewController.bundleProductID = #"BurnsyBadges.Bundle";
[shopViewController getProductInfo: self];
}
To Answer my own question. I had figured it out a few weeks ago and didn't get to update this. But it was a few things I had to correct in order to get multiple ViewControllers to access the Product Purchase Page correctly.
To get the one page working I needed to add a NavigationController to all my ViewControllers.

View Switching: Unrecognised selector sent to instance

I made some example that changes view by clicking button.
When clicking the "fist" button in view, I keep getting the error:
[__NSArrayM changeLogView:]: unrecognized selector sent to instance 0x100539e30
Here is the simplified code:
DSWAppDelegate.h
#import <Cocoa/Cocoa.h>
#class DSWHomeViewController;
#interface DSWAppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSButton *logBtn;
#property (weak) IBOutlet NSTabView *tabView;
#end
DSWAppDelegate.m
#import "DSWAppDelegate.h"
#import "DSWLogViewController.h"
#implementation DSWAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
[self.tabView setTabViewType:NSNoTabsNoBorder];
NSTabViewItem *item = nil;
DSWLogViewController *logvc = [[DSWLogViewController alloc initWithNibName:#"DSWLogViewController" bundle:[NSBundle mainBundle]];
item = [[NSTabViewItem alloc] initWithIdentifier:#"log"];
[item setView:logvc.view];
[self.tabView insertTabViewItem:item atIndex:0];
}
#end
DSWLogViewController.h
#import <Cocoa/Cocoa.h>
#interface DSWLogViewController : NSViewController
#property (weak) IBOutlet NSButton *firstBtn;
#property (weak) IBOutlet NSButton *secondBtn;
#property (weak) IBOutlet NSBox *box;
- (IBAction)changeLogView:(id)sender;
- (IBAction)testSelector:(id)sender;
#end
DSWLogViewController.m
#import "DSWLogViewController.h"
#implementation DSWLogViewController
- (IBAction)changeLogView:(id)sender
{
NSLog(#"changeLogView : %#", sender);
}
- (IBAction)testSelector:(id)sender
{
NSLog(#"testSelector : %#", sender);
}
#end
And I checked class name of file owner.
Something is sending a changeLogView: message to a mutable array instead of your view controller. Chances are, whatever you've connected the IBAction to is pointing to the wrong object - an array of some sort, rather than your view controller.

xCode Tableview set bounds

Trying to set the bounds of my table view to be smaller than my screen. I am setting it in my viewDidLoad method but nothing happens when I run. Not sure why it's not working. Am I calling it in the right place??
//OptionsViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.frame = CGRectMake(1,1,29,80);
}
// OptionsViewController.h
#import <UIKit/UIKit.h>
#interface OptionsViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
NSIndexPath *selectedRowIndex;
}
#property (weak, nonatomic) NSIndexPath *selectedRowIndex;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
Set the frame in viewDidAppear method . It works. You might want to look at the following question and answer for a good explanation :
Unable to set frame correctly before viewDidAppear
TL;DR - The UI elements are not drawn to screen yet during viewDidLoad .

Custom UIControls with Storyboard

I'm having trouble figuring this one out. I have a custom UIControl class set up to hold a UIImage and UILabel, and my UITableViewCell class holds two of these UIControls (leftProduct, & rightProduct)
#interface FeaturedProductControl : UIControl
#property (strong, nonatomic) IBOutlet UIImageView *featuredProductPhoto;
#property (strong, nonatomic) IBOutlet UILabel *featuredProductDescription;
#property (strong, nonatomic) Product *featuredProduct;
- (id)initWithProduct:(Product *)product;
#end
#interface FeaturedTableCell : UITableViewCell
#property (strong, nonatomic) IBOutlet FeaturedProductControl *leftProduct;
#property (strong, nonatomic) IBOutlet FeaturedProductControl *rightProduct;
#end
The images and labels are being filled in using the init method during cellForRowAtIndexPath, and they're coming through just fine. I have a target action associated with the UIControls in the Storyboard, but the productClicked: method doesn't seem to be called. I've tried changing it out to add the target action programmatically, no luck.
However, if I add an alloc/init to the code, the productClicked: method triggers properly, but unfortunately the UILabel and UIPhoto now come up empty onscreen. Since the UIControls are designed in Storyboard, I think I'm not supposed to do the alloc calls myself, but the TableViewController doesn't seem to like that it's not being called. I've tried calling alloc within [[FeaturedTableCell alloc] init], but it had no effect.
Contents of cellForRowAtIndexPath:
cellIdentifier = #"Featured Row";
FeaturedTableCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[FeaturedTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
FeaturedRow *featuredRowData = [self.productIndexModel objectAtIndexPath:indexPath]; // This contains the necessary products to fill the Row
Product *leftProduct = [featuredRowData.skuList objectAtIndex:0];
cell.leftProduct = [[FeaturedProductControl alloc] initWithProduct:leftProduct]; // Actions trigger, but no data
// cell.leftProduct = [cell.leftProduct initWithProduct:leftProduct]; // Data is filled in, but no action
// [cell.leftProduct addTarget:self action:#selector(productClicked:) forControlEvents:UIControlEventTouchUpInside]; // the storyboard mapping works without this line of code
if (featuredRowData.skuList.count > 1)
{
Product *rightProduct = [featuredRowData.skuList objectAtIndex:1];
cell.rightProduct = [cell.rightProduct initWithProduct:rightProduct];
// cell.rightProduct = [[FeaturedProductControl alloc] initWithProduct:rightProduct]; // Yes, these two are reversed from the left side code above for testing
[cell.rightProduct addTarget:self action:#selector(productClicked:) forControlEvents:UIControlEventTouchUpInside];
cell.rightProduct.hidden = NO; // right side column is hidden in case the number of products is odd
}
else
{
cell.rightProduct.hidden = YES;
}
[cell setNeedsLayout];
return cell;
Any idea what I'm doing wrong? I'm trying to keep as much of the initialization and setup as possible inside the storyboard, so I'd prefer not to go back to writing the whole UIControl programmatically.
Thanks everyone!
Figured it out. In my initWithProduct method, I included
self = [super init];
I commented out this line and the Touch Up Inside events started firing normally.

Cocoa class not displaying data in NSWindow

I have one class that controls one window, and another class that controls a different window in the same xib, however, the second window never displays what it should.
In the first class I alloc and init the second class, then pass some information to it. In the second class it displays that data in the table view.
Yes, in the .xib I have all the connections set up correctly, I've quadruple checked. Also the code is correct, same with the connections, I've quadruple checked.
Edit: and yes, there's data in the arrays, and the classes are NSObjects.
Edit2: I kinda found the problem. For some reason, the array is filled with contents, but it's returning 0 as a count.
Edit 9000:
Here's the code:
Answer.h
#import <Cocoa/Cocoa.h>
#interface MSAnswerView : NSObject {
IBOutlet NSWindow *window;
NSArray *User;
NSArray *Vote;
NSArray *Text;
IBOutlet NSTableView *view;
IBOutlet NSTableColumn *voteCount;
IBOutlet NSTableColumn *saidUser;
IBOutlet NSTextView *body;
}
-(void)setUpWithVoteCount:(NSArray *)array User:(NSArray *)user Text:(NSArray *)text;
#property (nonatomic, retain) NSWindow *window;
#property (nonatomic, retain) NSTableView *view;
#property (nonatomic, retain) NSTableColumn *voteCount;
#property (nonatomic, retain) NSTableColumn *saidUser;
#property (nonatomic, retain) NSTextView *body;
#end
.m
#import "MSAnswerView.h"
#implementation MSAnswerView
#synthesize view;
#synthesize voteCount;
#synthesize saidUser;
#synthesize body;
#synthesize window;
-(void)awakeFromNib
{
[view setTarget:self];
[view setDoubleAction:#selector(bodydata)];
[view reloadData];
}
-(void)setUpWithVoteCount:(NSArray *)array User:(NSArray *)user Text:(NSArray *)text
{
Vote = array;
User = user;
Text = text;
if (window.isVisible = YES) {
[view reloadData];
[view setNeedsDisplay];
}
}
-(int)numberOfRowsInTableView:(NSTableView *)aTable
{
return [User count];;
}
-(id)tableView:(NSTableView *)aTable objectValueForTableColumn:(NSTableColumn *)aCol row:(int)aRow
{
if (aCol == voteCount)
{
return [Vote objectAtIndex:aRow];
}
else if (aCol == saidUser)
{
return [User objectAtIndex:aRow];
}
else
{
return nil;
}
}
-(void)bodydata
{
int index = [view selectedRow];
[body setString:[Text objectAtIndex:index]];
}
#end
The problems in your code are numerous.
For one thing, this comparison in -setUpWithVoteCount:User:Text: is incorrect:
window.isVisible = YES
That should be the comparison operator, == not the assignment operator =.
Secondly, you are naming your ivars and methods incorrectly. Instance variables (in fact, variables of any type) should start with a lower-case letter. This is to distinguish them from class names. Check out the Apple coding guidelines.
I'd also suggest that a name like text is a bad name for a variable that stores a collection like an NSArray. Instead, you should name it something like textItems so it's clear that the variable represents a collection and not a single string.
Also, the class itself is poorly named. You have called it MSAnswerView but it's not a view, it's some type of window controller. At the very least call it MSAnswerWindowController. Better still would be to make it a subclass of NSWindowController and make it File's Owner in its own nib. This is the standard pattern for window controllers.
Your method -setUpWithVoteCount:User:Text: should really be an initializer:
- initWithVoteCount:user:text:
That way it's clear what it's for and that it should be called once at object creation time.
The main problem, however, is that you're not retaining the values that you pass in to your setup method. That means that if no other object retains a reference to them, they will go away at some indeterminate point in the future. If you access them at a later time, you will crash or at the very least receive bad data, which is what's occurring.
Of course, you must also add a -dealloc method in this case to ensure you release the objects when you're finished with them.
Putting all those suggestions together, your class should really look something like this:
MSAnswerWindowController.h:
#import <Cocoa/Cocoa.h>
//subclass of NSWindowController
#interface MSAnswerWindowController : NSWindowController <NSTableViewDataSource>
{
//renamed ivars
NSArray *users;
NSArray *voteCounts;
NSArray *textItems;
IBOutlet NSTableView *view;
IBOutlet NSTableColumn *voteCount;
IBOutlet NSTableColumn *saidUser;
IBOutlet NSTextView *body;
}
//this is now an init method
- (id)initWithVoteCounts:(NSArray *)someVoteCounts users:(NSArray *)someUsers textItems:(NSArray *)items;
//accessors for the ivars
#property (nonatomic, copy) NSArray* users;
#property (nonatomic, copy) NSArray* voteCounts;
#property (nonatomic, copy) NSArray* textItems;
#property (nonatomic, retain) NSWindow *window;
#property (nonatomic, retain) NSTableView *view;
#property (nonatomic, retain) NSTableColumn *voteCount;
#property (nonatomic, retain) NSTableColumn *saidUser;
#property (nonatomic, retain) NSTextView *body;
#end
MSAnswerWindowController.m:
#import "MSAnswerWindowController.h"
#implementation MSAnswerWindowController
//implement the init method
- (id)initWithVoteCounts:(NSArray*)someVoteCounts users:(NSArray*)someUsers textItems:(NSArray*)items
{
//this is an NSWindowController, so tell super to load the nib
self = [super initWithWindowNibName:#"MSAnswerWindow"];
if(self)
{
//copy all the arrays that are passed in
//this means we hold a strong reference to them
users = [someUsers copy];
voteCounts = [someVoteCounts copy];
textItems = [items copy];
}
return self;
}
//make sure we deallocate the object when done
- (void)dealloc
{
self.users = nil;
self.voteCounts = nil;
self.textItems = nil;
[super dealloc];
}
//this is called when the window first loads
//we do initial window setup here
- (void)windowDidLoad
{
[view setTarget:self];
[view setDataSource:self];
[view setDoubleAction:#selector(bodydata)];
}
//this is called when the view controller is asked to show its window
//we load the table here
- (IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[view reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTable
{
return [users count];
}
- (id)tableView:(NSTableView*)aTable objectValueForTableColumn:(NSTableColumn*)aCol row:(NSInteger)aRow
{
if (aCol == voteCount)
{
return [voteCounts objectAtIndex:aRow];
}
else if (aCol == saidUser)
{
return [users objectAtIndex:aRow];
}
return nil;
}
- (void)bodydata
{
NSInteger index = [view selectedRow];
[body setString:[textItems objectAtIndex:index]];
}
#synthesize users;
#synthesize voteCounts;
#synthesize textItems;
#synthesize view;
#synthesize voteCount;
#synthesize saidUser;
#synthesize body;
#end

Resources