in my ios app, i have a view with several cells called PersonalDetailTVC. After selecting a value on another view the app returns to PersonalDetailTVC, and I want that the background color of certain cell changes depending on the new value, but the colour only changes when I return to the view again. Can you help me, please?
#import "PersonDetailTVC.h"
#implementation PersonDetailTVC
#synthesize delegate;
#synthesize person = _person;
#synthesize selectedRole;
#synthesize personFirstnameTextField = _personFirstnameTextField;
#synthesize personSurnameTextField = _personSurnameTextField;
#synthesize personRoleTableViewCell = _personRoleTableViewCell;
#synthesize groupColorTableViewCell = _groupColorTableViewCell;
- (void)viewDidLoad
{
NSLog(#"Setting the value of fields in this static table to that of the passed Person");
//self.personNameTextField.text = self.person.name;
self.personFirstnameTextField.text = self.person.firstname;
self.personSurnameTextField.text = self.person.surname;
self.personRoleTableViewCell.textLabel.text = self.person.inRole.name;
self.groupColorTableViewCell.textLabel.text = self.person.hasColor.color;
self.selectedRole = self.person.inRole; // ensure null role doesn't get saved.
if ([self.person.hasColor.color isEqual:#"Grey"]){
self.groupColorTableViewCell.backgroundColor = [UIColor colorWithRed:21.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1];
}
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
[tgr setCancelsTouchesInView:NO];
[self.tableView addGestureRecognizer:tgr];
[super viewDidLoad];
}
- (void)viewDidUnload
{
//[self setPersonNameTextField:nil];
[self setPersonFirstnameTextField:nil];
[self setPersonSurnameTextField:nil];
[self setPersonRoleTableViewCell:nil];
[self setGroupColorTableViewCell:nil];
[super viewDidUnload];
}
- (IBAction)save:(id)sender
{
NSLog(#"Telling the PersonDetailTVC Delegate that Save was tapped on the PersonDetailTVC");
self.person.firstname = self.personFirstnameTextField.text; // Set Firstname
self.person.surname = self.personSurnameTextField.text; // Set Surname
[self.person setInRole:self.selectedRole];
[self.person setHasColor:self.selectedRole];// Set Relationship!!!
[self.person.managedObjectContext save:nil]; // write to database
[self.delegate theSaveButtonOnThePersonDetailTVCWasTapped:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender // !
{
if ([segue.identifier isEqualToString:#"Person Role Segue"])
{
NSLog(#"Setting PersonDetailTVC as a delegate of PersonRoleTVC");
PersonRoleTVC *personRoleTVC = segue.destinationViewController;
personRoleTVC.delegate = self;
personRoleTVC.selectedPerson = self.person;
}
else {
NSLog(#"Unidentified Segue Attempted!");
}
}
- (void)dismissKeyboard {
[self.view endEditing:TRUE];
}
- (void)roleWasSelectedOnPersonRoleTVC:(PersonRoleTVC *)controller
{
self.personRoleTableViewCell.textLabel.text = controller.selectedRole.name;
self.groupColorTableViewCell.textLabel.text = controller.selectedRole.color;
self.selectedRole = controller.selectedRole;
NSLog(#"PersonDetailTVC reports that the %# role was selected on the PersonRoleTVC", controller.selectedRole.name);
[controller.navigationController popViewControllerAnimated:YES];
}
#end
As far as i can see, you're not changing a color anywhere in the provided code. While reading the code, i noticed a few things, so i thought i share it with you, maybe it helps:
If I understand correctly you are using a UITableViewCell without a UITableView. This is actually not a problem as UITableViewCell derives from UIView, but the cell will not have the behaviour of a UITableViewCell (because this is usually controlled by a UITableView). When adding a UITableViewCell manually as a subview, the cell will have the default behaviour of a UIView.
In your save: method I see the following 2 lines:
[self.person setInRole:self.selectedRole];
[self.person setHasColor:self.selectedRole];// Set Relationship!!!
Are you intentionally passing self.selectedRole to a property hasColor (or setHasColor: method)? Since I don't know the types of these objects this might be OK, but looks like you should set a color there.
In the roleWasSelectedOnPersonRoleTVC: method your are currently setting the text property twice:
self.personRoleTableViewCell.textLabel.text = controller.selectedRole.name;
self.groupColorTableViewCell.textLabel.text = controller.selectedRole.color;
Shouldn't that last line be something like this?
self.groupColorTableViewCell.textLabel.textColor = controller.selectedRole.color;
Related
Im using an nsnumber as a counter but its not retaining its value. It may be a pointer issue because its showing up as 13 in my init statement but its null in my tap. I'm not sure where or why this is happening. I've never really messed with the automatically generated _instanceValue for my properties. I've always used self.propertyName as an accessor. I believe this may have something to do with it.
header file
#interface PezStoryViewer : UIViewController
#property(nonatomic, strong) NSNumber *counter;
-(id)initWithScene:(NSArray *)scenes;
+(void)viewTapped:(UIGestureRecognizer *)gesture;
+ (id)sharedInstance;
#end
m file
-(id)initWithScene:(NSArray *)scenes{
self = [super init];
if (self) {
//reverse array order
scenes = [[scenes reverseObjectEnumerator] allObjects];
//set counter
_counter = [[NSNumber alloc] initWithUnsignedInteger:[scenes count]];
NSLog(#"_counter = %#", _counter);
//initialize subviews
//and other stuff
}
return self;
}
-(void)viewTapped:(UIGestureRecognizer *)gesture{
NSLog(#"tap");
NSLog(#"_counter = %#", _counter);
NSUInteger i = [_counter unsignedIntegerValue];
i--;
NSLog(#"counter = %d", i);
if ([gesture.view isKindOfClass:[PezFrontImageView class]]) {
//3 Steps
//1: pop off subview
[gesture.view removeFromSuperview];
//2: if next view is a video, play it
//3: initialize new frontView under the existing backView
}
if (i==7) {
[self dismissViewControllerAnimated:NO completion:nil];
}
_counter = [NSNumber numberWithUnsignedInt:i];
}
+ (id)sharedInstance {
static id sharedInstance;
#synchronized(self) {
if (!sharedInstance)
sharedInstance = [[PezStoryViewer alloc] init];
return sharedInstance;
}
}
tap gesture on my subview that is of type UIImageView custom class
//add tapGesture
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:[PezStoryViewer sharedInstance] action:#selector(viewTapped:)];
[self addGestureRecognizer:tapGesture];
Please note that your sharedInstance method does not call initWithScene:. It just calls init. So you never initialize the counter property.
You really shouldn't be setting up your view controller as a singleton. Just create the view controller when needed and use the initWithScene: method.
I have a UITableView inside of an UITabBarController. Now, I am already checking to see if a user is logged in, and if they are not logged in I display a Modal View Controller to have them log in or sign up. But here's my problem, when the user hit's cancel, the user goes back to the original position of the table view. I can't have users see the rows in table view if they are not signed in. If a user is not logged in, I want a message exactly how the examples Apple gives in the iTunes store and the "Popular near me" examples in the Apple Human Interface Guideline:
https://developer.apple.com/library/iOS/documentation/userexperience/conceptual/mobilehig/StartingStopping.html#//apple_ref/doc/uid/TP40006556-CH52-SW1
So how would I display a message like that in a tableview controller? Keep in mind, I will always have data for that table view controller. Hopefully I am clear for everyone. Would I just bring the tableview background to the front of the tableview rows?
// ATableViewController embedded in a NavigationController with UITabBar
- (void)viewWillAppear:(BOOL)animated
{
// NSLog(#"dateViewDidLoad %f", [[NSDate date] timeIntervalSince1970]);
[super viewWillAppear:animated];
NSUserDefaults *textDef = [NSUserDefaults standardUserDefaults];
NSString *userName = [textDef stringForKey:#"userName"];
if (userName == nil) {
SignUpViewController *signup = [[SignUpViewController alloc]initWithNibName:#"SignUpViewController" bundle:nil];
[signup setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:signup animated:YES completion:nil];
// Display message you need to sign in to view this content
} else {
// Proceed and display the rows
}
}
If you use a UITableViewController you could "abuse" tableHeaderView of the tableView, by resizing it to full screen and disabling the dataSource methods and scrolling.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (isLoggedIn) {
self.tableView.tableHeaderView = nil;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.scrollEnabled = YES;
[self.tableView reloadData];
}
else {
if (!self.tableView.tableHeaderView) {
UIView *viewToDisplayIfNotLoggedIn = [[UIView alloc] initWithFrame:self.view.bounds];
viewToDisplayIfNotLoggedIn.backgroundColor = [UIColor redColor];
self.tableView.tableHeaderView = viewToDisplayIfNotLoggedIn;
}
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
self.tableView.scrollEnabled = NO;
[self.tableView reloadData];
}
}
If you are using a normal UIViewController it's even easier than that, just hide the tableView and show another UIView:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (isLoggedIn) {
self.tableView.hidden = NO;
self.viewToDisplayIfNotLoggedIn.hidden = YES;
}
else {
self.tableView.hidden = YES;
if (!self.viewToDisplayIfNotLoggedIn) {
self.viewToDisplayIfNotLoggedIn = [[UIView alloc] initWithFrame:self.view.bounds];
self.viewToDisplayIfNotLoggedIn.backgroundColor = [UIColor redColor];
[self.view addSubview:self.viewToDisplayIfNotLoggedIn];
}
self.viewToDisplayIfNotLoggedIn.hidden = NO;
}
}
After had many research, I don't find solution.So that I decided post my question. I have an ** IBOutlet UITextField *txtUsername at DetailViewController.h. At FormViewController I have a UIScrollview, and then I add [self.scrollView addSubview:DetailView.view]. The problem is when stand at DetailViewController.m I call txtUsername.text it return a nil value. Any suggestion?
It seems that you are trying to embed one view controller within another. I recommend using a Container view and then loading DetailViewController into that. Simply adding the view, as you are in your code, is not going to be enough. You probably want all the behavior in DetailViewController to work.
After debug many time I found the problem is:
At FormViewController I have method:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = #"Add new user";
DetailView = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
return self;
}
And method (Which had call at viewDidLoad)
-(void) loadViewBySegment {
[DetailView.view removeFromSuperview];
[self.scrollView addSubview:DetailView.view];
}
and ViewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStylePlain target:self action:#selector(save)];
self.navigationItem.rightBarButtonItem = saveButton;
if(user)
{
userDetailView.user = user;
}
[self loadViewBySegment];
// Do any additional setup after loading the view from its nib.
}
and
-(void)save{
[[userDetailView userDetailInstance] insertData];
}
And then at DetailViewController I create insertData function
-(void)insertData{
User *object = [[User alloc] init];
object.Username = txtUsername.text;
object.FullName = txtFullname.text;
object.Title = txtTitle.text;
object.Role = [NSNumber numberWithInt:[txtRole.text intValue]];
object.UserInitial =txtInital.text;
object.Status = YES;
object.AddedTime = [NSDate date];
object.UpdatedTime = [NSDate date];
object.Phone = #"0949931124";
object.Password = #"123456";
[[DBManager sharedInstant] insertData:BUsers item:[object dictionary] target:self];
}
The reason why texfield.text is nil:
At FormViewController I call an instance of DetailViewController it's mean DetailViewController - will be have new alloc init. so that textfield's value will which had enter by user before will be reset.
The right way I need to do is: change the save function at FormViewController:
-(void)save{
[DetailView insertData];
}
P/S:Lesson Learn for me is: be careful when use Instace (Singleton Design partner).
I want to have a persistent button in the bottom right corner of my app. During all view transitions, the button should remain static. I'm having trouble deciding what view to add the button to. I know the button ought to be stored in the AppDelegate, but I don't know what other view it would be sense to add it to except the window. One downside of adding it to the window is that when there's an app running in the background (ie Phone), the added status bar padding will push down the window. In general, adding it to the window seems to be a hacky solution -- any thoughts?
Yes, adding it to the UIWindow would be extremely hacky and finicky.
Storyboards
If you're using Storyboards and iOS 5.0 onwards, you should be able to use container views and do something like this:
Here's another picture showing the, rather simplistic, structure of the first View Controller:
The view controller on the left has a container, and then a view which holds the button on top of it. The container indicates that the navigation controller (directly to the right) should appear within itself, that relationship is shown by the =([])=> arrow (formally known as an embed segue). Finally the navigation controller defines its root view controller to the one on the right.
In summary, the first view controller pancakes-in the container view with the button on top, so everything that happens inside has to have the button on top.
Using childViewControllers
aka. The "I hate Storyboards and puppies" mode
Using a similar structure to the Storyboard version, you could create the base view controller with its button, and then, add the view that will become then new "root" of the application, underneath.
To make it clear, let's call the one view controller that holds the button the FakeRootViewController, and the view controller that will be, for all practical purposes, the root of the application: RootViewController. All subsequent view controllers won't even know that there's the FakeRootViewController above everyone else.
FakeRootViewController.m
// The "real" root
#import "RootViewController.h"
// Call once after the view has been set up (either through nib or coded).
- (void)setupRootViewController
{
// Instantiate what will become the new root
RootViewController *root = [[RootViewController alloc] <#initWith...#>];
// Create the Navigation Controller
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:root];
// Add its view beneath all ours (including the button we made)
[self addChildViewController:nav];
[self.view insertSubview:nav.view atIndex:0];
[nav didMoveToParentViewController:self];
}
AppDelegate.m
#import "FakeRootViewController.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
FakeRootViewController *fakeRoot = [[FakeRootViewController alloc] <#initWith...#>];
self.window.rootViewController = fakeRoot;
[self.window makeKeyAndVisible];
return YES;
}
That way, you can have all the benefits of inserting the button on the window, without all the guilt and "Should I really be a programmer?" that it causes.
Potentially you could have 1 main "root" view controller, and all you other view controllers could be child view controllers, with their views as child views. Then they would have their content, and the button would be in the "root" view controller. But this seems just as sketchy and hacky as putting it in the window, and probably less convenient.
I use this button:
#interface UIPopUpButton : UIImageView <UIPopoverControllerDelegate, UIActionSheetDelegate>
{
UIPopoverController* popoverController;
Class popoverClass;
}
- (id) initWithPoint: (CGPoint) point;
- (void) touchesBegan: (NSSet*) touches
withEvent: (UIEvent*) event;
+ (id) buttonAtPoint: (CGPoint) point;
+ (id) buttonAtOriginalPoint;
+ (void) unhighlight;
+ (void) bringButtonToFront;
#property (nonatomic, retain) UIPopoverController* popoverController;
#property (nonatomic, assign) Class popoverClass;
#end
#import "UIPopUpButton.h"
#implementation UIPopUpButton
static UIPopUpButton* button = nil;
static CGPoint originalPoint;
#synthesize popoverClass;
#synthesize popoverController;
+ (id) buttonAtPoint: (CGPoint) point
{
if (button == nil)
{
button = [[UIPopUpButton alloc] initWithPoint: point];
originalPoint = point;
button.popoverClass = [UIPopoverController class];
}
else
{
button.frame = CGRectMake(point.x, point.y, button.frame.size.width, button.frame.size.height);
}
return button;
}
+ (id) buttonAtOriginalPoint
{
return [self buttonAtPoint: originalPoint];
}
+ (void) unhighlight
{
button.highlighted = NO;
}
+ (void) bringButtonToFront
{
[[UIApplication sharedApplication].keyWindow addSubview: [self buttonAtOriginalPoint]];
}
- (id) initWithPoint: (CGPoint) point
{
UIImage* image1 = [UIImage imageNamed: #"topbutton.png"];
UIImage* image2 = [UIImage imageNamed: #"topbutton.png"];
if ((self = [super initWithImage: image1
highlightedImage: image2]))
{
self.userInteractionEnabled = YES;
self.frame = CGRectMake(point.x, point.y, self.frame.size.width, self.frame.size.height);
self.multipleTouchEnabled = NO;
}
return self;
}
- (BOOL) isAppCurrStatus
{
return ([DevToolsClientController sharedInstance].statusOfRootViewController == FrontEndApplication);
}
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
if(touch.view == self)
{
if (self.popoverController == nil)
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle: #"Please choice operation:"
delegate: self
cancelButtonTitle: nil
destructiveButtonTitle: nil
otherButtonTitles: nil];
[actionSheet addButtonWithTitle: #"Cancel"];
actionSheet.cancelButtonIndex = 0;
[actionSheet addButtonWithTitle: #"Button 1"];
actionSheet.actionSheetStyle = UIActionSheetStyleDefault;
[actionSheet setTag: 0];
[actionSheet setDelegate: self];
[actionSheet showInView: [self superview]];
[actionSheet release];
[actions release];
}
else
{
PopoverMenuController* contentViewController = [[PopoverMenuController alloc] init];
self.popoverController = [[UIPopoverController alloc] initWithContentViewController: contentViewController];
popoverController.delegate = self;
[popoverController presentPopoverFromRect: CGRectMake(10.0f, 10.0f, 5.0f, 5.0f)
inView: self
permittedArrowDirections: UIPopoverArrowDirectionAny
animated: YES];
contentViewController.popoverController = self.popoverController;
[contentViewController reloadData];
}
}
else
{
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
}
}
[super touchesBegan: touches withEvent: event];
}
#pragma mark UIActionSheetDelegate implementation
-(void) actionSheet: (UIActionSheet*) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex
{
NSNumber* indexAction = [[NSNumber alloc] initWithInt: buttonIndex - 1];
}
- (void) runAction: (NSNumber*) indexAction
{
[DevToolsPopoverMenuController runAction: [indexAction integerValue]];
}
#pragma mark -
#pragma mark UIPopoverControllerDelegate implementation
- (void) popoverControllerDidDismissPopover: (UIPopoverController*) thePopoverController
{
if (self.popoverController != nil)
{
self.popoverController = nil;
}
}
- (BOOL) popoverControllerShouldDismissPopover: (UIPopoverController*) thePopoverController
{
//The popover is automatically dismissed if you click outside it, unless you return NO here
return YES;
}
#end
call:
[UIPopUpButton bringButtonToFront];
My button is always on top.
Try subclassing the UIViewController class and make your own one with the button
Create a singleton object that holds the button so all view controllers can reference it and add it to their subview or add it to the window directly.
SomeClass.h
#property (nonatomic) UIButton *yourButton;
+(SomeClass*)sharedSomeClass;
SomeClass.m
#synthesize yourButton = _yourButton;
-(id)init
{
self = [super init];
if(self)
{
_yourButton = [UIButton new];
//Other settings you want for your button
}
return self;
}
+(SomeClass)sharedSomeClass
{
static SomeClass *sharedSomeClass;
if (!sharedSomeClass)
sharedSomeClass = [[super allocWithZone:nil]init];
return sharedSomeClass;
}
+(void)allocWithZone:(NSZone*)zone
{
return [self sharedSomeClass];
}
If you like you can access the window directly like this:
UIWindow *mainwindow = [[[UIApplication sharedApplication]delegate]window];
import SomeClass.h into your view controllers, and access the button from anywhere
#import "SomeClass.h"
SomeClass *someClass = [SomeClass sharedSomeclass];
UIButton *localButton = someClass.yourButton;
I have a very simple RootView Controller ->Detail View Controller, to display a list of core data object and show details on a selected object in the DetailViewController.
The DetailViewController is a UITableView with custom UITableViewCell that has a UITextField to allow user edits.
I am able to display the table, able to edit the text field and so on. However, I am not sure how to actually update the manage object once the user chooses the Done button or cancel the changes upon a Cancel button action.
I understand I can probably achieve this by using a EditViewController, that can be used to edit one property at a time. But, I am interested in a solution where I could support inline editing in the DetailViewController. Any suggestions would be very helpful.
Thanks,
Custom UITableView Cell code
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
_textField = [[UITextField alloc] initWithFrame:CGRectZero];
[_textField setTextAlignment:UITextAlignmentLeft];
[_textField setReturnKeyType:UIReturnKeyDone];
[_textField setClearButtonMode:UITextFieldViewModeWhileEditing];
[_textField setDelegate:self];
[[self contentView] addSubview:_textField];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void) layoutSubviews {
[super layoutSubviews];
CGRect contentRect = [self.contentView bounds];
// In this example we will never be editing, but this illustrates the appropriate pattern
if ([self isEditing]) {
self.textLabel.frame = CGRectZero;
self.textField.frame = CGRectMake(contentRect.origin.x + kCellLeftOffset, kCellTopOffset, contentRect.size.width - kCellLeftOffset, kCellHeight);
}
else {
CGRect frame = CGRectMake(contentRect.origin.x + kCellLeftOffset, kCellTopOffset, 90, kCellHeight);
CGRect textFrame = CGRectMake(frame.origin.x + frame.size.width + kCellLeftOffset, kCellTopOffset, 180, kCellHeight);
self.textLabel.frame = frame;
self.textField.frame = textFrame;
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (!editing)
[_textField resignFirstResponder];
}
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField {
return [self isEditing];
}
The text editing should really be handled in a controller rather than a view. Your custom cell is a view, the appropriate place to put the text field delegate methods would be the detail view controller.
Here is your solution:
Pass the managedObjectModel of your root view controller to your detail view controller as a property. Do the same with the managed object to be edited.
In the delegate methods of your text field, update the object's properties as appropriate
- (void)textFieldDidEndEditing:(UITextField *)textField {
self.managedObject.textAttribute = textField.text;
}
Finally, in your handlers of the Done and Cancel buttons, save or discard the changes:
-(void)cancel {
[self.managedObjectContext rollback];
[self dismissModalViewControllerAnimated:YES]; // or pop
}
-(void)done {
[self.managedObjectContext save:nil]; // better use proper error handling
[self dismissModalViewControllerAnimated:YES]; // or pop
}