欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS: 仿新浪微博 OC (持续更新ing)

程序员文章站 2023-12-22 11:47:52
...

效果图(暂定)
iOS: 仿新浪微博 OC (持续更新ing)

1.配置AppIcon和LaunchImage

iOS: 仿新浪微博 OC (持续更新ing)

iOS: 仿新浪微博 OC (持续更新ing)

2.项目框架初建

需求:
多视图控制器

思路:
(1) 自定义一个继承UITabBarController的类作为window的rootViewController
(2) 给标签控制器上的每个子控制器包上一个根控制器

WBTabBarController.m


- (void)viewDidLoad {
    [super viewDidLoad];

    UITableViewController *homeVC = [self creatChildControllerWithClassName:@"WBHomeTableViewController" andTitle:@"首页" andImageName:@"tabbar_home"];
    UITableViewController *findVC = [self creatChildControllerWithClassName:@"WBFindTableViewController" andTitle:@"发现" andImageName:@"tabbar_discover"];
    UITableViewController *messageVC = [self creatChildControllerWithClassName:@"WBMessageTableViewController" andTitle:@"消息" andImageName:@"tabbar_message_center"];
    UITableViewController *mineVC = [self creatChildControllerWithClassName:@"WBMineTableViewController" andTitle:@"我" andImageName:@"tabbar_profile"];

    self.viewControllers = @[homeVC,messageVC,findVC,mineVC];
    self.tabBar.tintColor = [UIColor orangeColor];

    // KVC替换系统的tabBar
    WBTabBar *tabBar = [[WBTabBar alloc] init];
    tabBar.delegate =self;
    [self setValue:tabBar forKey:@"tabBar"];
}


/**
 创建标签栏子控制器

 @param className 类名
 @param title 标签标题
 @param imageName 图标名称
 @return 子控制器
 */
- (UITableViewController *)creatChildControllerWithClassName:(NSString *)className andTitle: (NSString *)title andImageName: (NSString *)imageName{

    Class cla = NSClassFromString(className);

    UITableViewController *vc = [[cla alloc] init];

    // 标签名
    vc.tabBarItem.title = title;

    // 文字样式
    NSMutableDictionary *titleAttrs = [NSMutableDictionary dictionary];
    titleAttrs[NSForegroundColorAttributeName] = HWColor(123, 123, 123);
    NSMutableDictionary *selectorTitleAttrs = [NSMutableDictionary dictionary];
    selectorTitleAttrs[NSForegroundColorAttributeName] = [UIColor orangeColor];

    [vc.tabBarItem setTitleTextAttributes:titleAttrs forState:UIControlStateNormal];
    [vc.tabBarItem setTitleTextAttributes:selectorTitleAttrs forState:UIControlStateSelected];

    // 图标
    vc.tabBarItem.image = [UIImage imageNamed:imageName];
    NSString *selectedImageName = [imageName stringByAppendingString:@"_selected"];
    vc.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImageName]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    WBNavigationController *nav = [[WBNavigationController alloc] initWithRootViewController:vc];

    [self addChildViewController:nav];

    return nav;

}

WBNavigationController.m
这里用到一个单独抽取的UIBarButtonItem+Extension类,后面有写


+(void)initialize{

    // 设置统一的item样式
    UIBarButtonItem *item = [UIBarButtonItem appearance];

    NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
    textAttrs[NSForegroundColorAttributeName] = [UIColor orangeColor];
    textAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:13];
    [item setTitleTextAttributes:textAttrs forState:UIControlStateNormal];

    // 设置不可用状态
    NSMutableDictionary *disableTextAttrs = [NSMutableDictionary dictionary];
    disableTextAttrs[NSForegroundColorAttributeName] = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:0.7];
    disableTextAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:13];
    [item setTitleTextAttributes:disableTextAttrs forState:UIControlStateDisabled];

}


-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{

    if (self.viewControllers.count > 0) {
        // 跳转后不是子控制器,则自动隐藏tabbar
        viewController.hidesBottomBarWhenPushed = YES;
        /* 设置导航栏上面的内容 */
        // 设置左边的返回按钮
        viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_back" andTarget:self andSelector:@selector(back)];

        // 设置右边的更多按钮
        viewController.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_more" andTarget:self andSelector:@selector(more)];

    }

    [super pushViewController:viewController animated:animated];
}

