How to create a dropdown tableview like Vine App - ios

I'm wondering how one would go about programming the kind of dropdown tableview that the Vine app uses. If you have never used Vine, I've provided a picture below which depicts the UI Design I'm talking about. Essentially, when you press the left hand UIBarButton, this tableview drops down. When you touch anywhere again, it drops down a little further (5 or 10 pixels) and then leaves the screen with a nice animation.
Just looking for some feedback on how I might go about implementing this. Thanks in advance.

Not sure a UITableView is the way to go about it.
Perhaps you can use REMenu available on Github to get inspired or fork it to customize to your needs.

The REMenu is as close to an exact copy as you can get. I did notice though that it wasn't clipping the top of the menu when it slid down, it slid underneath the status / nav bar which to me didn't look right. Without having looked at the sliding logic (and with my impressive SE reputation of "8"), this is my quick take on how you make the menu appear.
create a view for the contents of the menu (the table view etc)
put it in an enclosing menu collapsed to a zero height, with the content sticking off the top of the collapsed menu view
set the menu view to clip the contents so the top of the menu is not visible, then animate the contents down, as you animate the menu height larger.
This sample uses a simple gradient for the contents of the menu.
#interface BackgroundLayer : NSObject
+(CAGradientLayer*) redBlueGradient;
#end
#implementation BackgroundLayer
+ (CAGradientLayer*) redBlueGradient
{
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors =
#[(id) [UIColor redColor].CGColor, (id) [UIColor blueColor].CGColor];
headerLayer.locations = nil;
return headerLayer;
}
#end
#interface ViewController ()
#property (nonatomic, strong) UIButton* doIt;
#property (nonatomic, strong) UIView* menu;
#property (nonatomic, strong) UIView* nestedView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create simple toggle button to test the menu
self.doIt = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.doIt.frame = CGRectMake(50, 50, 50, 44);
[self.doIt setTitle:#"Doit!" forState:UIControlStateNormal];
[self.doIt sizeToFit];
[self.doIt addTarget:self action:#selector(doIt:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.doIt];
// menu
self.menu = [[UIView alloc] initWithFrame:CGRectMake(20, 200, 280, 0)];
self.menu.layer.borderColor = [UIColor blackColor].CGColor;
self.menu.layer.borderWidth = 3.0;
self.menu.clipsToBounds = YES;
// menu contents
self.nestedView = [[UIView alloc] initWithFrame:CGRectMake(0, -100, 280, 100)];
CAGradientLayer *background = [BackgroundLayer redBlueGradient];
background.frame = self.nestedView.bounds;
[self.nestedView.layer addSublayer:background];
[self.nestedView clipsToBounds];
[self.menu addSubview:self.nestedView];
[self.view addSubview:self.menu];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) doIt:(id) sender
{
if (!CGRectEqualToRect(self.nestedView.frame, CGRectMake(0, 0, 280, 100)))
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 100);
self.nestedView.frame = CGRectMake(0, 0, 280, 100);
}];
}
else
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 0);
self.nestedView.frame = CGRectMake(0, -100, 280, 100);
}];
}
}
#end
Cheers.

The Problem with the REMenu is, it creates the viewControllers every time the user taps on particular section, which should not be the case. It should persist the state of each screen attached there.

Related

UIButton in a custom UIView is not being triggered when tapped upon

