Mapkit custom callout slide view from bottom - ios

I have a map which has multiple Annotations on it. When I click one I want to slide a small UIView from the bottom with some information about this pin. If I click on a button called more I want the view to expand upwards with more information in it.
I'm not sure how to do this the right way. My idea is to define a new xib and view controller for the custom callout. But I only want to display a small portion of that view so should I set its frame from the beginning to a fixed small value or rather use autolayout to shrink it to its content? And how can I slide it up from the bottom? I think I need to show it either modally or as a childviewcontroller and use some animation.
When the user clicks on more I would use another DetailViewController with a xib file which either would be shown also modally or also as a childviewcontroller. Is there any example how to achieve something like this? Or are there any better ways to do this?
For now the custom callout view would be the same for each Annotation. But I would have three different DetailViewControllers depending on which Annotation was clicked.
My basic idea is to separate these things so my MapViewController won't get too big.

I wouldn't use a view controller for the slide-in view. Just create a UIView with a frame that's just outside of your main view frame, and make it a subview. Like this:
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 200)];
[self.view addSubview:v];
self.infoView = v;
You can do that in viewDidLoad, or wherever you want in the initial setup of your view. It won't be visible because it's just off the bottom of the screen. Then when the user taps the callout button, animate your infoView into position like this:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// Populate infoView with whatever info you want to show about the annotation
//
// ...
//
// Then animate the view into position
[UIView beginAnimations:#"infoView" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
self.infoView.center = CGPointMake(0, self.view.frame.size.height - 200);
[UIView commitAnimations];
}

Related

iOS - Insert subview between the background and toolbar of another view

Pardon my ugly illustration below:
I have a view controller A that has a toolbar T at the bottom; toolbar is part of controller A, created by choosing "Opaque Toolbar" as "Bottom Bar" in the Attributes Inspector of view controller A. I'd like a banner B to slide up from the top of toolbar T, stay there for 2 seconds then disappear. Banner B is not within the view hierarchy of controller A. The issue right now is that the banner covers the toolbar during the sliding up process; I'd like the notification banner to emerge from the top of the toolbar, initially completely covered by the toolbar, instead of covering the toolbar.
Changing the layer index of the banner doesn't work, because the banner is not within the layer system of the view below it.
Here's my code snippet that adds the banner and animates it:
[containerView addSubview:innerView];
// Animate In
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[innerView setFrame:target];
} completion:^(BOOL finished) {
if (finished) {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[innerView addGestureRecognizer:tapGestureRecognizer];
}
}];
Is there a way to insert the banner so that it appears above the background of controller A but below the toolbar of the controller?
Maybe you can try :
[self.view bringSubviewToFront : toolbarView]; //Top level
[self.view insertSubview:bannerView belowSubview: toolBarView]; //below your toolbar
I assume your containerView is the view of a UINavigationController, as you cannot add a toolbar in the attributes inspector for UIViewController. I would suggest you just add the banner to self.view
[self.view addSubview:innerView];
If you feel like this will not work. You should probably subclass UINavigationController and add the subview of it.
[self.view insertSubview:bannerView belowSubview: self.toolbar]; //toolbar is a property of UINavigationController
I suppose it's possible to say something like:
[self.navigationController.view insertSubview:bannerView belowSubview: self.navigationController.toolBarView];
Generally you want view controllers to manager their own view hierarchy. So it is better to avoid the third options.

PKRevealController dim the hidden controller

Does anyone has an idea if PKRevealController has a method to add an overlay (dim) effect to the hidden controller when the right/left view slides in ?
https://github.com/pkluz/PKRevealController
Like I saw inside the example code, you can check which kind of state you have for a specific UIViewController. Can't you add a new UIView make it black with alpha:0.6 and set it hidden. If the UIViewController did change to the state in background, animate the hidden View like
[UIView animateWithDuration:0.5 animations:^{
[OverlayView setHidden:NO];
}];
Inside viewDidAppear you can check if your OverlayView is hidden:YES and animate it to NO.

uitableview with header like instagram user profile