// 返回
- (void)back
{
    [self popViewControllerAnimated:YES];
}

// 更多
- (void)more
{
    [self popToRootViewControllerAnimated:YES];
}

UIBarButtonItem+Extension.h

+ (instancetype) itemWithImageName:(NSString *) imageName andTarget:(id)target andSelector:(SEL)selector;

+ (instancetype) itemWithTitle:(NSString *) title andTarget:(id)target andSelector:(SEL)selector;

UIBarButtonItem+Extension.m

/**
 *  创建一个item
 *
 *  @param target    点击item后调用哪个对象的方法
 *  @param action    点击item后调用target的哪个方法
 *  @param image     图片
 *
 *  @return 创建完的item
 */
+ (instancetype) itemWithImageName:(NSString *) imageName andTarget:(id)target andSelector:(SEL)selector{

    UIBarButtonItem *item = [[self alloc] init];

    // 创建按钮
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    UIImage *image = [UIImage imageNamed:imageName];
    [button setBackgroundImage: image forState:UIControlStateNormal];
    NSString *highlightedImageName = [imageName stringByAppendingString:@"_highlighted"];
    [button setBackgroundImage:[UIImage imageNamed:highlightedImageName] forState:UIControlStateHighlighted];

    // 设置frame
    button.frame = CGRectMake(0, 0, image.size.width, image.size.height);

    // 设置事件
    [button addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside];


//    item.customView = button;
    return  [[UIBarButtonItem alloc] initWithCustomView:button];
}

+ (instancetype) itemWithTitle:(NSString *) title andTarget:(id)target andSelector:(SEL)selector{

    UIBarButtonItem *item = [[self alloc] init];

    // 创建按钮
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setTitle:title forState:UIControlStateNormal];
    UIColor *ItemNormalColor = [[UIColor alloc] initWithRed:80/255.0 green:80/255.0 blue:80/255.0 alpha:1];
    UIColor *ItemHighlightedColor = [UIColor orangeColor];

    [button setTitleColor:ItemNormalColor forState:UIControlStateNormal];
    [button setTitleColor:ItemHighlightedColor forState:UIControlStateHighlighted];

    // 尺寸
    [button sizeToFit];

    // 设置事件
    [button addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside];


    //    item.customView = button;
    return  [[UIBarButtonItem alloc] initWithCustomView:button];
}

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // 创建窗口
    _window = [[UIWindow alloc] init];
    _window.frame = [UIScreen mainScreen].bounds;

    // 设置根控制器
    _window.rootViewController = [[WBTabBarController alloc] init];


    // 显示窗口
    [self.window makeKeyAndVisible];

    return YES;
}

3.自定义tabBar

需求:
在 4 个控制器切换按钮中间增加一个撰写按钮
点击撰写按钮能够弹出发表微博的控制器

思路:
(1) 加号按钮的大小与其他 tabBarItem 的大小是一致的
(2) 添加加号按钮到TabBar中
(3) 遍历查找到其他4个UITabBarButton,设置宽度并调整位置
(4) 将撰写按钮放在自定义的UITabBar中间位置
(5) 在UITabBar内部监听撰写按钮的点击事件

WBTabBar.h

@class WBTabBar;

@protocol WBTabBarDelegate <UITabBarDelegate>
@optional
- (void)tabBarDidClickPlusButton:(WBTabBar *)tabBar;

@end


@interface WBTabBar : UITabBar

// 代理属性
@property (nonatomic, weak) id<WBTabBarDelegate> delegate;

@end

WBTabBar.m

@interface WBTabBar()
/// 加号按钮
@property(nonatomic,weak) UIButton *plusBtn;

@end

@implementation WBTabBar


