代码改变世界

iOS 新浪微博-2.0 搜索框/标题带箭头/下拉菜单

2015-10-06 00:51  jiangys  阅读(1859)  评论(0编辑  收藏  举报

不管是搜索框还是下拉菜单,我们都需要对背景的图片进行拉伸。定义一个Category分类对图片进行操作。

UIImage+Effect.h

#import <UIKit/UIKit.h>

@interface UIImage (Effect)

/**
 *  返回一张可以随意拉伸不变形的图片
 *
 *  @param name 图片名字
 */
+ (UIImage *)imageToResizable:(NSString *)name;

@end

UIImage+Effect.m

#import "UIImage+Effect.h"

@implementation UIImage (Effect)

/**
 *  返回一张可以随意拉伸不变形的图片
 *
 *  @param name 图片名字
 */
+ (UIImage *)imageToResizable:(NSString *)name
{
    UIImage *normal = [UIImage imageNamed:name];
    CGFloat w = normal.size.width * 0.5;
    CGFloat h = normal.size.height * 0.5;
    return [normal resizableImageWithCapInsets:UIEdgeInsetsMake(h, w, h, w)];
}

@end

搜索框

由于系统自带的不符合我们的要求,自定义一个搜索框

思路:

继承UITextField,设置背景图片,左边显示的图标。

SearchBar.h

#import <UIKit/UIKit.h>

@interface SearchBar : UITextField

+(instancetype)searchBar;

@end

SearchBar.m

#import "SearchBar.h"
#import "UIImage+Effect.h"

@implementation SearchBar

-(instancetype)initWithFrame:(CGRect)frame
{
    if (self=[super initWithFrame:frame]) {
        self.font=[UIFont systemFontOfSize:15];
        self.placeholder=@"请输入搜索条件";
        self.background =[UIImage imageToResizable:@"searchbar_textfield_background"];
        
        UIImageView *searchIcon=[[UIImageView alloc]init];
        searchIcon.image=[UIImage imageNamed:@"searchbar_textfield_search_icon"];
        searchIcon.height=30;
        searchIcon.width=30;
        // 保持图片原来的尺寸
        searchIcon.contentMode=UIViewContentModeCenter;
        self.leftView=searchIcon;
        self.leftViewMode=UITextFieldViewModeAlways;
    }
    return self;
}

+(instancetype)searchBar
{
    return [[self alloc]init];
}

@end

使用时候很简单:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    SearchBar *searchBar=[SearchBar searchBar];
    searchBar.width=300;
    searchBar.height=30;
    self.navigationItem.titleView=searchBar;
}

标题带箭头

  

思路:

  • 自定义按钮
  • 原生的按钮,箭头是在左边,文字在右边,通过在加载layoutSubviews,调整文字的x值是箭头的x值,箭头的x值是文字的最大x值。
  • 每次文字更改,重新计算文字的size

TitleButton.h

#import <UIKit/UIKit.h>

@interface TitleButton : UIButton

@end

TitleButton.m

//
//  TitleButton.m
//  Weibo
//
//  Created by jiangys on 15/10/8.
//  Copyright © 2015年 Jiangys. All rights reserved.
//

#import "TitleButton.h"

@implementation TitleButton

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        self.titleLabel.font = [UIFont boldSystemFontOfSize:17];
        [self setImage:[UIImage imageNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal];
        [self setImage:[UIImage imageNamed:@"navigationbar_arrow_up"] forState:UIControlStateSelected];
    }
    return self;
}

// 目的:想在系统计算和设置完按钮的尺寸后,再修改一下尺寸
/**
 *  重写setFrame:方法的目的:拦截设置按钮尺寸的过程
 *  如果想在系统设置完控件的尺寸后,再做修改,而且要保证修改成功,一般都是在setFrame:中设置
 */
- (void)setFrame:(CGRect)frame
{
    frame.size.width += 5;
    [super setFrame:frame];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    // 如果仅仅是调整按钮内部titleLabel和imageView的位置,那么在layoutSubviews中单独设置位置即可
    
    // 1.计算titleLabel的frame
    self.titleLabel.x = self.imageView.x;
    
    // 2.计算imageView的frame
    self.imageView.x = CGRectGetMaxX(self.titleLabel.frame) + 5;
}

- (void)setTitle:(NSString *)title forState:(UIControlState)state
{
    [super setTitle:title forState:state];
    
    // 只要修改了文字,就让按钮重新计算自己的尺寸
    [self sizeToFit];
}

- (void)setImage:(UIImage *)image forState:(UIControlState)state
{
    [super setImage:image forState:state];
    
    // 只要修改了图片,就让按钮重新计算自己的尺寸
    [self sizeToFit];
}

@end

使用:

    /* 中间的标题按钮 */
    TitleButton *titleButton = [[TitleButton alloc] init];
    [titleButton setTitle:@"首页" forState:UIControlStateNormal];
    
    // 监听标题点击
    [titleButton addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside];
    
    self.navigationItem.titleView = titleButton;

下拉菜单

