How to manage 2 UITableView (master and detail) in 1 UIView - objective-c

I would like to display 2 UItableView on a same UIView. Important thing: when I select a row on the first table (master), I want to display the detail rows on the second table (detail).
In order to do that, I have :
created 1 UIViewController with 2 UITableView IB Outlet properties, linked with the 2 tables in the storyboard
set delegate and datasource for both UITableView in the root UIViewController
created 1 UITableViewController for each UITableView
implemented all the required methods for both delegates
I manage to display data in the first table, but not in the second, although the tableView:numberOfRowsInSection: is correct for the two tables.
When I run my app in debug mode, I don't see any call to the tableView:cellForRowAtIndexPath: method for the detail table.
I don't understand what I have missed or where I'm wrong. Anyone could help me ?
Here are parts of my code :
RootViewController.h
#interface RootViewController : UIViewController
{
MasterTVController * masterController;
IBOutlet UITableView * masterTV;
IBOutlet UITableView * detailTV;
}
RootViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (masterController == nil) {
masterController = [[masterTVController alloc] init];
}
[masterTV setDataSource:masterController];
[masterTV setDelegate:masterController];
masterController.view = masterController.tableView;
[detailTV setDataSource:masterController.detailTVController];
[detailTV setDelegate:masterController.detailTVController];
}
MasterTVController.h
#interface MasterTVController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
NSMutableArray * masterArray;
NSMutableArray * detailArray;
DetailTVController * detailTVController;
}
MasterTVController.m
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
self.detailTVController = [[DetailTVController alloc] init];
self.detailTVController.view = self.detailTVController.tableView;
}
return self;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailArray = [[self.masterArray objectAtIndex:indexPath.row] objectForKey:#"details"];
self.detailTVController.dataArray = detailArray;
[self.detailTVController.tableView reloadData];
}

I suspect your detailTVController.tableView is not set to detailTV, so that last line in tableView:didSelectRowAtIndexPath: is not sending reloadData to the right table view. Try adding this in viewDidLoad:
masterController.detailTVController.tableView = detailTV;
You might also need to set masterController.tableView in a similar fashion.
Also, one of these two lines seems suspicious:
[menusTV setDataSource:masterController];
[masterTV setDelegate:masterController];

Related

NSTableView ViewBased never calling the needed delegate

I have a NSTableView where I wish to display a list of info.
Currently the viewForTableColumn method delegate never runs, but numberOfRowsInTableView does.
I have the NSTableViewDelegate and NSTableViewDataSource set in the ViewController head. And I set the tableview delegate and datasource to self. Does somebody know why it wouldn't run? I've added a screenshot and code below.
ViewController.h
#interface ViewController : NSViewController <NSTableViewDelegate, NSTableViewDataSource>
#property (strong) IBOutlet NSTableView *tableView;
#property (strong, nonatomic) NSMutableArray<App *> *installedApps;
#end
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
_installedApps = [[NSMutableArray alloc] init];
_tableView.dataSource = self;
_tableView.delegate = self;
// Other stuff that populates the array
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return _installedApps.count;
}
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView *result = [tableView makeViewWithIdentifier:#"appCell" owner:self];
result.textField.stringValue = #"Hello world";
return result;
}
The view is in a container view, I have the 'appCell' identifier set to the Table Cell View.
The array _installedApps is empty and numberOfRowsInTableView: returns 0. Thus, tableView:viewForTableColumn: is not called because there is no row to show. No rows also means no columns.
You should also ensure that you have configured your table view as view based in attributed inspector of the table view.
I can't see it in the screenshots, but...is the highlighted row of the view hierarchy (Table Cell View) the one with the appCell identifier?
[minutes pass...]
Oops; sorry. I see you've noted that above.
The reason I ask is that I made a new project from your code, changing the array type from App to NSString, added a one-column table view to the storyboard, linked it to the code, added a couple enties to the array in -viewDidLoad, and -- once I put the appCell identifier in the right place (duh) -- it all worked fine.
This is super strange, but I had the very same issue, everything connected correctly, number of rows being called, but not viewForTableColumn... In the end the following Answer proved to be the solution:
https://stackoverflow.com/a/13091436/3963806
Basically, I had setup the tableview straight out of the Object library, no layout constraints etc... Once I added layout constraints, the method started to be called... Super strange as I could see and click on the "rows" but they weren't populated correctly... I think it's down to clipping as mention in the linked answer.