-(instancetype)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];
    if(self){
        // 添加一个+号按钮 到tabbar中
        UIButton *plusBtn = [[UIButton alloc] init];
        // 设置背景
        [plusBtn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
        [plusBtn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];
        // 增加+号图片
        [plusBtn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
        [plusBtn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add_highlighted"] forState:UIControlStateHighlighted];
        // 大小
        [plusBtn sizeToFit];

        // 监听事件
        [plusBtn addTarget:self action:@selector(plusClick) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:plusBtn];
        self.plusBtn = plusBtn;
    }
    return self;

}

// 加号按钮点击事件
- (void)plusClick{
    NSLog(@"加号按钮被点击了");
}

// 标签按钮布局
- (void)layoutSubviews{
    [ super layoutSubviews ];

    self.plusBtn.translatesAutoresizingMaskIntoConstraints = false;
    //水平居中
    NSLayoutConstraint *plusBtn_CenterX = [self.plusBtn.centerXAnchor constraintEqualToAnchor:self.centerXAnchor];
    //垂直居中
    NSLayoutConstraint *plusBtn_CenterY = [self.plusBtn.centerYAnchor constraintEqualToAnchor:self.centerYAnchor];
    //宽度约束
    NSLayoutConstraint *plusBtn_Width = [self.plusBtn.widthAnchor constraintEqualToConstant:64];
    //高度约束
    NSLayoutConstraint *plusBtn_Height = [self.plusBtn.heightAnchor constraintEqualToConstant:44];
    [NSLayoutConstraint activateConstraints:@[plusBtn_CenterX,plusBtn_CenterY,plusBtn_Width,plusBtn_Height]];

    /// 其他按钮尺寸及位置
    // 按钮索引
    CGFloat index = 0;

    // 按钮宽度
    CGFloat itemW = self.bounds.size.width / 5;
    CGFloat itemH = self.bounds.size.height;

    for (UIView * subView in self.subviews) {

        if ([subView isKindOfClass:NSClassFromString(@"UITabBarButton")]) {

            // 设置宽度
            subView.frame = CGRectMake(itemW * index, 0, itemW, itemH);

            index += 1;

            if (index == 2) {
                index += 1;
            }

        }

    }


}
@end

4.发现页面

(1) 导入cityinfo.json文件,创建城市模型数据
(2) 搜索框,使用UISearchController,NSPredicate实现

*WBCityInfoModel.h

@interface WBCityInfoModel : NSObject

/// 城市名
@property (nonatomic, copy) NSString *name;

/// id
@property (nonatomic, copy) NSString *idName;

/// 子城市名
@property (nonatomic, copy) WBCityInfoModel *children;

@end

*WBCityInfoModel.m

@implementation WBCityInfoModel

-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    // jsonModel属性与系统重名
    if ([key isEqualToString:@"id"]) {
        _idName = value;
    }
}

@end

*WBFindTableViewController.m

#import "WBFindTableViewController.h"
#import "WBCityInfoModel.h"
#import "YYModel.h"
#import "WBSearchResultController.h"

@interface WBFindTableViewController ()<UISearchResultsUpdating,UISearchControllerDelegate,UISearchBarDelegate>

// 模型数组
@property (nonatomic, strong) NSArray *modelArray;

// 结果展示控制器
@property (nonatomic, strong) WBSearchResultController *resultsTableController;

// 筛选结果集
@property (nonatomic, strong) NSArray *results;

@property (nonatomic, strong) UISearchController *searchContro;

// for state restoration
@property BOOL searchControllerWasActive;
@property BOOL searchControllerSearchFieldWasFirstResponder;

@end

@implementation WBFindTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.navigationItem.title = @"搜索";

    // 加载数据
    [self loadCitysData];

    // 注册cell
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellID"];

    [self setupUI];

}

- (void) setupUI{

    // 结果控制器
    WBSearchResultController *searchVC = [WBSearchResultController new];

    searchVC.datas = [self.modelArray copy];
    UISearchController *searchContro = [[UISearchController alloc]initWithSearchResultsController: searchVC];

    // 在导航条显示
    if (@available(iOS 11.0, *)) {
        self.navigationController.navigationBar.prefersLargeTitles = YES;
        self.navigationItem.searchController = searchContro;
    } else {
        self.tableView.tableHeaderView = searchContro.searchBar;
    }

    // 设置结果更新代理
    searchContro.searchResultsUpdater = self;
    [self.searchContro.searchBar sizeToFit];

    //是否添加半透明覆盖层
    searchContro.dimsBackgroundDuringPresentation = NO;
    //是否隐藏导航栏
    searchContro.hidesNavigationBarDuringPresentation = YES;

    //如果不添加下面这行代码,在设置hidesNavigationBarDuringPresentation这个属性为YES的时候,搜索框进入编辑模式会导致,searchbar不可见,偏移-64;// know where you want UISearchController to be displayed

    self.definesPresentationContext = YES;

    searchContro.searchBar.placeholder = @"搜索";

    self.resultsTableController.tableView.delegate = self;
    self.searchContro = searchContro;
    self.searchContro.delegate = self;
    self.searchContro.searchBar.delegate = self; // so we can monitor text changes + others

}


- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // restore the searchController's active state
    if (self.searchControllerWasActive) {
        self.searchContro.active = self.searchControllerWasActive;
        _searchControllerWasActive = NO;

        if (self.searchControllerSearchFieldWasFirstResponder) {
            [self.searchContro.searchBar becomeFirstResponder];
            _searchControllerSearchFieldWasFirstResponder = NO;
        }
    }
}


#pragma mark - UISearchBarDelegate

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
}


#pragma mark - UISearchControllerDelegate

// Called after the search controller's search bar has agreed to begin editing or when
// 'active' is set to YES.
// If you choose not to present the controller yourself or do not implement this method,
// a default presentation is performed on your behalf.
//
// Implement this method if the default presentation is not adequate for your purposes.
//
- (void)presentSearchController:(UISearchController *)searchController {

}

- (void)willPresentSearchController:(UISearchController *)searchController {
    // do something before the search controller is presented
}

- (void)didPresentSearchController:(UISearchController *)searchController {
    // do something after the search controller is presented
}

- (void)willDismissSearchController:(UISearchController *)searchController {
    // do something before the search controller is dismissed
}

- (void)didDismissSearchController:(UISearchController *)searchController {
    // do something after the search controller is dismissed
}


#pragma mark - UISearchResultsUpdating方法
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController{

    WBSearchResultController *vc = searchController.searchResultsController;
    // 设置searchController的代理为当前控制器,用于搜索结果页点击cell跳转
    vc.tableView.delegate = self;
    NSString *inputStr = searchController.searchBar.text ;

    if (searchController.searchBar.text != @"" || vc.datas != nil || vc.datas.count > 0) {

        // 使用谓词过滤数据
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"name CONTAINS %@",inputStr];
        // 取出包含‘inputStr’的元素
        self.results = [self.modelArray filteredArrayUsingPredicate:pred];

        // 把过滤完的数据传给控制器
        vc.datas = self.results;

        // 刷新数据
        [vc.tableView reloadData];

    }else{

        return;
    }
}

#pragma mark - cell点击事件
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    // 判断点击的cell模型是当前tableView的还是结果tableView的
    // 取得该cell的模型
    WBCityInfoModel *city = tableView == self.tableView ?  self.modelArray[indexPath.row] : self.results[indexPath.row];

    // 跳转页标题
    WBSearchResultController *vc = [[WBSearchResultController alloc] init];
    vc.title = city.name;
    NSLog(@"被点击了%@",city.name);

    vc.view.backgroundColor = [UIColor yellowColor];

    // push to resultsTableController
    [self.navigationController pushViewController:vc animated:YES];
    [tableView deselectRowAtIndexPath:indexPath animated:NO];

}

#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.modelArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID" forIndexPath:indexPath];

    // 设置数据
    WBCityInfoModel *info = self.modelArray[indexPath.row];
    cell.textLabel.text = info.name;

    return cell;
}

#pragma mark - 加载数据
- (void)loadCitysData {

    NSString *url = [[NSBundle mainBundle]URLForResource:@"citysData.json" withExtension:nil];
    //加载JSON文件
    NSData *data = [NSData dataWithContentsOfURL:url];

    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    // YYModel实现字典数组转模型数组
    self.modelArray = [NSArray yy_modelArrayWithClass:[WBCityInfoModel class] json:dict];
    // 回到主线程刷新UI
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
         [self.tableView reloadData];
    }];
}

@end

*WBSearchResultController.h

#import <UIKit/UIKit.h>
#import "WBCityInfoModel.h"
@interface WBSearchResultController : UITableViewController

// 接收模型属性
@property (nonatomic, strong)NSArray *datas;

@end

*WBSearchResultController.m

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"searchReasultCellID"];

}


#pragma mark - 数据源方法
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return self.datas.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"searchReasultCellID" forIndexPath:indexPath];

    // 设置数据
    WBCityInfoModel *info = self.datas[indexPath.row];
    cell.textLabel.text = info.name;

    return cell;
}

上一篇:

下一篇: