I'm trying to change the alpha of an UIButton from another class. The function that is called in set the alpha property of my UIButton is actually called because I've put a NSLog there and I can see how it works. I'd be thankful if you could give me any suggestion.
Here's my current code.
ViewController.h
- (void) setAlphaToButton;
#property (strong, nonatomic) IBOutlet UIButton *myButton;
ViewController.m
#synthesize myButton;
- (void) setAlphaToButton {
myButton.alpha = 0.5;
NSLog(#"Alpha set");
}
ImageViewSubclass.m
- (void) tapDetected:(UITapGestureRecognizer *)tapRecognizer {
ViewController *VC = [[ViewController alloc] init];
[VC setAlphaToButton];
}
And when the image view is pressed, in my console I get: Alpha set. And the button doesn't change.
In your code, an instance of ViewController is alloced and inited, and the method setAlphaToButton is called on it. Then the view controller is released because you have no object retaining it. That's why you don't see any effect; the ViewController instance you call the method on never appears on screen.
It's not clear how your code is supposed to work; do you have an instance of ViewController in existence when tapDetected is called? If this is the case, and this is the ViewController whose button you want to alter the alpha of, then you need to have a reference to that instance of ViewController and call setAlphaToButton on it.
Your view is not loaded at the moment you trying to set alpha! You need to call this method after your viewDidLoad fired. You can force it by calling view, but it's kind of hackand not recommended!
MyViewController *vc = [MyViewController new];
vc.view; // this string will force view loading
[vc setAlphaToButton];
Add a property of uiviewcontroller class in imageviewsubclass as
ImageViewSubclass.h
#propery (nonatomic, retain) uiviewController *parent;
ImageViewSubclass.m
#synthesize parent;
And initialize it with "self" in view controller class when initalize object of imageviewsubclass and add on the view like
ImageViewsubclass *oneObj = [ImageViewsubClass alloc] init];
oneOBj.parent = self;
do the same for all objects of ImageviewsubClass objects.
and in
- (void) tapDetected:(UITapGestureRecognizer *)tapRecognizer {
[parent setAlphaToButton];
}
Related
I have a View Controller class which contains a button property, and I need to change its enabled stated from a different class (Table View Controller). I'm also calling a method that's in that VC class. I can call the method just fine, but when I try to access the button property, it's nil. Actually all of its properties are nil. I must have something not set up quite right.
//ViewController.h
#property (strong, nonatomic) IBOutlet UIButton *aButton;
- (IBAction)myButtonTapped;
//ViewController.m
//did not override setter or getter for aButton
- (IBAction)myButtonTapped {
//code here
}
//Table VC.m
#property (strong, nonatomic) ViewController *myVC;
- (ViewController *)myVC {
if (!_myVC) _myVC = [[ViewController alloc] init];
return _myVC;
}
- (void)userEnteredText:(NSNotification *)notification {
[self.myVC myButtonTapped]; //runs method without issue
self.myVC.aButton.enabled = YES; //does not occur since aButton is nil - myVC is not nil
}
You need to study the information provided by #Hot Licks to understand how to keep references between your objects, but I can tell you that part of your problem is your getter method -
- (ViewController *)myVC {
if (!_myVC) _myVC = [[ViewController alloc] init]; // <-- This is a problem
return _myVC;
}
If your _myVC variable is nil then your getter method allocates a new ViewController - so you won't get a reference to the existing viewController. As you then call the plain init method for your new viewController none of its properties will be initialised - so you get nil for your button.
You don't need to write any code for a simple property like this - the default code that is created for you is all you need. What you do need to do is set the myVC property from your current viewController instance. So somewhere in your viewController you will have
tableVC.myVC=self;
You will need to do this somewhere where you have a reference to your tableVC - so this could be inprepareForSegue if you are using storyboards and segues or wherever you present or push the table vc if you aren't
Just started xcode 5 and xctest. How do I test that a view loads on button press. I have programatically added method that gets called when the rightBarButtonItem is clicked
action:#selector(onSettingsButton)
and in onSettingsButton
-(void) onSettingsButton{
SettingsViewController *svc = [[SettingsViewController alloc] init];
[self.navigationController pushViewController:svc animated:YES];
}
How to write xctest to ensure SettingsViewController brings up the Settings view? Thank you.
You need an interaction test — that is, a test that checks interactions between objects. In this case, you want to test that -pushViewController:animated: is called on the navigation controller with a SettingsViewController. So we want to put a mock object into self.navigationController which we can ask, "Were you called as expected?"
I'll assume a simple name for the class: MyView.
The way I'd do this by hand is to Subclass and Override navigationController. So in my test code, I'd do something like this:
#interface TestableMyView : MyView
#property (nonatomic, strong) id mockNavigationController;
#end
#implementation TestableMyView
- (UINavigationController *)navigationController
{
return mockNavigationController;
}
#end
Now instead of creating a MyView, the test will create a TestableMyView and set its mockNavigationController property.
This mock can be anything, as long as it responds to -pushViewController:animated: and records the arguments. Here's a simple example, by hand:
#interface MockNavigationController : NSObject
#property (nonatomic) int pushViewControllerCount;
#property (nonatomic, strong) UIViewController *pushedViewController;
#property (nonatomic) BOOL wasPushViewControllerAnimated;
#end
#implementation MockNavigationController
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
self.pushViewControllerCount += 1;
self.pushedViewController = viewController;
self.wasPushViewControllerAnimated = animated;
}
#end
Finally, here's a test:
- (void)testOnSettingsButton_ShouldPushSettingsViewController
{
// given
MockNavigationController *mockNav = [[MockNavigationController alloc] init];
TestableMyView *sut = [[TestableMyView alloc] init];
sut.mockNavigationController = mockNav;
// when
[sut onSettingsButton];
// then
XCTAssertEquals(1, mockNav.pushViewControllerCount);
XCTAssertTrue([mockNav.pushedViewController isKindOfClass:[SettingsViewController class]]);
}
These things can be simplified by using mock object frameworks such as OCMock, OCMockito, or Kiwi's mocking. But I think it helps to start by hand first, so that you understand the concepts. Then choose the tools that help. And if you know how to do it by hand, you'll never say, "Mocking framework X doesn't do what I need! I'm stuck!"
Found one way. Maybe there are others ..
- (void)testSettingsViewShowsWhenSettingsButtonIsClicked{
[self.tipViewController onSettingsButton];
id temp = self.tipViewController.navigationController.visibleViewController;
XCTAssertEqual([temp class], [SettingsViewController class], #"Current controller should be Settings view controller");
}
First call the onSettingsButton, which is the same as clicking the button, but not really. Maybe it's okay for this simple test case? How to simulate the actual press?
Then get the current view from the tipviewcontoller which is the rootview of the app and check that it is a SettingsViewController.
I have a UIViewController with a single button and an activity indicator.
In the class for this VC MainViewController.m I do the following in viewDidAppear:
- (void)viewDidLoad
{
[super viewDidLoad];
_actLoadLoc.color = [UIColor blueColor];
_startButton.enabled = NO;
[_startButton setTitle:#"Fetching Location" forState:UIControlStateDisabled];
}
Another method in my MainViewController.m is called readyToGo and is implemented as follows:
-(void) readyToGo
{
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"Done sleeping");
_startButton.enabled = YES;
[_startButton setTitle:#"Start" forState:UIControlStateNormal];
_actLoadLoc.stopAnimating;
}
I have properties for both UIButton, UIActivityIndicatorView and a declaration of the readyToGo method in my MainViewController.h as follows:
#property (weak, nonatomic) IBOutlet UIButton *startButton;
#property (weak, nonatomic) IBOutlet UIActivityIndicatorView *actLoadLoc;
-(void) readyToGo;
The readyToGo method is called from another class abc.[h/m] which imports MainViewController.h. The call happens after one of the functions in abc.m completes filling an array with calculated data.
The call works since Done Sleeping shows in the output, however the startButton is not enabled, its test does not change and the actLoadLoc does not stop animating... Any idea what's wrong with my code/method?
Thanks in Advance!
You are calling the readyToGo on the wrong instance of the view controller. You have an instance which is displaying content on the screen and you are, in some way, creating a new one to call the method on. You need to get the existing one instead.
It's not ideal, but you should be able to get the controller with:
UINavigationController *n = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController;
SDPPMainViewController *mvc = (SDPPMainViewController *)[n viewControllers][0];
(Will need to add some casts, and should probably break out to multiple lines)
I am trying to make a custom protocol that i hope somebody might help with.
I have a mainViewController (mainVC) that has a label. That label needs to be updated with a string when i press a button in edwwVC.
I am using ARC and storyboard.
The problem is when i press the Done Button on the edwwVC, the "done" method is called BUT the delegate method is not called in mainVC.
Whereas, if i call the done method VIA the mainVC, then the done method is called AND the delegate method. So I can see the connection is there, I just do not understand why the delegate method is not called when i press the done button in the edwwVC.
I imagine it has something to do with the init of the edwwVC. Because it is already initiated by storyboard, so it looks to me as if I am initializing it again the in the viewDidLoad method of the mainVC. But that is how far i got :)
Thanks in advance!
edwwVC.h
#import <UIKit/UIKit.h>
#import "IIViewDeckController.h"
#class EDWWViewController;
#protocol EDWWViewControllerDelegate <NSObject>;
#optional
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC;
#end
#interface EDWWViewController : UIViewController <IIViewDeckControllerDelegate> {
__weak id<EDWWViewControllerDelegate> delegate;
NSMutableArray *edwwPoints;
}
#property (weak) id<EDWWViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) NSString *testString;
- (IBAction)done:(id)sender;
- (IBAction)add:(id)sender;
#end
edwwVC.m:
#pragma mark - delegate method
- (IBAction)done:(id)sender {
testString = #"This is the test string!";
[delegate edwwVCDidFinish:self];
[self.viewDeckController closeRightViewAnimated:YES];
NSLog(#"Done pressed");
}
MainVC.m:
- (void)viewDidLoad
{
[super viewDidLoad];
edwwViewController = [[EDWWViewController alloc] init];
edwwViewController.delegate = self;
}
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC {
edwwLabel.text= edwwVC.testString;
NSLog(#"delegate method called");
}
Remove the line ...
__weak id<EDWWViewControllerDelegate> delegate;
From the .h and change the line...
[delegate edwwVCDidFinish:self];
to...
[self.delegate edwwVCDidFinish:self];
In the .m.
That should sort it.
The way you have it set up the ivar delegate is not the same as the property delegate (which is actually an ivar called _delegate) (thanks #Joris Kluivers, just adding for clarity). They are pointing to different things.
If you add a breakpoint where you are calling the delegate method I think you'll find that delegate is nil. Whereas _delegate (or self.delegate) is not nil.
::EDIT::
Ahh... just spotted the second bit too.
If you are setting up the edwwvc in storyboard then you should be alloc initing it too.
If you are segue-ing to the edwwvc then you should intercept the segue in mainVC.m like this...
- (void)prepareForSegue: //blah the rest of the name...
{
if ([segue.identifier isEqualToString:#"the name of your segue"])
{
EDWWViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
}
This will take the controller that you are pushing to from the storyboard and set the delegate to it.
:: ANOTHER EDIT ::
If EDWWVC is inside a containerViewController then you can do this inside viewDidLoad in MainVC.m...
- (void)viewDidLoad
{
// other stuff...
for (UIViewController *controller in self.childViewControllers) {
if ([controller isKindOfClass:[EDWWViewController class]]) {
EDWWViewController *edwwvc = (EDWWViewController*)controller;
eddwvc.delegate = self;
}
}
}
You may find this code has to go in viewDidAppear or something but I think viewDidLoad shouldd work just fine.
You may actually be able to set the delegate property directly by using the storyboard to (but I'm not 100% certain on this).
The answer was in the containerVC of both controllers.
Where i initialized the view controllers: the viewDidLoad of the containerVC m file:
- (void)viewDidLoad
{
[super viewDidLoad];
mainVC = (MainViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"MainVC"];
edwwVC = (EDWWViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"EDWWVC"];
//THIS LINE WAS MISSING
edwwVC.delegate = mainVC;
self.centerController = mainVC;
self.rightController = edwwVC;
}
BUT guys thanks for the help! :) Appreciate it got me in the right direction! :) THANKS! :)
I´m having problems declarating my own delegate. Well...thats not exactly true: i have it declarated and, when i build the project, the compiler reports no issues. I declarated it in this way:
I made a file (enviarDatos.h) for declare the protocol:
#protocol enviarDatos <NSObject>
- (void)addItemViewController:(NSMutableArray *)item;
#end
In the Vista2.h (ViewController) file I imported the file enviarDatos.h and declared a property:
#property (nonatomic, weak) id <enviarDatos> delegare;
In the Vista2.m (ViewController) file I use the protocol method:
#interface ViewController : UIViewController <enviarDatos> {
And, finally, in the ViewController.m file I implement the delegates method:
- (void)addItemViewController:(NSMutableArray *)ar {
origen = ar;
}
Does anyone see something wrong? the code of the last function its never executing.
Thanks for your help.
EDIT:
What i need is to change an array in ViewController from Vista2 (another viewcontroller)
Then create delegate property in next view(child view) & set it to self in parent view while pushing or showing child view.
ParentView.m
1.Implement protocol methods
- (void)addItemViewController:(NSMutableArray *)ar
{
origen = ar;
}
2.While showing child view
ChildViewController *child = [[ChildViewController alloc] init];
child.delegate = self;
//present child view
ChildView.h
#property (nonatomic, weak) id <enviarDatos> delegare;
ChildView.m
-(void) anyMethod
{
if([self.delegate respondsToSelector:#selector(addItemViewController:)])
{
[self.delegate addItemViewController:mutableArray];
}
}
Ah, it looks like you are declaring the delegate property in the wrong place.
You should declare the property delegate in enviarDatos.h.
#property (nonatomic, weak) id <enviarDatos> delegate;
Then in Vista2.m you will do something like this...
EnviarDatos *myObject = [[EnviarDatos alloc] init];
myObject.delegate = self;
This then sets up the EnviarDatos object and assigns the Vista2 object as the delegate.
Now, in EnviarDatos.m you can run...
[self.delegate addItemViewController:someObjectArray];
And this will then run that code in the Vista2 object.
Delegates are used for calling back to objects that create them (or some other objects). If you create an object and then want to run a method in it then you won't need a delegate.
Can you say at what condition addItemViewController is invoked?
You seem to be on the right track, but are you sure you are setting the delegate as
[yourObject setDelegate: self];
Have you tried debugging it? Does the debugger pause at addItemViewController if you set a breakpoint there? Can you confirm the delegate is not null inside the method? I may post some code but your seems to be right except for the assigning of delegate, I think you should check it.