I've placed a UIButton inside a custom UIView, CustomButtonView. The UIButton is not responding to tap/click events, and its appropriate selector method is not being triggered. Here's my code below; what have I done wrong?
As well, the custom view has a background color of red. When I add the custom view to my view controller's view, the red rectangle displays. However, when I add my UIButton to my custom view (with the background color of red), only the UIButton displays, not the red rectangle. Why is this happening?
//
// CustomButtonView.h
//
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface CustomButtonView : UIView
#property (strong, nonatomic) UIImageView *buttonImageView;
#property (strong, nonatomic) UIButton *button;
#end
NS_ASSUME_NONNULL_END
//
// CustomButtonView.m
//
#import "CustomButtonView.h"
#implementation CustomButtonView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor redColor];
_button = [UIButton buttonWithType:UIButtonTypeSystem];
_button.backgroundColor = [UIColor greenColor];
[_button setTitle:#"john doe" forState:UIControlStateNormal];
[_button setTitle:#"john doe" forState:UIControlStateHighlighted];
_button.translatesAutoresizingMaskIntoConstraints = NO;
_button.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
_button.titleLabel.textColor = [UIColor whiteColor];
self.userInteractionEnabled = NO;
self.button.userInteractionEnabled = YES;
//[self addSubview:_button];
//[_button.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
//[_button.leadingAnchor constraintEqualToAnchor:self.leadingAnchor].active = YES;
}
return self;
}
#end
//
// ViewController.m
// TestApp
//
//
#import "ViewController.h"
#import "CustomButtonView.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor yellowColor];
_customButtonView = [[CustomButtonView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
_customButtonView.backgroundColor = [UIColor redColor];
_customButtonView.translatesAutoresizingMaskIntoConstraints = NO;
[_customButtonView.button addTarget:self action:#selector(customButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_customButtonView];
}
- (void)customButtonTapped:(id)sender{
NSLog(#"Button tapped!");
}
#end
Here are some screenshots, below. Please click on the links.
When I display the UIButton inside the custom-view, only the button displays. The red rectangle of the custom-view doesn't display.
When I simply add the custom-view to my view-controller (ie with no UIButton added to the custom view), the red rectangle of the custom-view displays.
When you disable user interaction for a specific view then this user interaction is not forwarded to its subviews. Or in other words; for user interaction to work you need to make sure that all superviews in chain (superview of superview of superview) do have user interaction enabled.
In your case you have self.userInteractionEnabled = NO; which means that any subview of self (including your button) will not accept any user interaction.
Next to this issues it is still possible that other issues are present. For instance: A subview will accept user interaction only within physical area of all of its superviews. That means that if a button with frame { 0, 0, 100, 100 } is placed on a superview of size { 100, 50 } then only the top part of the button will be clickable.
As for the size changes this is a completely different story...
You decided to disable translation of autoresizing mask on your custom view by writing _customButtonView.translatesAutoresizingMaskIntoConstraints = NO;. That means that you will not use frame in conjunction with other constraints. It means that whatever frame you set (in your case CGRectMake(0, 0, 100, 100)) this frame will be overridden by anything else that may effect the frame of your custom view.
In case where your button code is commented out there is no such thing that "may effect the frame of your custom view" and the view stays as size of { 100, 100 }. But as soon as you added a button you added additional constraints which most likely lead to resizing your button to sizeToFit and with it its superview which is your custom view.
To avoid this you need to either remove disabling of translation of autoresizing mask into constraint on your custom view. Or you need to define custom view size by adding constraints to it so that they are set to { 100, 100 }. You can simply constrain heightAnchor and widthAnchor to 100.

Add buttons to the left and right of the searchbar

I want a searchbar that has a button on the left of the text field, and on the right. Identical to the searchbar found on the current Alien Blue reddit app.
So far, the closest thing I have to achieving this is that I have been able to change the text of the "Cancel" button on the right side using this line of code.
[self.searchController.searchBar setValue:#"Hello" forKey:#"_cancelButtonText"];
But that doesn't really help since I want the right side to open up different search options, not cancel the search.
Help me ____, you're my only hope
Put the effect first
//RWSearchBar.h
#define BarFrame CGRectMake(100, 20, 200, 44)
#import <UIKit/UIKit.h>
#interface RWSearchBar : UISearchBar
#end
//RWSearchBar.m
#import "RWSearchBar.h"
#implementation RWSearchBar
- (void)layoutSubviews {
self.frame = BarFrame;
}
#end
//RWViewController.m
#import "ViewController.h"
#import "RWSearchBar.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
RWSearchBar *searchBar = [[RWSearchBar alloc] init];
[self.view addSubview:searchBar];
CGRect aFrame = BarFrame;
UIButton *leftBtn = [[UIButton alloc] initWithFrame:CGRectMake(aFrame.origin.x - 60, aFrame.origin.y, 60, 44)];
leftBtn.backgroundColor = [UIColor blueColor];
UIButton *rightBtn = [[UIButton alloc] initWithFrame:CGRectMake(aFrame.origin.x + aFrame.size.width, aFrame.origin.y, 60, 44)];
rightBtn.backgroundColor = [UIColor yellowColor];
[self.view addSubview:leftBtn];
[self.view addSubview:rightBtn];
}
You can custom more visual effect by yourself.

Overlapping transparent UIViews

I’m overlaying two UIViews with a white backgroundColor at 25% opacity. In a small part, they overlap each other, meaning that at that area, they are summed to 50% opacity.
I’d like to keep that 25% opacity, even if the two views overlap, effectively meaning that in those overlapped points, each view’s opacity drops to 12.5% to total 25%.
I’ve done a little looking into compositing but I’m not sure which of these modes would help, or how I’d go about applying them to a specific part of these two UIView instances.
(http://docs.oracle.com/javase/tutorial/2d/advanced/compositing.html is what I was reading, and I found the CGBlendMode for drawing, if it comes to using that (though I’d prefer not to if possible!))
You can't control the compositing mode of views (or, really, CALayers) on iOS.
The best solution I can think of here is to leave both views with a clearColor (or nil) background, and use a single CAShapeLayer to draw the background of both. If your two views have the same parent, it's not too hard.
Let's say the parent is of type ParentView. Override layoutSubviews in ParentView to create and update the backdrop layer as necessary. Be sure to send setNeedsLayout to the parent view if you move either of the child views.
ParentView.h
#import <UIKit/UIKit.h>
#interface ParentView : UIView
#property (nonatomic, strong) IBOutlet UIView *childView0;
#property (nonatomic, strong) IBOutlet UIView *childView1;
#end
ParentView.m
#import "ParentView.h"
#implementation ParentView {
CAShapeLayer *backdrop;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutBackdrop];
}
- (void)layoutBackdrop {
[self createBackdropIfNeeded];
[self arrangeBackdropBehindChildren];
[self setBackdropPath];
}
- (void)createBackdropIfNeeded {
if (backdrop == nil) {
backdrop = [CAShapeLayer layer];
backdrop.fillColor = [UIColor colorWithWhite:1 alpha:0.25].CGColor;
backdrop.fillRule = kCAFillRuleNonZero;
backdrop.strokeColor = nil;
}
}
- (void)arrangeBackdropBehindChildren {
[self.layer insertSublayer:backdrop atIndex:0];
}
- (void)setBackdropPath {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.childView0.frame];
[path appendPath:[UIBezierPath bezierPathWithRect:self.childView1.frame]];
backdrop.path = path.CGPath;
}
#end
If you add them both to the same parent UIView, tell that UIView to rasterize, then set the alpha on the parent, you'll get the desired effect. I'm not sure if that fits with your display structure or performance needs.
UIView *parent = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[parent setBackgroundColor:[UIColor clearColor]];
[parent.layer setShouldRasterize:YES];
[parent.layer setRasterizationScale:[[UIScreen mainScreen] scale]];
[parent setAlpha:0.25];
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 125, 125)];
[subview1 setBackgroundColor:[UIColor whiteColor]];
[parent addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectMake(75, 75, 125, 125)];
[subview2 setBackgroundColor:[UIColor whiteColor]];
[parent addSubview:subview2];