Xcode / ObjectiveC - Convert UITableViewController into TableView embedded in a UIViewController

I am fairly new to this Native App dev - I have built an app which contains a UITableViewController to display messages - all works fine - but for styling reasons I need to change it from a TableViewController to a tableview embedded within a viewcontroller.
I have made a view controller containing a table view and relevant linked custom cells / fields and altered the associated header file to -
#interface NotificationsListTVController : UIViewController
but my table methods no longer fire and I'm not sure how to instantiate them?
(code below)
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.GPTNotifications.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
static NSString *CellIdentifierRead = #"CellRead";
UITableViewCell *cell;
notifications *n = [self.GPTNotifications objectAtIndex:indexPath.row];
if (n.read == false) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
CustomCellRead *cellReadB = (CustomCellRead *)cell;
cellReadB.notifTitle.text = n.notifTitleD;
cellReadB.notifDate.text = n.notifDateD;
cellReadB.notifMsg.text = n.notifMessage;
return cellReadB;
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierRead forIndexPath:indexPath];
CustomCell *cellReadB = (CustomCell *)cell;
cellReadB.notifTitle.text = n.notifTitleD;
cellReadB.notifDate.text = n.notifDateD;
cellReadB.notifMsg.text = n.notifMessage;
return cellReadB;
}
}
Are you setting the delegate and datasource of your tableview to your class?
Something like:
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
When you create a UITableViewController this is done for you, but if you add the table yourself you need to set them.
Also:
#interface NotificationsListTVController : UIViewController <UITableViewDelegate, UITableViewDataSource>
I do it this way in Interface Builder:
Make your TableViewController
Make your ViewController and add a ContainerView to it
Delete the segued embedded ViewController that comes with it
Select the ContainerView and draw a connection from viewDidLoad to your TableViewController
you'll get only once option: embed
Done. Your TableViewController will now get displayed within your ViewController.
Pass whatever Data you need forward from the ViewController to the TableViewController with the embedded Segue.
Make the following changes in NotificationsListTVController.h:
#interface NotificationsListTVController : UIViewController<UITableViewDataSource,UITableViewDelegate>
Also in NotificationsListTVController.m, dont forget to provide these two statements as well.
tableView.delegate=self ;
tableView.dataSource=self;
These are required to set the delegate methods.These two statements need to be provided after initializing your tableView. Like for instance :
tblView = [[UITableView alloc] initWithFrame:CGRectMake(100,200,320,420) style: UITableViewStyleGrouped];
tblView.delegate = self;
tblView.dataSource = self;
[self.view addSubview:tblView];
These methods you are referring to are the delegate methods which cannot be fired directly unlike other ordinary methods.
Hope it Helps!!!

Correct way to transition between collection view and paged detail view

