I am using a Master-Detail template.
I have a segmented control in the detail view and I have set the MasterViewController as the delegate.
This enables me to give the user choices.
I know the segmented control is working and passing the choices to the MVC.
I want each choice to trigger a new set of data which can then be loaded into the table view in the MasterViewController.
My problem is that I cannot then find a way to update the data in the table view.
You can use an instance variable declared in MasterViewController to be updated while UISegmentedControl changes. Based on that, When you come back to the MasterViewController; use its viewWillAppear to [tableView reloadData]
Tell me if I am getting your question wrong.
EDIT:
Take an integer name it segmentIndex; update as and when Segment gets changed. Based on the value of segmentIndex load the dataSource needed to be displayed on UITableView of your MasterViewController
EDIT 2:
When UISegmentedControl's value changes, put mvc.segmentIndex = (currentValueOfYourSegmentedControl);
Then in MasterViewController's viewWillAppear
switch (self.segmentIndex)
{
case 0:
// Set Datasource for First Choice.
break;
case 0:
// Set Datasource for Second Choice.
break;
case 0:
// Set Datasource for Third Choice.... and so on...
break;
default:
// Default Behavior
break;
}
Hope You Get My Point.
If it was me I would use notifications instead of delegates. i.e Detect the segmentedControl value change using valueChanged inside the detailViewController and then post an NSNotification from your detailViewcontroller which your masterViewController receives.
Easiest way is:
[[NSNotificationCenter defaultCenter] postNotificationName:#"segmentOneChosen" object:nil userInfo:nil];
Have you masterViewController register for this notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(segmentOneChosen) name:#"segmentOneChosen" object:nil];
It would be better still to pass the chosen value along with the notification so you don't need a separate notification for each segment.
!! untested code:
NSArray *keys = [NSArray arrayWithObjects:#"segmentChosen", nil];
NSArray *objects = [NSArray arrayWithObjects:[NSNumber numberWithInt:self.topicsChoiceSegControl.selectedSegmentIndex], nil];
NSDictionary * dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[[NSNotificationCenter defaultCenter] postNotificationName:#"segmentChosen" object:nil userInfo:dict];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(segmentChosen:) name:#"segmentChosen" object:nil];
-(void) segmentChosen:(NSNotification *)notification {
NSNumber *segmentChosenNum = [[notification userInfo] valueForKey:#"segmentChosen"];
}
I know this doesn't answer your question per se, but it does provide an alternative solution to the problem you are trying to overcome.
Related
I followed the Apple documentation to move a textfield upwards when the keypad appears.
The code works fine my problem is that I need that one specific textfield is moved towards the other, instead of implementing the code Apple every textfield I select is moved upwards ... How can I do to move a specific textField and not all?
Thank you very much, I insert the following code used
-(void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = changePasswordTextField.superview.frame;
bkgndRect.size.height -= kbSize.height;
[scrollView setContentOffset:CGPointMake(0.0, changePasswordTextField.frame.origin.y+kbSize.height) animated:YES];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
[scrollView setContentOffset:CGPointZero animated:YES];
}
You can achieve your functionality by following steps.
Set delegate of your UITextField.
Implement textFieldDidBeginEditing method which will be called when keyboard open for textfield. So you may change frame of textfield in this method as below.
-(void)textFieldDidBeginEditing:(UITextField *)textField{
[textField setFrame:CGRectMake(0.0, textField.frame.origin.y-VALUE,textField.frame.size.width,textField.frame.size.height) animated:YES];
// VALUE = textfield you want to move upward vertically
}
Now, to handle keyboard hiding event, you can set frame of your textfield to its origin in textFieldDidEndEditing method as below.
- (void)textFieldDidEndEditing:(UITextField *)textField{
[textField setFrame:CGRectMake(0.0, textField.frame.origin.y+VALUE,textField.frame.size.width,textField.frame.size.height) animated:YES];
// VALUE = textfield you want to move downward vertically
}
I hope it may help you.
I've looked everywhere, and hope that perhaps someone can point me in the right direction.
I just want to run a method each time the user selects a different record.
The bigger picture (in case there is an alternate way) is that when the user selects the record (single click) the persons phone numbers are to be put into a segmented control.
I've tried:
To connect an action to a button, I usually open the assistant editor, and right-click drag to the .h file. But when I'm doing it with this abpeoplepickerview I only get an Outlet connection type?
the people picker is a . 'compound view' that actually consits of a tableview, 2 buttons and a searchfield (IIRC)
answer:
you're out of luck and this component isnt suitable for you BUT of course you do some hacking:
- (void)viewDidLoad {
//you get the internal tableview
id views = [self findSubviewsOfKind:NSClassFromString(#"ABPeoplePickerTableView") withTag:NSNotFound inView:sef.peoplePickerView];
id view = [views count] ? [views objectAtIndex:0] : nil;
//subscribe to the notification
if([view respondsToSelector:#selector(selectedRow)]) {
[[NSNotificationCenter defaultCenter] addObserverForName:NSTableViewSelectionDidChangeNotification object:view queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self peoplePickerSelectedRecordChanged:self.peoplePickerView];
}];
}
}
- (NSArray *)findSubviewsOfKind:(Class)kind withTag:(NSInteger)tag inView:(NSView*)v {
NSMutableArray *array = [NSMutableArray array];
if(kind==nil || [v isKindOfClass:kind]) {
if(tag==NSNotFound || v.tag==tag) {
[array addObject:v];
}
}
for (id subview in v.subviews) {
NSArray *vChild = [self findSubviewsOfKind:kind withTag:tag inView:subview];
[array addObjectsFromArray:vChild];
}
return array;
}
- (IBAction)peoplePickerSelectedRecordChanged:(id)sender {
NSLog(#"%#", [sender selectedRecords]);
}
ABPeoplePickerView gives notifications for exactly what you need. Look near the end of the class reference.
#implementation someController
#synthesize picker; //your ABPeoplePickerView
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
// or some other method that gets called early on
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificate:)
name:ABPeoplePickerNameSelectionDidChangeNotification
object:picker];
}
- (void) notificate: (NSNotification*) notification {
ABPerson *person = picker.selectedRecords.firstObject;
NSLog(#"NOTIFIED %#"), person.name);
// name is a property of ABPerson I added in a category
// do what you will
}
Don't forget to remove the observer if you dismiss the window.
I am using GDataFeedYouTubeVideo to populate a tableview with images and titles. This works. I want to play the video after the cell is selected. I am using the url from the feed to pass to the MPMoviePlayer and it looks like it loads because the screen goes black, the moviePlaybackDidFinish is called but does not play the video and goes back to displaying the tableview? An example url from the feed is:
https://www.youtube.com/v/o7QAMH3qRvU?version=3&f=user_uploads&app=youtube_gdata
This does work from a browser but not in the MPMoviePlayer? Please help me figure this one out. I would rather not have to write some hack routine to replace or remove the URLString returned by the feed. I am using ARC & Storyboards. The second time I select a cell I get:
An instance 0xce6a7b0 of class AVPlayerItem was deallocated while key value observers were still registered with it...
Yes I did try what was suggested in:
iOS 5 an instance of AVPlayerItem was deallocated
and this did not fix it.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
YouTubeVideo *item = [searchList objectAtIndex:indexPath.row];
if (item != nil) {
NSURL *url = [NSURL URLWithString:item.URLString];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.shouldAutoplay = YES;
[self.view addSubview:moviePlayer.view];
[moviePlayer setFullscreen:YES animated:YES];
}
}
- (void)moviePlaybackDidFinish:(NSNotification *)notification {
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
if ([player respondsToSelector:#selector(setFullscreen:animated:)])
[player.view removeFromSuperview];
}
Tried this and it works if I format the url slightly:
https://github.com/hellozimi/HCYoutubeParser
I have a alertview that starts in a view A and must stop in view B. How can I stop the alertview on B?
thks
How are you moving from A to B while a UIAlertView is displayed? Maybe post some code.
This scenario does not seem user-friendly, but there is a way you can dismiss the alert from A in B.
View A
Create an NSNotificationCenter and point it to a method that dismisses the alert:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissAlert) name:#"dismissAlert" object:nil];
And the notification should call something like the following:
- (void) dismissAlert:(NSNotification *)notification
{
[alertView dismissWithClickedButtonIndex: 0 animated: YES];
}
View B
Now when you want to dismiss the alert, call the notification you created in View A:
[[NSNotificationCenter defaultCenter] postNotificationName:#"dismissAlert" object:nil];
Use dismissWithClickedButtonIndex:animated:
I'm seeing some quirky behaviour with Cocoa's KVC/KVO and bindings. I have an NSArrayController object, with its 'content' bound to an NSMutableArray, and I have a controller registered as an observer of the arrangedObjects property on the NSArrayController. With this setup, I expect to receive a KVO notification every time the array is modified. However, it appears that the KVO notification is only sent once; the very first time the array is modified.
I set up a brand new "Cocoa Application" project in Xcode to illustrate the problem. Here is my code:
BindingTesterAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface BindingTesterAppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow * window;
NSArrayController * arrayController;
NSMutableArray * mutableArray;
}
#property (assign) IBOutlet NSWindow * window;
#property (retain) NSArrayController * arrayController;
#property (retain) NSMutableArray * mutableArray;
- (void)changeArray:(id)sender;
#end
BindingTesterAppDelegate.m
#import "BindingTesterAppDelegate.h"
#implementation BindingTesterAppDelegate
#synthesize window;
#synthesize arrayController;
#synthesize mutableArray;
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSLog(#"load");
// create the array controller and the mutable array:
[self setArrayController:[[[NSArrayController alloc] init] autorelease]];
[self setMutableArray:[NSMutableArray arrayWithCapacity:0]];
// bind the arrayController to the array
[arrayController bind:#"content" // see update
toObject:self
withKeyPath:#"mutableArray"
options:0];
// set up an observer for arrangedObjects
[arrayController addObserver:self
forKeyPath:#"arrangedObjects"
options:0
context:nil];
// add a button to trigger events
NSButton * button = [[NSButton alloc]
initWithFrame:NSMakeRect(10, 10, 100, 30)];
[[window contentView] addSubview:button];
[button setTitle:#"change array"];
[button setTarget:self];
[button setAction:#selector(changeArray:)];
[button release];
NSLog(#"run");
}
- (void)changeArray:(id)sender
{
// modify the array (being sure to post KVO notifications):
[self willChangeValueForKey:#"mutableArray"];
[mutableArray addObject:[NSString stringWithString:#"something"]];
NSLog(#"changed the array: count = %d", [mutableArray count]);
[self didChangeValueForKey:#"mutableArray"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(#"%# changed!", keyPath);
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
NSLog(#"stop");
[self setMutableArray:nil];
[self setArrayController:nil];
NSLog(#"done");
}
#end
And here is the output:
load
run
changed the array: count = 1
arrangedObjects changed!
changed the array: count = 2
changed the array: count = 3
changed the array: count = 4
changed the array: count = 5
stop
arrangedObjects changed!
done
As you can see, the KVO notification is only sent the first time (and once more when the application exits). Why would this be the case?
update:
Thanks to orque for pointing out that I should be binding to the contentArray of my NSArrayController, not just its content. The above posted code works, as soon as this change is made:
// bind the arrayController to the array
[arrayController bind:#"contentArray" // <-- the change was made here
toObject:self
withKeyPath:#"mutableArray"
options:0];
First, you should bind to the contentArray (not content):
[arrayController bind:#"contentArray"
toObject:self
withKeyPath:#"mutableArray"
options:0];
Then, the straightforward way is to just use the arrayController to modify the array:
- (void)changeArray:(id)sender
{
// modify the array (being sure to post KVO notifications):
[arrayController addObject:#"something"];
NSLog(#"changed the array: count = %d", [mutableArray count]);
}
(in a real scenario you'll likely just want the button action to call -addObject:)
Using -[NSMutableArray addObject] will not automatically notify the controller. I see that you tried to work around this by manually using willChange/didChange on the mutableArray. This won't work because the array itself hasn't changed. That is, if the KVO system queries mutableArray before and after the change it will still have the same address.
If you want to use -[NSMutableArray addObject], you could willChange/didChange on arrangedObjects:
- (void)changeArray:(id)sender
{
// modify the array (being sure to post KVO notifications):
[arrayController willChangeValueForKey:#"arrangedObjects"];
[mutableArray addObject:#"something"];
NSLog(#"changed the array: count = %d", [mutableArray count]);
[arrayController didChangeValueForKey:#"arrangedObjects"];
}
There may be a cheaper key that would give the same effect. If you have a choice I would recommend just working through the controller and leaving the notifications up to the underlying system.
A much better way than explicitly posting whole-value KVO notifications is to implement array accessors and use them. Then KVO posts the notifications for free.
That way, instead of this:
[self willChangeValueForKey:#"things"];
[_things addObject:[NSString stringWithString:#"something"]];
[self didChangeValueForKey:#"things"];
You would do this:
[self insertObject:[NSString stringWithString:#"something"] inThingsAtIndex:[self countOfThings]];
Not only will KVO post the change notification for you, but it will be a more specific notification, being an array-insertion change rather than a whole-array change.
I usually add an addThingsObject: method that does the above, so that I can do:
[self addThingsObject:[NSString stringWithString:#"something"]];
Note that add<Key>Object: is not currently a KVC-recognized selector format for array properties (only set properties), whereas insertObject:in<Key>AtIndex: is, so your implementation of the former (if you choose to do that) must use the latter.
Oh, I was looking for a long time for this solution ! Thanks to all !
After getting the idea & playing around , I found another very fancy way:
Suppose I have an object CubeFrames like this:
#interface CubeFrames : NSObject {
NSInteger number;
NSInteger loops;
}
My Array contains Objects of Cubeframes, they are managed via (MVC) by an objectController and displayed in a tableView.
Bindings are done the common way:
"Content Array" of the objectController is bound to my array.
Important: set "Class Name" of objectController to class CubeFrames
If I add observers like this in my Appdelegate:
-(void)awakeFromNib {
//
// register ovbserver for array changes :
// the observer will observe each item of the array when it changes:
// + adding a cubFrames object
// + deleting a cubFrames object
// + changing values of loops or number in the tableview
[dataArrayCtrl addObserver:self forKeyPath:#"arrangedObjects.loops" options:0 context:nil];
[dataArrayCtrl addObserver:self forKeyPath:#"arrangedObjects.number" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(#"%# changed!", keyPath);
}
Now, indeed, I catch all the changes : adding and deleting rows, change on loops or number :-)