I've got a UIViewController which is also the UITableViewDelegate, amongst other things, for a UITableView, created in FirstView.xib
#interface FirstViewController : UIViewController <
UITextFieldDelegate,
UITableViewDelegate,
UITableViewDataSource
> {
UITableView *searchResults; // this is the property for the table view
...
}
I want this table view to make use of PullToRefresh: https://github.com/leah/PullToRefresh, but the documentation there only explains how to make use of the class as a sub class of the view controller
#import "PullRefreshTableViewController.h"
#interface DemoTableViewController : PullRefreshTableViewController {
NSMutableArray *items;
}
My app uses a Tab bar as the root view controller, can anyone explain to me how I can make the UITableView into a PullRefreshTableView? When I don't have a UITableViewController to edit?
The secret is in the scroll view delegate methods which you can already respond to since you are acting as the tables delegate. This article provides a good start to create your own pull to refresh.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
This will let you know when the user starts dragging the scrollview so you can begin checking whether to refresh or not.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
This allows you to make necessary transitions while scrolling (mainly swapping text and flipping the arrow)
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate
This is where you decide if the user has dragged far enough down to start the refresh.
Use this API
It works fine with UIViewController
Related
What I've done so far is working but I would like to know whether this is the proper way or not.
I have a map that shows an annotation when this is pressed shows a callout.
The next view shown is a table view. This table has a button to remove that annotation.
I created one property in the table View of type MKMapView. After this view is initialized when the callOut accessory is tapped, I set the MKMapView property.
When the button is pressed in the table view, I delete the annotation through the map property.
Is this the right way?
Rather than the detail view directly manipulating the parent (map) controller view's controls, a more "right" approach might be to use delegate+protocol.
Define a protocol with the methods that the map controller needs to implement (eg. deleteAnnotation, detailViewDone, etc).
The detail view will have a delegate property for that protocol and call the protocol methods via the delegate property instead of directly accessing and modifying another view's controls.
The map controller would set itself as the delegate of the detail view and actually implement the protocol methods.
This way, each controller/class doesn't have to know the internal details of how the others work and let's you more easily change how each one works internally without affecting code in the others (as long as the protocol doesn't change). It improves encapsulation and reusability.
For example, in the detail view .h, define the protocol and declare the delegate property:
#protocol DetailViewControllerDelegate <NSObject>
-(void)deleteAnnotation:(id<MKAnnotation>)annotation;
-(void)detailViewDone;
//could have more methods or change/add parameters as needed
#end
#interface DetailViewController : UIViewController
#property (nonatomic, assign) id<DetailViewControllerDelegate> delegate;
#end
In the detail view .m, wherever you handle the delete button, call the delegate method instead:
if ([delegate respondsToSelector:#selector(deleteAnnotation:)])
{
[delegate deleteAnnotation:annotation];
}
In the map controller .h, declare that it implements the protocol and declare the methods:
#interface MapViewController : UIViewController<DetailViewControllerDelegate>
-(void)deleteAnnotation:(id<MKAnnotation>)annotation;
-(void)detailViewDone;
#end
In the map controller .m, in calloutAccessoryControlTapped where you create the detail view, set the delegate property instead of the map view property:
DetailViewController *dvc = [[DetailViewController alloc] init...
dvc.annotation = view.annotation;
dvc.delegate = self;
[self presentModalViewController:dvc animated:YES];
Finally, also in the map controller .m, implement the delegate method:
-(void)deleteAnnotation:(id<MKAnnotation>)annotation
{
[mapView removeAnnotation:annotation];
//dismiss the detail view (if that's what you want)...
[self dismissModalViewControllerAnimated:YES];
}
From the documentation, the articles Delegates and Data Sources and Using Delegation to Communicate with Other Controllers may be useful as well.
I have a UISplitViewController with the master view set up like this:
UITabBarController
Tab1:
UINavigationController -> UIViewController -> UIViewController
Tab2:
UINavigationController -> UIViewController
Each of the UIViewControllers is a table view, and when the user chooses a row in the last one, an image is shown in the detail view, which contains a UIScrollView.
The tab bar Controller is the UISplitViewControllerDelegate and handles putting up the button on a toolbar at the top of the scroll view.
The problem is, I want to add code to dismiss the popover when the user makes their choice. The pointer to the popover has to be saved in the tab bar controller when the button goes up, and then used to dismiss the popover several view controllers down the line when the user makes their final selection. There doesn't seem to be any way for the view controller that needs that pointer to get at it, without doing something gross like storing it in the App Delegate.
I don't see other people asking this question, which leads me to believe that I've once again overlooked something simple. Please enlighten me!
It sounds like your tab bar controller is already a subclass of UITabBarController, which means that you've already got some custom code in there. I would suggest that the tab bar controller is the primary owner of the popover, and it is the table view controller's responsibility to simply notify the tab bar controller that a selection has been made. The tab bar controller can respond to that message by dismissing the popover. You can take advantage of the fact that UIViewController already has a method for accessing the tab bar controller that contains a given controller.
So it would look something like this:
#interface MyTabBarController : UITabBarController
- (void)itemWasSelected;
#end
#implementation MyTabBarController {
UIPopoverController *popover;
}
- (void)itemWasSelected {
[popover dismissPopoverControllerAnimated:YES];
}
#end
//////////////
#implementation TableController
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)path {
// Do whatever else you want to do
MyTabBarController *tabController = (MyTabBarController *)self.tabBarController;
[tabController itemWasSelected];
}
With this solution, the table controller doesn't have to know anything about the popover; it just has to know that it's going to be presented inside a MyTabBarController, which seems a reasonable thing for it to know.
You could create a singleton class to track your popover status and then make it available to all classes equally and easily. That way it could easily be updated and accessed from any code without having to go straight to overburdening the app delegate even though thats basically the same idea but a bit cleaner in its own singleton.
I have just converted from .nib files to storyboard, but suddenly the view wont rotate topbar in landscape view. All the settings are "inferred" in my view, and i have not really made any changes since the conversion.
Is this a common problem when upgrading? I have not found any specific info.
And furthermore i do not force any view rotations in my code.
If any more info is needed i can supply anything!
Thanks in advance.
ViewController:
- (void) viewDidLoad {
[super viewDidLoad];
self.view.autoresizingMask = UIViewAutoresizingNone;
self.view.autoresizesSubviews = UIViewAutoresizingNone;
}
I've taken a look at your code and you seem to be missing a method that allows your view controller to rotate freely.
Subclass UIViewController e.g. like this:
// .h file
#interface OrientationAwareViewController : UIViewController
#end
// m.file
#implementation OrientationAwareViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
#end
Then set OrientationAwareViewController in the storyboard as your main view controller's class name. That said, I have no idea how this worked for you when using nibs :) Documentation says clearly:
By default, this method returns YES for the UIInterfaceOrientationPortrait orientation only. If your view controller supports additional orientations, override this method and return YES for all orientations it supports.
I'm very new in Mac OS programming. At the moment I'm trying to create simple measurement application which will have one window with the toolbar at the top and the appropriate view in the bottom. Clicking button in the toolbar should result in switching view below it - e.g. clicking on the "Connection" button will show with connection settings, "Measurements" will show current data from the device.
The problem is - I don't know how to handle swapping views, maybe in other words - something I know but not exactly...
I found similar discussion here: NSViewController and multiple subviews from a Nib but there is no answer how to create NSWindowController and how to assign it to the Main window. Because I guess it is necessary to create NSWindowController to be able to swapping views. If I'm wrong, please correct me.
So I'm creating new project (called Sample here) and there is SampleAppDelegate.h file, which looks like:
#interface SampleAppDelegate : NSObject <NSApplicationDelegate> {
#private
NSWindow *window;
}
#property (assign) IBOutlet NSWindow *window;
#end
There is window ivar, which holds the only one window, created from the MainMenu.xib (as I think).
So how should I create NSWindowController for the window from the SampleAppDelegate?
Should I just create my WindowController subclass and in the function
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
of the SampleAppDelegate like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MyWindowController *wc = [[MyWindowController alloc] initWithWindow:self.window];
[wc showWindow:self];
self.myWindowController = wc;
[wc release];
}
I'll be very grateful for any hints and help.
Marcin
You shouldn't need an NSWindowController to do view swapping, NSWindowController used (I think) just when you need multiple toplevel windows.
You can just subclass NSViewController for each type of view that you want, put each view into a nib, and call -(NSView *)view when you need a view to put into the bottom part of the window. You should be able to just add it to the window like normal, or put it in an NSBox by using setContentView:view
For your two views you'd create MeasurmentsViewController and a ConnectionViewController. Then you'd create your views in MeasurementsView.nib and ConnectionView.nib, and use those nibs to initialise your view controllers.
Then in your main window, if you were to put an NSBox, if you wanted to put the MeasurementsView into it
NSView *measurementsView = [measurementsViewController view];
[boxAtBottomOfWindow setContentView:measurementsView];
and to put the ConnectionView into it
NSView *connectionView = [connectionViewController view];
[boxAtBottomOfWindow setContentView:connectionView];
Working on an experiment on the iPad. Tried some variations on how to do this, but I can't seem to get it to work correctly...
I tap a UIButton on my MainViewController and a TextEntryModule is added to the view. TextEntryModule is its own class (for multiple instantiation) and it contains a UITextView called TextEntry (this all works at the moment).
I tap on the TextEntry UITextView and it brings up the keyboard and another view (located in MainViewController) with a UITextView called TextPreview. (this also works at the moment).
The part I'm having trouble with is synching the two UITextViews. The idea being that when I type into TextEntry, the text in TextPreview will also be updated.
Outlets are linked properly for the text fields, but I think I'm missing something "obvious":
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
Thanks in advance!
UITextView: delegate.
- (void)textViewDidChange:(UITextView *)textView
Then assign it the value of the other textview in this method.
Edit
#interface MainViewController <UITextViewDelegate> {
...
}
...
#end
Then you implement this method in the implementation file of MainViewController
#implementation MainViewController
//More code
- (void)textViewDidChange:(UITextView *)textView {
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
}
#end
Then you will have to set the TextEntryModule object's delegate to self since the controller now conform to the protocol and can "act" upon this notification.
You need to become a UITextFieldDelegate and monitor when text changes in the one field and then update the other field. Take a look at the documentation on it.