Making a list of UIViews that slide up and down when touched

I'm trying to figure out an approach to build something like the image below, which is a list of items that when a section is clicked slides out content. It's a really common UX on most websites and what not. My idea is to have each gray box (button) slide out a UIView containing some other items. I'm still new to iOS development but I'm struggling to find how you can animate a UIView to slide down and push the content below it down as well. Hoping some one can give me a good starting point or point to some info outside the realm of the apple docs.
Thanks!
So if you just have a few views, I would not recommend the UITableView approach, since it is not so easy to customize with animations and table views usually want to fill the whole screen with cells. Instead write a expandable UIView subclass that has the desired two states. Add a method to switch between extended and collapsed state. On expanding/collapsing adjust their positions so that they always have enough space.
I provide you an example of views adjusting their frames. I guess it should be easy to do the same with auto layout constraints: give the views a fixed height constraint and change this on collapsing/expanding. The same way set the constraints between the views to be 0 so that they are stacked on top of each other.
Expandable View:
#interface ExpandingView(){
UIView *_expandedView;
UIView *_seperatorView;
BOOL _expanded;
}
#end
#implementation ExpandingView
- (id)init
{
self = [super initWithFrame:CGRectMake(15, 0, 290, 50)];
if (self) {
_expanded = NO;
self.clipsToBounds = YES;
_headerView = [[UIView alloc] initWithFrame:self.bounds];
_headerView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self addSubview:_headerView];
_seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-1, self.bounds.size.width, 1)];
_seperatorView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:_seperatorView];
_expandedView = [[UIView alloc] initWithFrame:CGRectOffset(self.bounds, 0, self.bounds.size.height)];
_expandedView.backgroundColor = [UIColor blueColor];
[self addSubview:_expandedView];
}
return self;
}
- (void)layoutSubviews{
[self adjustLayout];
}
- (void)adjustLayout{
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, 50);
_seperatorView.frame = CGRectMake(0, 49, self.bounds.size.width, 1);
_expandedView.frame = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height-50);
}
- (void)toggleExpandedState{
_expanded = !_expanded;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _expanded?200:50);
[self adjustLayout];
}
#end
ViewController:
#interface ExpandingViewController (){
NSArray *_expandingViews;
}
#end
#implementation ExpandingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_expandingViews = #[
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
];
for(ExpandingView *view in _expandingViews){
[view.headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(expandingViewTapped:)]];
[self.view addSubview:view];
}
}
- (void)viewWillLayoutSubviews{
int y = 100;
for(ExpandingView *view in _expandingViews){
view.frame = CGRectOffset(view.bounds, (CGRectGetWidth(self.view.bounds)-CGRectGetWidth(view.bounds))/2, y);
y+=view.frame.size.height;
}
}
- (void)expandingViewTapped:(UITapGestureRecognizer*)tapper{
ExpandingView *view = (ExpandingView*)tapper.view.superview;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:0 animations:^{
[view toggleExpandedState];
[self.view layoutIfNeeded];
} completion:nil];
}