Currently I have a uicollection view which displays a specific album in the users photos, (ALAssets library).
In my mainView.m I gather the pictures:
+ (ALAssetsLibrary *)defaultAssetsLibrary {
static dispatch_once_t pred = 0;
static ALAssetsLibrary *library = nil;
dispatch_once(&pred, ^{
library = [[ALAssetsLibrary alloc] init];
});
return library;
}
- (void)beginLoadingPhotoInfo {
...
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {NSLog(#"Probs");}
];
}
Load them (the thumbnail version) all into the collection view and that all works well.
Then when a user selects a photo I call this prepareToSegue method: (still in mainView.m)
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:#"showDetail"])
{
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
DetailViewController *detailviewcontroller = [segue destinationViewController];
detailviewcontroller.photoArrayIndex = indexPath.row;
//photos array
detailviewcontroller.photosArray = _photoListArray;
}
Currently I am sending an array with the info of the photos and attempting to scroll to the position in the array.
I found this resource here for the horizontal paging:
http://adoptioncurve.net/archives/2013/04/creating-a-paged-photo-gallery-with-a-uicollectionview/
Which allows for paging using a collection view. I wrote that a detailViewController class.
Here's the question. How should I connect the two?
Idea 1: Have my mainView send an integer number representing the photo selected and the detailViewController will then load that one and begin lazy loading the photos.
Idea 2: Somehow preload some of the full screen photos and then send the integer with the spot in the array.
Idea 3: Send both the number and my array object over to the detailViewController so that I don't have to enumerate through the assets library again.
Are any of these the correct approach or did I miss the idea completely?
edit:
What I have in my detail controller is an uicollectionview flow layout with paging enabled.
This is the method where I set up the layout:
- (void) setCollectionView {
[self.collectionView registerClass:[DetailViewCell class] forCellWithReuseIdentifier:#"detailViewCell"];
//Flow Layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:YES];
[self.collectionView setCollectionViewLayout:flowLayout];
CGFloat pageWidth = self.collectionView.frame.size.width;
NSInteger num = _photosArrayIndex + 1;
CGPoint scrollTo = CGPointMake(pageWidth * num, 0);
NSLog(#"scroll to: %#", NSStringFromCGPoint(scrollTo));
[self.collectionView setContentOffset:scrollTo];
}
What It should do is take the value from my main view and move to that image. Unfortunately it does not. I'm not sure why and also I feel like there is a better way of doing this. It just seems sort of Hackish.
How do I connect the two better controller better and what is the correct way of loading the photos/ how do I get to the photo (in the full size detail view) I was on when they were in a grid layout.
Help is appreciated.
OK, there are three parts to this.
First is the UICollectionViewController subclass to display the gallery of photos (UIImage).
Second is the UIPageViewController subclass to manage the swiping from side to side of each individual PhotoViewController.
Third is the UIViewController subclass (PhotoViewController) to display a single photo.
The storyboard will look something like this...
On the left is a UICollectionViewController this has a segue to the UIPageViewController in the middle. On the right is a UIViewController that has an Identifier set in the properties pane (note, there is no segue to this).
Identifier for the PhotoViewController...
In the PhotoPageViewController I have a custom object...
With a Class type PhotoPageModelController set in the properties pane... This is connected as the dataSource of the PhotoPageViewController.
That's pretty much all the storyboard set up required.
So, the first thing to set up is the PhotoPageModelController. This is the dataSource for the PhotoPageViewController as such will dispense subclasses of UIViewController so that the PhotoPageViewController can display them.
The Model Controller
PhotoPageModelController.h
#class PhotoViewController;
#interface PhotoPageModelController : NSObject <UIPageViewControllerDataSource>
// this is the array of the photos. Either an array of UIImages or objects containing
// them or something. My personal project had an array of photoIDs that I could use to
// pull the photos out of Core Data.
// In this example the array will contain instances of UIImage.
#property (nonatomic, strong) NSArray *photos;
- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(PhotoViewController *)controller;
#end
PhotoPageModelController.m
#import "PhotoPageModelController.h"
#import "PhotoViewController.h"
#implementation PhotoPageModelController
- (UIImage *)photoAtIndex:(NSUInteger)index
{
// check that the index is in bounds and then return the UIImage to display.
// In my project I just returned the ID of the photo and let the photo
// controller load the actual image from core data. (See below)
if ([self.photos count] == 0
|| index >= [self.photos count]) {
return nil;
}
return self.photos[index];
}
#pragma mark - convenience methods
- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
UIImage *photo = [self photoAtIndex:index];
if (photo == nil) {
return nil;
}
// This is why we don't have a segue. We are loading it manually
// from the storyboard using the identifier.
EventPhotoViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"PhotoViewController"];
// The model controller is where the PhotoViewController gets the actual image from.
// Or an object containing the image with a name, date, details, etc...
// The controller doesn't know anything about the other photos. Only the one it's displaying.
controller.photo = photo;
return controller;
}
- (NSUInteger)indexOfViewController:(PhotoViewController *)controller
{
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
return [self.photos indexOfObject:controller.photo];
}
#pragma mark - page view data source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
// We need to find the index of the current controller so we can get the index
// and then the view controller for the one before it.
NSUInteger index = [self indexOfViewController:(PhotoViewController *) viewController];
if ((index == 0) || (index == NSNotFound)) {
// We have reached the beginning of the photos array so return nil.
// This tells the Page View Controller that there isn't another page.
return nil;
}
index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
// This is the same as above but going forward instead of backward.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(EventPhotoViewController *) viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.photoIDs count]) {
return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
#end
OK. So that is the Photo Page Model Controller.
The Page View Controller
Next for the PhotoPageViewController.
PhotoPageViewController.h
#import <Foundation/Foundation.h>
#interface PhotoPageViewController : UIPageViewController
#property (nonatomic, strong) NSArray *photos;
#property (nonatomic) NSUInteger initialIndex;
#end
PhotoPageViewController.m
#import "PhotoPageViewController.h"
#import "PhotoPageModelController.h"
#interface PhotoPageViewController ()
// this property is connected in the storyboard
#property (nonatomic, weak) IBOutlet PhotoPageModelController *modelController;
#end
#implementation PhotoPageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.modelController.photos = self.photos;
// We use the initialIndex property to get the first controller and display it.
UIViewController *initialController = (UIViewController *)[self.modelController viewControllerAtIndex:self.initialIndex storyboard:self.storyboard];
[self setViewControllers:#[initialController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:^(BOOL finished) {
}];
// That's it. Because we have the datasource class it makes this class really easy and short.
// It doesn't even need to know anything about the view controllers it is displaying.
// It's just a dispensing machine.
}
#end
The Photo View Controller
Next is the view controller that will display the actual photo.
All it needs is a property of type UIImage called photo and then a UIImageView to place it in. I'll leave this up to you as you can do it many different ways.
I've put a zoomable UIScrollView in mine so that the user can pinch zoom the photo. I've also got some extra info such as the name of the person who took the photo and the date it was taken etc... Set this up however you like.
The collection view segue
The final part (at last) is going from the collection view to the page view controller.
This is done in prepareForSegue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"PhotoSegue"]) {
PhotoPageViewController *controller = segue.destinationViewController;
NSIndexPath *selectedIndex = [self.collectionView indexPathsForSelectedItems][0];
// The PageViewController doesn't need anything except the index to start on...
// i.e. the index of the photo that the user just selected.
controller.initialIndex = (NSUInteger)selectedIndex.item;
// ...and the array of photos it will be displaying.
controller.photos = self.photos;
// Everything else is done by the PageViewController.
}
}