思路:

  1. 自定义一个下拉框DropdownMenu,该下拉框是继承一个UIView,在DropdownMenu显示的时候,设置尺寸为整个windows宽高,并设置背景颜色为无色。
  2. 往DropdownMenu 增加一个用来显示菜单的容器UIImageView,该容器的背景图是新浪提供,在初始化的时候,传入tableController.View ,并设置大小。
  3. 显示的位置,在DropdownMenu显示时设置。
  4. 定义两个方法,除了Show显示方法,还需要定义一个销毁方法,当DropdownMenu被点击touchesBegan时,销毁(隐藏掉)
  5. 定义代理,当DropdownMenu显示或者隐藏的时候,对外通知

DropdownMenu.h

#import <UIKit/UIKit.h>

@class DropdownMenu;

@protocol DropdownMenuDelegate <NSObject>
@optional
- (void)dropdownMenuDidDismiss:(DropdownMenu *)menu;
- (void)dropdownMenuDidShow:(DropdownMenu *)menu;
@end

@interface DropdownMenu : UIView

@property (nonatomic, weak) id<DropdownMenuDelegate> delegate;

/**
 *  内容
 */
@property (nonatomic, strong) UIView *content;

/**
 *  内容控制器
 */
@property (nonatomic, strong) UIViewController *contentController;

/**
 *  初始化菜单
 */
+(instancetype)menu;

/**
 *  显示
 *
 *  @param from 要显示的控件上
 */
- (void)showFrom:(UIView *)from;

/**
 *  销毁
 */
- (void)dismiss;


@end

DropdownMenu.m

//
//  DropdownMenu.m
//  Weibo
//
//  Created by jiangys on 15/10/6.
//  Copyright © 2015年 Jiangys. All rights reserved.
//

#import "DropdownMenu.h"
#import "UIImage+Effect.h"

@interface DropdownMenu()
/**
 *  将来用来显示具体内容的容器
 */
@property (nonatomic, weak) UIImageView *containerView;
@end

@implementation DropdownMenu

- (UIImageView *)containerView
{
    if (!_containerView) {
        // 添加一个灰色图片控件
        UIImageView *containerView = [[UIImageView alloc] init];
        // 拉伸图片
        containerView.image =[UIImage imageToResizable:@"popover_background"];
        containerView.userInteractionEnabled = YES; // 开启交互
        [self addSubview:containerView];
        self.containerView = containerView;
    }
    return _containerView;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // 清除颜色
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

+ (instancetype)menu
{
    return [[self alloc] init];
}

- (void)setContent:(UIView *)content
{
    _content = content;
    
    // 调整内容的位置
    content.x = 10;
    content.y = 15;

    // 设置灰色的高度
    self.containerView.height = CGRectGetMaxY(content.frame) + 11;
    // 设置灰色的宽度
    self.containerView.width = CGRectGetMaxX(content.frame) + 10;
    
    // 添加内容到灰色图片中
    [self.containerView addSubview:content];
}

- (void)setContentController:(UIViewController *)contentController
{
    _contentController = contentController;
    
    self.content = contentController.view;
}

/**
 *  显示
 */
- (void)showFrom:(UIView *)from
{
    // 1.获得最上面的窗口
    UIWindow *window = [[UIApplication sharedApplication].windows lastObject];
    
    // 2.添加自己到窗口上
    [window addSubview:self];
    
    // 3.设置尺寸
    self.frame = window.bounds;
    
    // 4.调整灰色图片的位置
    // 默认情况下,frame是以父控件左上角为坐标原点
    // 转换坐标系
    CGRect newFrame = [from convertRect:from.bounds toView:window];
    //    CGRect newFrame = [from.superview convertRect:from.frame toView:window];
    self.containerView.centerX = CGRectGetMidX(newFrame);
    self.containerView.y = CGRectGetMaxY(newFrame);
    
    // 通知外界,自己显示了
    if ([self.delegate respondsToSelector:@selector(dropdownMenuDidShow:)]) {
        [self.delegate dropdownMenuDidShow:self];
    }
}

/**
 *  销毁
 */
- (void)dismiss
{
    [self removeFromSuperview];
    
    // 通知外界,自己被销毁了
    if ([self.delegate respondsToSelector:@selector(dropdownMenuDidDismiss:)]) {
        [self.delegate dropdownMenuDidDismiss:self];
    }
}

/**
 *  点击其它空白地方销毁
 */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self dismiss];
}

@end

通用的时候,很简单,先实现代理,设置当前控制器为代理

/**
 *  标题点击
 */
- (void)titleClick:(UIButton *)titleButton
{
    // 1.创建下拉菜单
    DropdownMenu *menu = [DropdownMenu menu];
    menu.delegate = self;
    
    // 2.设置内容
    TitleMenuViewController *vc = [[TitleMenuViewController alloc] init];
    vc.view.height = 150;
    vc.view.width = 150;
    
    menu.contentController = vc;
    
    // 3.显示
    [menu showFrom:titleButton];
}

该章节源码下载:http://pan.baidu.com/s/1pJ5UTjd

新浪微博Github:https://github.com/jiangys/Weibo