subclassing UINavigationController and adding a sliding menu

My goal: To have a sliding menu that will push the current view of my app when it opens.
I've subclassed my UINavigationController in order to create custom "back" buttons and the sliding from the right menu. To do so, inside my subclassed UINavigationController i'm changing the x origin of the current UIViewController (self.view.fram) to a value that will move it to the left x = -100; and by doing so i'm exposing the right menu.
After completing the above I've found out that by moving self.view I'm also moving the touchable area so the button on the right menu won't be accesible \ clickable.
I've read a lot about pointInside and hitTest that can help me achieve what i want but still couldn't implement it in a way that will fit my needs.
I'm aware that there are a 1000 open source projects on github that do exactly what I want but 'd like to write it my self and understand it better.
Thanks.
NavControllerSubCls.h:
#interface NavControllerSubCls : UINavigationController <UINavigationControllerDelegate>
#property (nonatomic, strong) UIView *testView;
#property (nonatomic, strong) UIButton *rightMenuBtn;
#end
NavControllerSubCls.m:
-(void)viewDidLoad
{
[super viewDidLoad];
UIView *navBar = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 41)];
navBar.backgroundColor = [UIColor blueColor];
UIImageView *bkrNavBar = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 320, 39)];
[navBar addSubview:bkrNavBar];
_rightMenuBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[_rightMenuBtn setFrame:CGRectMake(240, 20, 52, 19)];
[_rightMenuBtn setTitle:#"Click" forState:UIControlStateNormal];
[_rightMenuBtn addTarget:self action:#selector(showRightMenu) forControlEvents:UIControlEventTouchUpInside];
[navBar addSubview:_rightMenuBtn];
_testView = [[UIView alloc]initWithFrame:CGRectMake(300, 20, 120, 100)];
_testView.backgroundColor = [UIColor greenColor];
UIButton *testBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testBtn.frame = CGRectMake(0, 0, 120, 100);
[testBtn addTarget:self action:#selector(doSomething) forControlEvents:UIControlEventTouchUpInside];
[_testView addSubview:testBtn];
[self.view addSubview:_testView];
[self.view addSubview:navBar];
[self.view insertSubview:_testView aboveSubview:self.view];
}
-(void)doSomething
{
NSLog(#"I'm Working!");
}
- (void)showRightMenu
{
CGRect frame = self.view.frame;
frame.origin.x = -100;
self.view.frame = frame;
}
You mean like the Facebook app? I think a better design would be to have a parent view controller that manages the sliding and two child view controllers, one for the menu and one for the content.
EDIT
The SlidingViewController has two child view controllers: a ContentViewController that takes up all the space and a MenuViewController underneath the ContentViewController.
The SlidingViewController only manages the sliding interaction, whether that's coming from a pan gesture or from a method call. When it's time to show the menu, the SlidingViewController will shift the ContentViewController so that the MenuViewController is visible (by changing the transform or frame property).
The ContentViewController and MenuViewController are just containers for the real view controllers. You use these containers so that you can do whatever you want to them (say if you want to do some really funky animation) without affecting the real view controllers.
So the view controller hierarchy would look like this:
SlidingViewController
/ \
MenuViewController ContentViewController
| |
UITableViewController, etc. UINavigationController, etc.

Resources