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
Related
I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?
Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH
AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}
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 have a View-Based NSTableView which should be initially empty. I also have "add" and "remove" buttons for adding and deleting rows from same NSTableView.
My delegate methods and method for adding new row look like this:
#import "PreferencesApiKeysViewController.h"
#import "UserKeys.h"
#interface PreferencesApiKeysViewController ()
#property (weak) IBOutlet NSTableView *keysTableView;
#end
#implementation PreferencesApiKeysViewController
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if([tableColumn.identifier isEqualToString:#"userKeysColumn"]) {
UserKeys *allKeys = [self.allKeys objectAtIndex:row];
cellView.textField.stringValue = allKeys.userKeyName;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"Initial rows: %li", (unsigned long)[self.allKeys count]);
return [self.allKeys count];
}
- (IBAction)addKey:(id)sender {
UserKeys *newKey = [[UserKeys alloc] initWithKeyName:#""
apiID:#""
apiCode:#"" ];
[self.allKeys addObject:newKey];
NSLog(#"Total rows: %li", (unsigned long)self.allKeys.count);
NSInteger newRowIndex = self.allKeys.count;
if (newRowIndex == 0) {
NSLog(#"No rows.");
newRowIndex = 0;
} else {
NSLog(#"Has rows.");
newRowIndex = self.allKeys.count-1;
}
NSLog(#"New Index: %ld", (long)newRowIndex);
[self.keysTableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] withAnimation:NSTableViewAnimationSlideDown];
[self.keysTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:newRowIndex] byExtendingSelection:NO];
[self.keysTableView scrollRowToVisible:newRowIndex];
}
#end
and in my AppDelegate.m, I'm calling my view like this:
#import "AppDelegate.h"
#import "UserKeys.h"
#include "PreferencesApiKeysViewController.h"
#interface AppDelegate()
#property (nonatomic,strong) IBOutlet PreferencesApiKeysViewController *prefsViewController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.prefsViewController = [[PreferencesApiKeysViewController alloc] initWithNibName:#"PreferencesApiKeysViewController" bundle:nil];
// Create few rows as dummy data
/*
UserKeys *key1 = [[UserKeys alloc] initWithKeyName:#"key one"
apiID:#"123"
apiCode:#"xxx" ];
UserKeys *key2 = [[UserKeys alloc] initWithKeyName:#"key two"
apiID:#"456"
apiCode:#"yyy" ];
NSMutableArray *tempKeys = [NSMutableArray arrayWithObjects:key1, key2, nil];
self.prefsViewController.allKeys = tempKeys;
*/
// done.
[self.window.contentView addSubview:self.prefsViewController.view];
self.prefsViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
#end
Now, the thing here is that if I uncomment those few lines for adding dummy data in AppDelegate and launch my app, everything works fine. I can add/remove rows without any problem, I can even delete all of them and add a new one after that.
But, if I comment those lines again, my app starts with an empty table (which is what I need), and when I want to add new row I get an error:
*** Assertion failure in -[NSTextFieldCell _objectValue:forString:errorDescription:], /SourceCache/AppKit/AppKit-1187.34/AppKit.subproj/NSCell.m
Looking further the thread, I see only one line referencing to my app:
0x0000000100003116 -[PreferencesApiKeysViewController tableView:viewForTableColumn:row:] + 406
I'm guessing that my TableView is not properly instantiated if allKeys array doesn't contain any single object really, but I'm not sure how to fix that? How to create an empty NSTableView and have the ability to add the first row by myself when "add" button is clicked (without adding any dummy data to allKeys)?
Add this to the implementation of PreferencesApiKeysViewController:
#implementation PreferencesApiKeysViewController
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
if ((self = [super initWithNibName:nibName bundle:bundle])) {
self.allKeys = [NSMutableArray array];
}
return self;
}
// add the following only if not using ARC
- (void)dealloc {
[_allKeys release];
[super dealloc];
}
#end
What's happening when you allow that commented-out code to be compiled in is that you assign an NSMutableArray instance to the allKeys instance variable. As a result, adding and removing items works as you would expect.
Generally, in a case like this, the class that manages the NSMutableArray should override the init method to make sure the array is properly initialized to an empty array. (By default, non-IBOutlet instance variables are initialized to nil, which won't allow you to properly add or remove items from it).
i need to build an application that define an array that should be made of image items.
every image iten has an image, a name and a photographer name.
i build my image item class and i want you to check if my define is correct and good(i just start to learn objective c).
i want you to emphasize on the set's methods.
here is the photoitem.h:
#import <Foundation/Foundation.h>
#interface photoItem : NSObject
{
UIImage *imageView;
NSString *photoNameLabel;
NSString *photographerNameLabel;
UIButton *viewPhoto;
}
#property(readonly) NSString *name;
#property(readonly) NSString *nameOfPhotographer;
#property(readonly) UIImage *imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer: (NSString*)photographerName;
#end
here is my photoitem.m:
#import "photoItem.h"
#implementation photoItem
#synthesize name;
#synthesize nameOfPhotographer;
#synthesize imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName
{
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
return self;
}
-(void) setName:(NSString *)name
{
photoNameLabel = name;
}
-(void) setNameOfPhotographer:(NSString *)nameOfPhotographer
{
photographerNameLabel = nameOfPhotographer;
}
-(void)setImageItem:(UIImage *)imageItem
{
imageView = imageItem;
}
#end
i hope you could fix my errors(if there are some).
thanks.
Two problems come to mind:
1) -(id)makePhotoItemWIthPhoto:name:photographer: might be better as -(id)initWithPhoto:name:photographer:. Otherwise the caller needs to alloc and init an object first so that self is valid, then call your method. At that point, the return of self doesn't make sense.
Example:
-(idinitWithPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName {
self = [super init];
if (self) {
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
}
return self;
}
2) The three readonly properties don't seem to have any purpose since they have no connection to the variables that you initialize in the makePhotoItemWIthPhoto: method.
I'm new to objective-c and I'm finding that I don't know how to correctly assert that a text property on some given label is equal to a raw string value. I'm not sure if I just need to cast the label as NSString or if I need to modify my assert statement directly.
#interface MoreTest : SenTestCase {
MagiczzTestingViewController* controller;
}
- (void) testObj;
#end
#implementation MoreTest
- (void) setUp
{
controller = [[MagiczzTestingViewController alloc] init];
}
- (void) tearDown
{
[controller release];
}
- (void) testObj
{
controller.doMagic;
STAssertEquals(#"hehe", controller.label.text, #"should be hehe, was %d instead", valtxt);
}
#end
The implementation of my doMagic method is below
#interface MagiczzTestingViewController : UIViewController {
IBOutlet UILabel *label;
}
#property (nonatomic, retain) UILabel *label;
- (void) doMagic;
#end
#implementation MagiczzTestingViewController
#synthesize label;
- (void) doMagic
{
label.text = #"hehe";
}
- (void)dealloc {
[label release];
[super dealloc];
}
#end
The build is fine when I modify the assert to compare a raw NSString to another but when I try to capture the text value (assuming it's of type NSString) it fails. Any help would be much appreciated!
STAssertEquals() checks for identity of the two values provided, so it's equivalent to doing this:
STAssertTrue(#"hehe" == controller.label.text, ...);
Instead, you want STAssertEqualObjects(), which will actually run an isEqual: check like the following:
STAssertTrue([#"hehe" isEqual:controller.label.text], ...);
You need to load the nib of the view controller. Otherwise there won't be any objects for the label outlet to be hooked up to.
One way to do this is to add an ivar for the view controller's view to your test case:
#interface MoreTest : SenTestCase {
MagiczzTestingViewController *controller;
UIView *view;
}
#end
#implementation MoreTest
- (void)setUp
{
[super setUp];
controller = [[MagiczzTestingViewController alloc] init];
view = controller.view; // owned by controller
}
- (void)tearDown
{
view = nil; // owned by controller
[controller release];
[super tearDown];
}
- (void)testViewExists
{
STAssertNotNil(view,
#"The view controller should have an associated view.");
}
- (void)testObj
{
[controller doMagic];
STAssertEqualObjects(#"hehe", controller.label.text,
#"The label should contain the appropriate text after magic.");
}
#end
Note that you also need to invoke super's -setUp and -tearDown methods appropriately from within yours.
Finally, do not use dot syntax for method invocation, it is not a generic replacement for bracket syntax in message expressions. Use dot syntax only for getting and setting object state.