I've been struggling with this for quite a while now.
I have to implement an user profile similar to what Instagram has in their ios app.
When clicking on the first to buttons on that tab bar like thing all the contents downwards from it changes. The tableview that is displayed on the bottom part has dynamic size so they keep account of that also.
I have something implemented where the top part is a UIView with 5 buttons and based on them the bottom part (witch is like a container view) changes content. And these two (top uiview and bottom container view) are part of UIScrollView. But this way I can't get information back in time on the size about the tableview's size that I want to display in the bottom part in order to modify the UIScrollView's size. And I have a feeling this is a flawed way to do it.
I would really appreciate any ideas oh how to implement this king of interaction. Thank you.
I believe it's a headerView on a UITableView or a UICollectionView, depending on which view mode you have selected. When you tap one of the buttons it changes out the UITableView to a UICollectionView or vice versa.
You want to keep track of the current contentOffset for whichever is being displayed (UICollectionView and UITableView are both subclasses of UIScrollView so you will be able to get this from both) and then set the contentOffset on the view you're switching to.
Setup an ivar for the UIView header subclass so you can easily re-use it.
This is what I have. My problem is that I'm mot getting back in useful time the tableview's frame height from the tableview controller to the UserProfileViewController in order to change the latter's scrollview size. I also feel that I'm somehow doing this backwards so any suggestions are more than welcome.
This view has two parts: an upper part and a lower part. The parent view is a scroll view. What I wanted to achieve with this is having a sort of tab bar in the upper part that will controll waht will appear in the lower part.
The upper part has a flip animation when the upper left button is pressed to reveal another view.
The way this is achieved is by having 2 views: a dummy view and the back view. The dummy view has the front view as a child. The front view is the one that containes all the buttons.
The code for this animation is achieved in this way:
- (IBAction)infoButtonPressed:(id)sender
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.hoverView cache:YES];
if ([self.headerView superview]) {
[self.headerView removeFromSuperview];
[self.hoverView addSubview:self.backOfHeaderView];
[self.infoButton removeFromSuperview];
[self.backOfHeaderView addSubview:self.infoButton];
} else {
[self.backOfHeaderView removeFromSuperview];
[self.hoverView addSubview:self.headerView];
[self.infoButton removeFromSuperview];
[self.headerView addSubview:self.infoButton];
}
[UIView commitAnimations];
}
The lower part is made out of a container view that acts as a place holder.
When a button is pressed a different view controller is displayed in the container view.
Each view controller has a container view of it's own. The specific view of that view controller (tableview) is added to it's container view when the controller is loaded. It also makes sure that if the tableview is already added to the container view it will be removed. All this is done in each specific view controller.
In the view controller of the User Profile view there is an instance of the container view and one of a UIViewController that also acts as a placeholder(named currentViewController from now on). When a specific button is pressed it checks if the an instance of the view controller that we want to display already exists. If not it will make one and will set it's tableview's frame to the bounds of the container view. After that it will remove the currentViewController's view from the superview and the currentViewController itself from the parent viewcontroller to make sure that if there is something assigned to these they will not be there. Then it goes and assigns the desired viewcontroller to the currentViewController. It also assigns the desired viewcontroller's containerView instance to the containerview in the parent viewcontroller (the User Profile viewcontroller). At the end it will add the desired viewcontroller as a child to the main viewcontroller (the User Profile viewcontroller) and desired viewcontroller's view to the containerView of the main viewcontroller.
This is the code for one of the buttons:
//Check if there is an instance of the viewcontroller we want to display. If not make one and set it's tableview frame to the container's view bounds
if(!_userWallViewController) {
self.userWallViewController = [[WallViewController alloc] init];
// self.userWallViewController.activityFeedTableView.frame = self.containerView.bounds;
}
[self.userWallViewController.containerView addSubview:self.userWallViewController.activityFeedTableView];
//If the currentviewcontroller adn it's view are already added to the hierarchy remove them
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
//Add the desired viewcontroller to the currentviewcontroller
self.currentViewController = self.userWallViewController;
//Pass the data needed for the desired viewcontroller to it's instances
self.userWallViewController.searchURLString = [NSString stringWithFormat:#"event/user/%#/", self.userID];
self.userWallViewController.sendCommentURLString = [NSString stringWithFormat:#"event/message/%#", self.userID];
self.userWallViewController.totalCellHeight = ^(float totalCellHeight){
self.userWallViewController.numberOfCells = ^(float numberOfCells){
NSLog(#"The total number of cells: %f", numberOfCells);
NSLog(#"The total cell height: %f", totalCellHeight);
self.scrollView.contentSize = CGSizeMake(320.0, totalCellHeight + 172.0 + 33.0);
CGRect newFrame = self.userWallViewController.containerView.frame;
newFrame.size.height = totalCellHeight + 33.0;
self.userWallViewController.containerView.frame = newFrame;
NSLog(#"Container view: %f", self.containerView.frame.size.height);
NSLog(#"Scroll view: %f",self.scrollView.contentSize.height );
};
};
//Add this containerview to the desired viewcontroller's containerView
self.userWallViewController.containerView = self.containerView;
//Add the needed viewcontroller and view to the parent viewcontroller and the containerview
[self addChildViewController:self.userWallViewController];
[self.containerView addSubview:self.userWallViewController.view];
[self performSelector:#selector(changeScrollView) withObject:self afterDelay:0.5];
//CLEAN UP THE CONTAINER VIEW BY REMOVING THE PREVIOUS ADDED TABLE VIEWS
[self.userFansViewController.userSimpleTableView removeFromSuperview];
[self.fanOfViewController.userSimpleTableView removeFromSuperview];
[self.userPublishedMovellaListViewController.gridView removeFromSuperview];
[self.userPublishedMovellaListViewController removeFromParentViewController];
self.userPublishedMovellaListViewController = nil;
}
I know this answer is over a year late, but I wanted to state my hypothesis on it...just incase it might help someone else later. Im implementing a similar view and came to this conclusion. Anyone is welcomed to correct me if I'm wrong.
I think that perhaps the top view is a header view and the two options that seem like a collection view and a table view are both collection views.
Because the layout of collection views can be fine tuned to the most minute details, I think the view that looks like a table view is just a really specifically designed collection view. And when switching between the views, the collection view's data and properties are being swapped and reloaded.

Animated Subviews

I have a split view controller in my Ipad app, in which the detail view has subviews which are UITableView's.
The functionality I am trying to implement is to get an info view at the same position of a subview when a button(info button) on the subview is pressed. I need to get this info view by animation and that animation is that when the info button is pressed, that subview alone would flip (UIAnimationOptionFlipFromRight) and the info view would show...
This is how try to implement it -
-(void) showInfoView: (id)sender
{
infoView = [[InfoViewController alloc] initWithNibName:#"ViewViewController" bundle:nil];
infoView.view.frame = CGRectMake(250, 300, 200, 200);
[UIView transitionWithView:self.view duration:1
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self.view addSubview:infoView.view];
}
completion:nil];
}
When I run the simulator and press the info button on any subview, what happens is that the animation happens perfectly, i.e. the subview flips from the right but the infoView is not getting displayed.
It would be great if someone could tell me where I am going wrong here.
The basic steps of performing an animation to onscreen is as follows:
Create the view.
Move it to the initial (offscreen) position
Add to the view hierarchy
Perform the animation.
It seems like you're skipping step 3 and possibly step 2.
Did you try setting the subview frame to the frame of your superview, i.e
info.View.frame = self.view.frame;

How to present datepicker only upon click of a button

I need to know if a date picker can be presented like an alert view upon the click of a button. So far, I have hidden the date picker in viewdidload method and set hidden=no in the method for the button.
I intend to store the date within Core Data.
Create the pickerView, place it outside of your view bounds, add it as a subview and use animation to slide it up. Something like the below should work...
CGRect frame = CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 215);
UIPickerView *typePicker = [[UIPickerView alloc] initWithFrame:frame];
[self.view addSubview:typePicker];
[UIView beginAnimations:#"slideIn" context:nil];
[typePicker setCenter:CGPointMake(typePicker.center.x, self.view.frame.size.height - typePicker.frame.size.height/2)];
[UIView commitAnimations];
You put the date picker in a subview and then present the subview at the click of a button. You probably want the subview to slide in from the side or bottom.

Resources