iPad custom/dynamic layout

I am a newbie to iOS development. I have gone through a couple of tutorials and know the basics, but currently I am stuck on how to proceed further. I am planning to create an app for basic home automation (i.e. switching lights, measuring temperature etc.). The backend is all set, so this is just about the frontend. This is what I am planning to do:
The main view of the app should display a floor plan or the layout of the house
On this floor plan you should be able to add lights/sensors/etc. - lets say objects to keep it generic
These objects should be draggable so that you can arrange them on the floor plan according to where they really are (physically) - ideally this drag mode is toggable similar to rearranging icons on the home screen
Each object should have a popover view (i.e. to set the dimmer intensity, switch lights etc.)
I know there is a lot of work to do, but I don't really know how to set this up. Current alternatives:
Create a custom UIView subclass that contains all the logic an do the drawing in custom code, i.e. the dragging, the popover positioning etc. - but I have the feeling that I wouldn't really be leveraging the iOS framework capabilities
Display the floor plan as an UIImageView and one UIButton for each object. This has the advantage that I can use StoryBoard to do the layouting and wiring (i.e. create segues for popovers etc.) - but I simply can't figure out how to do this with a variable number of buttons (since I don't know in advance how many buttons there will be). Is there some way to create these buttons in code?
Use a custom UITableView. I have seen a couple of examples where they seem to use table views even if the layout has nothing to do with tables (like in my example) but I haven't found any tutorials that explain this concept in more detail
Or am I totally on the wrong track? Any input is appreciated.
Thanks
D.
UPDATE:
After some more research and thought on this I think the way to go with iOS 6 is to use an UICollectionView with a custom layout. Once I have come up with a complete solution I will post it here. For older iOS versions I think it would be promising to go with Option Nr. 2 - i.e. creating each UIButton (for the automation objects e.g. lights) in code and having a custom UIView subclass to do the layouting of these buttons.
Ok I think UICollectionView is ideal for this usage scenario and I am just lucky to have started with iOS programming just as it was introduced to the framework. The following example is a UICollectionView that displays its elements according to their inherent coordinates. This example could also be applied to positioning objects on a map. I couldn't find any examples elsewhere so I'll post the main steps here (since I am a beginner please correct any mistakes).
To start off I created a simple project with one view and storyboard in XCode. I removed the standard view and inserted a Collection View Controller instead and configured my UICollectionViewController subclass as the class that should be used (in the properties of the controller in storyboard).
For the demo just set the background of the default UICollectionViewCell to a color and set the Identifier to "AutomationCell" for this example (if you change it be sure to adjust the code below).
First I create a simple object with some properties that represents an object that should be displayed on the floor plan:
#interface AULYAutomationObject : NSObject
#property NSString *title;
#property CGPoint position;
#end
Then I need my own delegate as subclass to the standard UICollectionViewDelegate since my custom UICollectionViewLayout will not have direct access to the dataSource objects. Therefore I provide a method that will give me the position of the object:
#protocol AULYAutomationObjectLayoutDelegate <UICollectionViewDelegate>
- (CGPoint)getPositionForItemAtIndexPath:(NSIndexPath *)indexPath;
#end
Make sure to implement this protocol in your controller like this:
#interface AULYViewController : UICollectionViewController <AULYAutomationObjectLayoutDelegate>
Then I implemented the standard datasource and delegate methods along with my custom one in the view controller subclass:
#interface AULYViewController ()
#property NSArray *objects;
#property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
#end
#implementation AULYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Set up the data source
NSMutableArray *automationObjects = [[NSMutableArray alloc] initWithCapacity:10];
// add some objects here...
self.objects = [automationObjects copy];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
[self.collectionView addGestureRecognizer:longPressRecognizer];
}
#pragma mark - UICollectionViewController
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.objects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
AULYAutomationObjectViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"AutomationCell" forIndexPath:indexPath];
// If you have a custom UICollectionViewCell with a label as outlet
// you could for example then do this:
// AULYAutomationObject *automationObject = self.objects[indexPath.row];
// cell.label.text = automationObject.title;
return cell;
}
#pragma mark - AULYAutomationObjectLayoutDelegate
- (CGPoint)getPositionForItemAtIndexPath:(NSIndexPath *)indexPath
{
AULYAutomationObject *automationObject = self.objects[indexPath.item];
return automationObject.position;
}
In a real project you would probably do some conversion from the object model position to the position on screen (e.g. GPS data to pixels) but here this is left out for simplicity.
After having done that we still need to set up our layout. This has the following properties:
#interface AULYAutomationObjectLayout : UICollectionViewLayout
#property (nonatomic, strong) NSIndexPath *draggedObject;
#property (nonatomic) CGPoint dragPosition;
#end
And the following implementation:
#implementation AULYAutomationObjectLayout
- (void)setDraggedObject:(NSIndexPath *)draggedObject
{
_draggedObject = draggedObject;
[self invalidateLayout];
}
- (void)setDragPosition:(CGPoint)dragPosition
{
_dragPosition = dragPosition;
[self invalidateLayout];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
id viewDelegate = self.collectionView.delegate;
if ([viewDelegate respondsToSelector:#selector(getPositionForItemAtIndexPath:)])
{
CGPoint itemPosition = [viewDelegate getPositionForItemAtIndexPath:indexPath];
layoutAttributes.center = itemPosition;
layoutAttributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
}
if ([self.draggedObject isEqual:indexPath])
{
layoutAttributes.center = self.dragPosition;
layoutAttributes.transform3D = CATransform3DMakeScale(1.5, 1.5, 1.0);
layoutAttributes.zIndex = 1;
}
return layoutAttributes;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [[NSMutableArray alloc] initWithCapacity:4];
for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[allAttributes addObject:layoutAttributes];
}
return allAttributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (CGSize)collectionViewContentSize
{
return [self.collectionView frame].size;
}
#end
To set the custom layout in the storyboard just go to the properties of the controller view and select custom as the layout type - then select your custom class.
Now to enable drag and drop support with the long press gesture simply add the following to your controller:
- (void)handleTapGesture:(UITapGestureRecognizer *)sender
{
AULYAutomationObjectLayout *automationLayout = (AULYAutomationObjectLayout *)self.collectionView.collectionViewLayout;
if (sender.state == UIGestureRecognizerStateBegan)
{
CGPoint initialPinchPoint = [sender locationInView:self.collectionView];
NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint];
[self.collectionView performBatchUpdates:^{
automationLayout.draggedObject = tappedCellPath;
automationLayout.dragPosition = initialPinchPoint;
} completion:nil];
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
automationLayout.dragPosition = [sender locationInView:self.collectionView];
}
else if (sender.state == UIGestureRecognizerStateEnded)
{
AULYAutomationObject *automationObject = self.objects[automationLayout.draggedObject.item];
automationObject.position = [sender locationInView:self.collectionView];
[self.collectionView performBatchUpdates:^{
automationLayout.draggedObject = nil;
automationLayout.dragPosition = CGPointMake(0.0, 0.0);
} completion:nil];
}
}
One important note:(this cost me at least an hour): When using the transform3D you should make sure to import QuartzCore into your linked frameworks (in the project properties below the orientation settings). Otherwise you will get a Mach-O Linker Error saying that _CATransform3DMakeScale can not be found.

Passing variables (or similar) to newly loaded view

I am loading new views for a small iphone app, and was wondering how to pass details from one to another?
I am loading a tableview full of data from and xml file, then once clicked a new view is brought in via:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SubInfoViewController *subcontroller = [[SubInfoViewController alloc] initWithNibName:#"SubInfoView" bundle:nil];
[self presentModalViewController:subcontroller animated:YES];
[subcontroller release];
}
Next step would be to tell the newly loaded view which row had just been loaded?
Any idea, thoughts more than welcome, and please be gentle big newbie...
I typically create my own init method to do things like this. I think it would likely be better to pass in the corresponding "model" object represented by the tableView row, rather than the row number itself, like this:
In SubInfoViewController.h
#interface SubInfoViewController : UIViewController {
YourObject *yourObject;
}
#property (nonatomic, retain) YourObject *yourObject;
Then in SubInfoViewController.m:
- (SubInfoViewController*)initWithYourObject:(YourObject*)anObject {
if((self = [super initWithNibName#"SubInfoView" bundle:nil])) {
self.yourObject = anObject;
}
return self;
}
You'd create and present it this way:
// assuming you've got an array storing objects represented
// in the tableView called objectArray
SubInfoViewController *vc = [[SubInfoViewController alloc] initWithYourObject:[objectArray objectAtIndex:indexPath.row]];
[self presentModalViewController:vc animated:YES];
[vc release];
This could be adapted pretty easily to allow you to pass in any type of object or value (such as a row number if you still want to do that).
Add an instance variable to your view controller and declare a property corresponding to it, so after you alloc, init it, set it like subcontroller.foo = Blah Blah.