业务思路: 客户端调用Java接口把消息id,收藏时间传给后台APIWeb服务器,后端和 IM 服务器通讯,拿到真正收藏的消息StoreBody 进行存储。

收藏:
APIWeb服务器业务收藏成功,返回相关收藏id给客户端。进行存储到表中。

删除:
客户端根据收藏id调用java接口成功后将收藏记录移除。

同步数据:
每次打开个人中心的收藏页面,请求接口进行数据同步,服务器存储时间戳和数条记录,客户端拿到数据后进行循环处理每条记录,根据收藏id,收藏时间,删除标记来决定是否将该记录进行插入到本地收藏表中。

为了使搜索的结果控制器直接重用收藏的控制器。封装了类似EHMyStoreBaseResultsController的result控制器来进行显示。 收藏的基类EHMyStoreBaseController 持有displayController 即为该类型。
同时,该StoreBaseController实现了GYContainerSearchResultsDelegate协议。如下:记录搜索关键字。

1
2
3
4
- (void)beginStoreSearchByKeyWords:(NSString *)searchStr
{
_historySearchStr = searchStr;
}
  • h 文件
1
2
3
4
5
6
7
8
9
@protocol GYContainerSearchResultsDelegate <NSObject>
//点击搜索,去查询数据库记录
- (void)beginStoreSearchByKeyWords:(NSString *)searchStr;

@end

@interface EHMyStoreBaseResultsController : GYContainerSearchResultsController
- (instancetype)initWithWrapStoreBaseControllerName:(NSString *)baseChildControllerName withOwerViewController:(EHMyStoreBaseController *)storeChildVC;
@end
  • m文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@interface EHMyStoreBaseResultsController()
@property(strong,nonatomic) EHMyStoreBaseController *childStoreVc; //EHMyStoreBaseController子类的搜索结果集展示类
@property(strong,nonatomic) EHMyStoreBaseController *owerStoreVc; //EHMyStoreBaseController的子类。
@end

@implementation EHMyStoreBaseResultsController

- (instancetype)initWithWrapStoreBaseControllerName:(NSString *)baseChildControllerName withOwerViewController:(EHMyStoreBaseController *)storeChildVC
{
if(self = [super initWithWrapStoreBaseControllerName:baseChildControllerName]) {

_owerStoreVc = storeChildVC;
}
return self;

}

-(void)viewDidLoad
{
[super viewDidLoad];

_childStoreVc = [[NSClassFromString(self.baseChildControllerName) alloc] init];
_childStoreVc.withoutSearchFlag = YES; //不需要搜索条
_childStoreVc.markSearchResultFlag = YES; //标记在搜索页: 需要把顶部收藏分类去掉
[self addChildViewController:_childStoreVc];

[self.view addSubview:_childStoreVc.view];
[_childStoreVc.view mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.mas_equalTo(0);
make.top.mas_equalTo((self.searchBar.frame.size.height + STATUSBAR_HEIGHT ));
}];
}


#pragma mark------UISearchBarDelegate-----
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
if([self.owerStoreVc respondsToSelector:@selector(cancelMulEditStatus)]){
[self.owerStoreVc cancelMulEditStatus];
}
return YES;
}

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSLog(@"%s", __FUNCTION__);
self.searchStr = [StringUtil trimString:searchBar.text];
[self setSearchResultsTitle:nil];//先不显示一下

if([self.searchStr length] == 0 || [self.searchStr length] == 1) {
[self setSearchResultsTitle:@"收藏搜索的空文字提示"];
}else {
[self setSearchResultsTitle:nil];
}
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(searchHandle) object:nil];
[self performSelector:@selector(searchHandle) withObject:nil afterDelay:0.2f];
}

//真正触发搜索的这一方法
- (void)searchHandle {
[super searchHandle];
if([self.childStoreVc respondsToSelector:@selector(beginStoreSearchByKeyWords:)]){
[self.childStoreVc beginStoreSearchByKeyWords:self.searchStr];
}
}

#pragma mark - UISearchResultsUpdating
//每输入一个字符都会执行一次
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController{
// NSLog(@"搜索关键字:%@",searchController.searchBar.text);
//放在presentSearchController不生效,因此暂定放在此处
}

// 当用户在搜索控制器中开始编辑时或者将active属性设置为YES时,该方法被调用
- (void)presentSearchController:(UISearchController *)searchController{
[self setSearchResultsTitle:@"收藏搜索的空文字提示"];//设置一开始的提示语
self.searchStr = [StringUtil trimString:self.searchBar.text];
//清空搜索表格的数据

}

@end

分割线

EHMyStoreBaseResultsController: GYContainerSearchResultsController

  • GYSearchController 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    @interface GYSearchController : UISearchController
    @end

    #define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
    #define kIS_IPHONE_X (IS_IPHONE && SCREEN_MAX_LENGTH == 812.0)

    @interface GYSearchController ()
    @end

    @implementation GYSearchController

    - (void)viewDidLoad {
    NSLog(@"viewDidLoad");
    [super viewDidLoad];
    [self add_imageBg];
    }

    - (void)viewWillAppear:(BOOL)animated {
    NSLog(@"");
    [super viewWillAppear:animated];
    }

    #pragma mark -
    -(void)viewDidLayoutSubviews {
    NSLog(@"viewDidLayoutSubviews");
    [self setSomeFrame];
    [super viewDidLayoutSubviews];
    [self setSomeFrame];
    }

    -(void)viewWillLayoutSubviews {
    NSLog(@"viewWillLayoutSubviews");
    [self setSomeFrame];
    [super viewWillLayoutSubviews];
    [self setSomeFrame];
    }

    - (void) setSomeFrame {
    if(kIS_IPHONE_X){
    if(1){//searBar内部背景修正
    if (@available(iOS 11.0, *)) {
    CGRect rect = [[self.searchBar subviews][0] subviews][0].frame;
    if(self.isActive){
    CGFloat statusHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
    rect.origin.y = - statusHeight;
    rect.size.height = self.searchBar.bounds.size.height;
    [[self.searchBar subviews][0] subviews][0].frame = rect;
    }
    }
    }
    }
    }

    //添加图片的方式
    - (void)add_imageBg {
    UIImageView *imageBg = ({
    UIImageView *iv = (UIImageView*)[self.view viewWithTag:999];
    if(!iv){
    UIColor *searchBarBgColor = [UIColor colorWithRed:242.0/255.0 green:242.0/255.0 blue:242.0/255.0 alpha:1.0];
    CGFloat statusHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
    iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, statusHeight+self.searchBar.frame.size.height-1)];//后面-1是因为iphoneX实际调试为99=44+56-1
    iv.backgroundColor = [UIColor redColor];
    iv.backgroundColor = searchBarBgColor;
    iv.tag = 999;
    [self.view addSubview:iv];
    }
    iv;
    });
    imageBg.hidden = NO;
    }

    @end
  • GYContainerSearchResultsController 文件

  • h文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #define IS_IOS11   ([[[UIDevice currentDevice]systemVersion]floatValue] >= 11.0)?YES:NO //是否iOS11及以后
    #define kSearchBarHeight ((IS_IOS11)? 56.0f:44.0f)

    ///为UISearchController的searchResultsController对象
    @interface GYContainerSearchResultsController : UIViewController

    @property(nonatomic,readonly) NSString *baseChildControllerName;

    - (instancetype)initWithWrapStoreBaseControllerName:(NSString *)baseChildControllerName;

    /** 搜索的searchController */
    @property(nonatomic, strong) UISearchController *searchController;
    /** 是否激活搜索状态中:注意新旧两版不同细节情况;特别旧版在WillEnd开始就为NO了 */
    @property(nonatomic, assign) BOOL isActive;
    /** 搜索的searchBar */
    @property(nonatomic, strong) UISearchBar *searchBar;
    /** 搜索的搜索字串 */
    @property (nonatomic,strong) NSString *searchStr;
    /** 当前Search的上级宿主viewController (一般为self.searchController的nextResponder,只有在搜索框显示出来时才可使用!!!) */
    @property(nonatomic, weak) UIViewController *hostViewController;

    @property(nonatomic, strong) NSString *defaultTipStr;
    #pragma mark - UISearchControllerDelegate的几个回调block========================
    @property(nonatomic, copy) void(^willPresentSearchControllerBlock)(GYContainerSearchResultsController *searchResultsController);
    @property(nonatomic, copy) void(^didPresentSearchControllerBlock)(GYContainerSearchResultsController *searchResultsController);
    @property(nonatomic, copy) void(^willDismissSearchControllerBlock)(GYContainerSearchResultsController *searchResultsController);
    @property(nonatomic, copy) void(^didDismissSearchControllerBlock)(GYContainerSearchResultsController *searchResultsController);

    #pragma mark - UISearchController辅助初始化方法===================================
    /** 上级viewController初始化一些设置 @param viewController 传入待处理的vc */
    + (void)viewController_viewDidLoad_someSetting:(UIViewController*)viewController;
    /** 初始化设置searchBar */
    - (void)initSearchBar;

    -(void)viewController_viewDidLayoutSubviews:(UITableView *)tableView hostViewController:(UIViewController*)hostViewController bottomMargin:(CGFloat)bottomMargin;
    @end

    #pragma mark - UISearchBarDelegate-暴露出子类可以重写的方法名======================
    @interface GYContainerSearchResultsController(GY_UISearchBarDelegate)
    -(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar;
    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
    - (void)searchHandle;
    - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar;
    - (void)setSearchResultsTitle:(NSString *)title;
    - (void)showSearchTip:(NSString *)title;
    @end
  • m文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    #define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
    #define kIS_IPHONE_X (IS_IPHONE && SCREEN_MAX_LENGTH == 812.0)

    @interface GYContainerSearchResultsController ()<UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate
    >
    @property (nonatomic, strong) UILabel *emptyTipsLabel;
    @property (nonatomic, strong) UILabel *defaultTipsLabel;
    @end

    @implementation GYContainerSearchResultsController

    - (UIViewController *)hostViewController {
    if (_hostViewController==nil) {
    UIResponder *nextResponder = [self.searchController nextResponder];
    if ([nextResponder isKindOfClass:[UIViewController class]]) {
    _hostViewController = (UIViewController *)nextResponder;
    }
    }
    return _hostViewController;
    }

    - (BOOL)isActive {
    return self.searchController.active;
    }

    - (instancetype)initWithWrapStoreBaseControllerName:(NSString *)baseChildControllerName
    {
    if(self = [super init]) {

    _baseChildControllerName = baseChildControllerName;
    //初始化设置searchBar
    [self initSearchBar];
    }
    return self;
    }

    - (void)viewDidLoad {
    [super viewDidLoad];

    if(_emptyTipsLabel == nil){
    _emptyTipsLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, SCREEN_WIDTH, 30)];
    _emptyTipsLabel.font = [UIFont systemFontOfSize:20.0f];
    _emptyTipsLabel.textColor = [UIColor colorWithRed:202.0/255.0 green:202.0/255.0 blue:202.0/255.0 alpha:1.0];
    _emptyTipsLabel.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:_emptyTipsLabel];
    }
    }

    - (UILabel *)defaultTipsLabel{
    if (_defaultTipsLabel == nil) {
    UILabel *lab = [UILabel new];
    lab.textColor = KUIColorFromRGB(0x417FF8);
    lab.font = [UIFont systemFontOfSize:17];
    _defaultTipsLabel = lab;
    _defaultTipsLabel.hidden = YES;
    [self.searchController.searchBar addSubview:_defaultTipsLabel];
    }
    return _defaultTipsLabel;
    }

    #pragma mark - UISearchResultsUpdating
    - (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
    NSLog(@"updateSearchResultsForSearchController->inputStr==%@",searchController.searchBar.text);
    }


    #pragma mark - UISearchControllerDelegate代理
    - (void)willPresentSearchController:(UISearchController *)searchController {
    NSLog(@"willPresentSearchController");
    if (self.defaultTipStr.length) {
    self.defaultTipsLabel.hidden = NO;
    float w = [self.defaultTipStr boundingRectWithSize:CGSizeMake(MAXFLOAT, 30) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:17]} context:nil].size.width + 10;
    self.defaultTipsLabel.text = self.defaultTipStr;
    self.defaultTipsLabel.frame = CGRectMake(40, 5, w, self.searchBar.bounds.size.height - 10);
    self.searchBar.searchTextPositionAdjustment = UIOffsetMake(w, 0);
    self.searchBar.placeholder = @"";
    }else{
    self.defaultTipsLabel.hidden = YES;
    self.searchBar.searchTextPositionAdjustment = UIOffsetMake(0, 0);
    [self.searchBar changeLeftPlaceholder:[StringUtil getLocalizableString:@"chats_search"]];
    }
    !self.willPresentSearchControllerBlock?:self.willPresentSearchControllerBlock(self);
    }

    - (void)didPresentSearchController:(UISearchController *)searchController {
    NSLog(@"didPresentSearchController");
    !self.didPresentSearchControllerBlock?:self.didPresentSearchControllerBlock(self);
    }

    - (void)willDismissSearchController:(UISearchController *)searchController {

    [self.navigationController setNavigationBarHidden:NO animated:NO];
    [self.navigationController.view layoutIfNeeded];
    self.defaultTipsLabel.hidden = YES;
    self.searchBar.searchTextPositionAdjustment = UIOffsetMake(0, 0);
    [self.searchBar changeLeftPlaceholder:[StringUtil getLocalizableString:@"chats_search"]];
    !self.willDismissSearchControllerBlock?:self.willDismissSearchControllerBlock(self);
    }

    - (void)didDismissSearchController:(UISearchController *)searchController {
    NSLog(@"didDismissSearchController");
    !self.didDismissSearchControllerBlock?:self.didDismissSearchControllerBlock(self);
    }

    - (void)presentSearchController:(UISearchController *)searchController {
    NSLog(@"presentSearchController");
    }


    #pragma mark------UISearchBarDelegate-----
    -(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
    NSLog(@"searchBarShouldBeginEditing");
    return YES;
    }

    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    NSLog(@"%s", __FUNCTION__);
    self.searchStr = [StringUtil trimString:searchBar.text];
    [self setSearchResultsTitle:nil];

    if([self.searchStr length] == 0) {
    }
    else if ([self.searchStr length] == 1) {
    [self setSearchResultsTitle:[StringUtil getLocalizableString:@"search_tip"]];
    }
    else {
    [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(searchHandle) object:nil];
    [self performSelector:@selector(searchHandle) withObject:nil afterDelay:0.2f];
    }
    }

    - (void)searchHandle {
    NSLog(@"开始去搜索。。。。");
    }

    - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    if ([self.searchStr length] < 1) {
    return;
    }
    //去除焦点收键盘
    [searchBar resignFirstResponder];
    //搜索提示
    [[LCLLoadingView currentIndicator] setCenterMessage:[StringUtil getLocalizableString:@"searching"]];
    [[LCLLoadingView currentIndicator] show];
    //再搜索
    [self searchHandle];
    }

    //搜索提示文字
    - (void)setSearchResultsTitle:(NSString *)title {
    if(title==nil){
    _emptyTipsLabel.hidden = YES;
    }else{
    _emptyTipsLabel.hidden = NO;
    _emptyTipsLabel.text = [NSString stringWithFormat:@"%@", title];
    }
    }

    //alert
    - (void)showSearchTip:(NSString *)title {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:@"" delegate:nil cancelButtonTitle:[StringUtil getLocalizableString:@"confirm"] otherButtonTitles: nil];
    [alert show];
    }


    #pragma mark - UISearchController辅助初始化方法===================================
    #pragma mark - 上级viewController初始化一些设置
    + (void)viewController_viewDidLoad_someSetting:(UIViewController*)viewController
    {
    UIViewController *_self = viewController;
    //#warning 如果进入预编辑状态,searchBar消失(UISearchController套到�TabBarController可能会出现这个情况),请添加下边这句话
    _self.definesPresentationContext=YES;
    if (@available(iOS 11.0, *)) {
    } else {
    _self.automaticallyAdjustsScrollViewInsets = NO;
    }

    //适配ios7UIViewController的变化
    if ([_self respondsToSelector:@selector(extendedLayoutIncludesOpaqueBars)]) {
    _self.extendedLayoutIncludesOpaqueBars = NO;
    }
    _self.edgesForExtendedLayout = UIRectEdgeAll;

    //第三种方式开关
    [_self setAutomaticallyAdjustsScrollViewInsets:YES];
    [_self setExtendedLayoutIncludesOpaqueBars:YES];
    _self.edgesForExtendedLayout = UIRectEdgeNone;
    _self.edgesForExtendedLayout = UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight;
    }

    #pragma mark - 初始化设置searchBar
    - (void)initSearchBar {
    // 创建用于展示搜索结果的控制器
    GYContainerSearchResultsController *result = self;
    self.searchController = ({
    UISearchController *_searchController = [[GYSearchController alloc] initWithSearchResultsController:result];
    _searchController.delegate = self;
    _searchController.searchResultsUpdater = result;
    _searchController.dimsBackgroundDuringPresentation = YES;//取消蒙版
    _searchController.obscuresBackgroundDuringPresentation = NO;//搜索时,背景变模糊
    _searchController.hidesNavigationBarDuringPresentation = YES;//点击搜索的时候,是否隐藏导航栏
    _searchController;
    });
    self.searchBar = self.searchController.searchBar;
    [self.searchBar changeLeftPlaceholder:[StringUtil getLocalizableString:@"chats_search"]];
    self.searchBar.delegate = self;

    [self.searchBar setBackgroundImage:[self imageWithColor:KUIColorFromRGB(0xF5F5F5) size:self.searchBar.bounds.size]];
    UIImage *image = [self imageWithColor:KUIColorFromRGB(0xffffff) size:CGSizeMake(self.searchBar.bounds.size.width, self.searchBar.bounds.size.height - 20)];
    image = [self rh_bezierPathClip:image cornerRadius:8];
    [_searchBar setSearchFieldBackgroundImage:image forState:UIControlStateNormal];
    }

    - (UIImage *)rh_bezierPathClip:(UIImage *)img
    cornerRadius:(CGFloat)cornerRadius {
    int w = img.size.width * img.scale;
    int h = img.size.height * img.scale;
    CGRect rect = (CGRect){CGPointZero, CGSizeMake(w, h)};
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(w, h), false, 1.0);
    [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius] addClip];
    [img drawInRect:rect];
    UIImage *cornerImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cornerImage;
    }

    - (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size{
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
    }

    #pragma mark -上级viewController辅助方法

    -(void)viewController_viewDidLayoutSubviews:(UITableView *)tableView hostViewController:(UIViewController*)hostViewController bottomMargin:(CGFloat)bottomMargin{
    //调节宿主tableView
    CGRect tableViewAdjustRect = CGRectZero;
    if(self.isActive) {
    CGFloat y = [[UIApplication sharedApplication] statusBarFrame].size.height+self.searchBar.frame.size.height;
    y = (!kIS_IPHONE_X)?y:(y-1);//iphonex时搜索部分的高度为99=44+56-1;
    CGFloat height = SCREEN_HEIGHT-y;
    UINavigationController *nav = hostViewController.navigationController;
    if(nav && nav.viewControllers[0] == hostViewController
    && [nav nextResponder]
    &&([[nav nextResponder] isKindOfClass:[UITabBarController class]]//ios11
    ||[[[[nav nextResponder] nextResponder] nextResponder] nextResponder]//ios10
    )
    ){//在tabarController里且为其导航根vc时,要减去下面的tabBar下面高度
    height = SCREEN_HEIGHT-y-TABBAR_HEIGHT-safeAreaXBottom;
    }
    tableViewAdjustRect = CGRectMake(0, y, SCREEN_WIDTH, height);
    }else {
    CGFloat y = self.searchBar.frame.size.height;
    CGFloat height = SCREEN_HEIGHT-NAVIGATIONBAR_HEIGHT-STATUSBAR_HEIGHT-y;//-safeAreaXBottom;
    UINavigationController *nav = hostViewController.navigationController;
    if(nav && nav.viewControllers[0] == hostViewController
    && [nav nextResponder]
    &&([[nav nextResponder] isKindOfClass:[UITabBarController class]]//ios11
    ||[[[[nav nextResponder] nextResponder] nextResponder] nextResponder]//ios10
    )
    ){
    height = SCREEN_HEIGHT-NAVIGATIONBAR_HEIGHT-STATUSBAR_HEIGHT-self.searchBar.frame.size.height-TABBAR_HEIGHT-safeAreaXBottom;
    }
    tableViewAdjustRect = CGRectMake(0, y, SCREEN_WIDTH, height);
    }
    if (bottomMargin > 0) {
    CGRect rect = tableViewAdjustRect;
    CGFloat h = rect.size.height - bottomMargin;
    if ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) && ([UIScreen mainScreen].bounds.size.width >= 375) && ([UIScreen mainScreen].bounds.size.height >= 812)) {
    h -= 34;
    }
    tableView.frame = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width,h);
    }else{
    tableView.frame = tableViewAdjustRect;
    }
    }

使用TZImagePicker,添加预览。支持视频、图片、gif。 当浏览大图时候,需要进行手势控制。 视频,图片,gif 继承原有的cell。
在PhotoPreviewController配置手势处理configDragContainerView。当往下拖拽时候消失控制器。当滑动到视频时候,显示一个关闭按钮。手势处理只需定义一个NHPanGestureRecognizer继承UIPanGestureRecognizer,通过touchesMoved控制state UIGestureRecognizerStateFailed。

旋转: 先保存原图,然后用原图来预览旋转后的效果。

聊天输入框@某人逻辑分类点说明

IM 应用软件无论是App端或者是PC端都有@某人这样一个功能点,由于用户输入框输入条件的复杂随机性种类多,@的针对不同情况进行拼接或者新增样式显示蓝色字体表示该人名可链接。由输入框开始,用户输入@的方式进行分类:

  1. 首字母为空,用户直接输入@,然后跳转到成员页面进行选择相关人员,选完即带回人名 结果为:“@某人”
  2. input输入框输入了文字的基础上: 在最前面下班为0的第1个位置进行插入@ 结果为:“@某人原input输入框的文字”
  3. input输入框输入了文字的基础上,在最后面下班为length-1的第后一个位置进行插入@ 结果为:“原input输入框的文字@某人”
  4. input输入框输入了文字的基础上,在文字内容中输入@。即条件 0 < index < length-1 结果为:“原input输入@某人框的文字”

在以上四种输入情况中,3,4 输入拼接@某人的时候,需要判断前一个字是否为@, 如果是,则后面拼接的时候直接+某人即可。另外当在第4种情况文字中间输入的时候,中英文下输入可能遇到文本框的值为 “ @@文字”,按照之前的逻辑当光标输入在第2个@后时,然后键盘输入@跳转页面选择成员,最后的结果应该是“ @@某人文字”这个组合,为了更友好地显示,还需检查当前两个也是@的时候,即出现1个@. 即结果为“ @某人文字” 以便更友好地显示。

核心逻辑:根据文本框原输入文字是否有内容,无内容时候直接拼接。有内容时候分两种情况,即光标的输入位置情况:当location等于0的时候,即在首位置输入@某人拼接原文本内容。当location大于0的时候,即光标可能出现在内容文字之间,也可能是在最后面。也是分两种情况:根据location+1判断是否小于原文本框文字长度length.如果小于,则表示在内容之间插入。否则,在内容最后尾部插入。无论是之间还是尾部做插入,都需检查前一个字符是否为@,如果是,则在做拼接人名时候就无需再追加@。当在之间做插入的时候,为了友好显示,可适当判断前两个字符为@@时候进行替换为一个@。

1、iOS 11之前的导航栏的高度是64px(状态条+导航栏),iOS11之后如果设置了prefersLargeTitles = YES(默认NO)则为96pt。所以一般不用管。

2、在iOS 11上运行tableView向下偏移64px或者20px,因为iOS 11废弃了automaticallyAdjustsScrollViewInsets,而是给UIScrollView增加了contentInsetAdjustmentBehavior属性。避免这个坑的方法是要判断

3、tableView的sectionHeader、sectionFooter高度与设置不符,因为tableView的estimatedRowHeight、estimatedSectionHeaderHeight、 estimatedSectionFooterHeight三个高度估算属性由默认的0变成了UITableViewAutomaticDimension。最简单的方法就是直接设置为0。

4、iPhone X状态条由20px变成了44px,UITabBar由49px变成了83px。设置布局时y直接写成64的就要根据机型设置。可以设置宏

#define Device_Is_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO),

推送模块,推送消息存储

H5 的webView 跳转 与原生交互。

图片压缩缓存。 发表话题。

Runtime 到底是个什么鬼?

Runtime是Objective-C中底层的一套C语言API,是一个将C语言转化为面向对象语言的拓展。Runtime的一切都围绕两个中心:类的动态配置消息传递

能干啥?

  • 动态的在内存中创建一个类
  • 给类增加一个属性
  • 给类增加一个协议实现
  • 给类增加一个方法实现IMP
  • 遍历一个类的所有成员变量、属性和方法等
  • 拦截系统自带的方法调用(Method Swizzling黑魔法)
  • 将某些OC代码转化为Runtime代码,探究底层。如block的实现原理
  • 实现给分类增加属性
  • 实现NSCoding的自动归档和接档
  • 实现字典的模型和自动转换

前言

SKU百度百科为 库存保有单位 (Stock keeping Unit 或者SKU)是对每一个产品和服务的唯一标示符。 也就是说sku 为商品对应的很多种类规格情况下的 库存,价格 各有不同。需要通过sku 这样一种组合结构存储这些计量值。
在移动电商时代,网购已然成为潮流。更是全民参与。每一年都有购物节 : 京东618 , 淘宝天猫双11, 双12 。 一个淘宝店家月销售额30万背后 ,他的商品sku有上千种。 一个卖橱柜的,小到抽屉,拉篮,五金 ,把手,螺丝钉 都可以细分成很小的SKU。移动电商商品背后的SKU种类的复杂性。可正是因为商品SKU的多样化,才能商家带来巨量的销售额。

从详情页认识SKU

C端sku选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这个商品包含的SKU信息:{
规格类型:[{
尺寸:[XS, S, M, L,XL, XXL]
},{
颜色: [白色,珊瑚粉,草木绿]
}],
规格明细组合描述: {
XS-白色:{
图片: 图片url,
价格: ¥328,
库存: 188件,
扩展..字段: 值
},
其他规格组合:{
图片: 图片url,
价格: 价格..,
库存: 库存数量..,
扩展..字段: 值
}
}
}

图片

抽奖功能模块源码
产品出发点
1、通过动态抽奖数据和排行激发用户抽奖消费;
2、通过技能和宝石等以及转盘的燃爆值,中奖率翻倍等等吸引用户;
3、根本上是为了激发用户活跃性以及刺激消费

技术难点
1、抽奖转盘分为两种转盘,业务逻辑分一个parentVC 管理两个 子 VC;
2、抽奖奖品服务端返回,显示在每一个扇形区域的中间,金额同似;
3、点击抽奖,拿到奖品id, 找出是在转盘中的哪个区域,让转盘指针停止在那一区域;
4、抽奖动态弹幕;
5、燃烧值 和 倒计时的控制;

业务操作说明
1、两个转盘,某个转盘点击开始抽奖 互不影响; 黄金转盘具有宝箱和技能;
2、每抽取一次,扣除相应的金额,如有中奖,则弹出中奖奖品;抽奖分为单次,十连抽,三十连抽等;
3、抽奖后产生记录;在记录中可看到每次抽奖消费情况;
4、转盘有燃烧值,当满时候,触发倒计时3分钟,倒计时内 中奖率将翻倍;
5、燃烧值每3秒刷新一次,当中途离开再次打开,则拿到最新倒计时显示;
6、弹幕从最新100条记录 每隔2秒随机生成一条记录;

模块设计
1、整个框架布局在一个 MainVC中控制转盘类型,请求余额,通知等刷新金额; 管理childVC ;
2、childVC 中 处理抽奖业务逻辑;UI 布局,抽奖请求, 燃烧值处理,弹幕处理;
3、其他功能性子View 单独自定义视图; 转盘视图,燃烧值进度视图,倒计时进度视图, 弹窗类视图;弹幕视图;
4、弹窗类视图 以一个ParentView公共视图容器 ,其中子视图由枚举参数值控制自定义;有玩法视图,记录视图,保险视图,技能视图,排行榜视图;

图片
图片2
当需要在地图上层覆盖一个列表时候,我们想看到更多的内容,列表被拉出来后翻看数据;
当想收起时候,可用下拉,或者随着手势滑落下来;

需解决的问题:
1、滑动表格,地图不移动;手势来回滑时候,列表要跟着高度变化;
2、当滑到最高时候,不再往上滑动;相反,滑到最低高度时候不再继续向下;
3、滑上去之后,再往下滑动 不影响列表数据的查看与浏览;

思考

手势开始:记录 开始点startY,

手势移动:
根据起始位置移动差 确定上滑动 , 下滑动;

起点B 点 记录方向 0;
B-A 正向, 记录方向符号 1;
B-C 反向 ,记录方向 -1;

偏移值 大于0 , 上滑动;
当B-A-C 后,立马记录 1 变成 -1; 记录标记状态前,

正向如:B-A 的过程 ,需判断是否为 为 -1 到1 还是 0-1 , 如果是-1 则是来回拖动;否则为正向拖动;

code :以下是重点逻辑代码;

(控制器Controller)- 负责转发请求,网络处理,控制业务逻辑。

(视图View) - 页面视图UI交互; 模型与视图不能通信,需通过控制器controller来实现;

(模型Model) - 数据业务模型的呈现; 涉及到的UI 改动, 数据驱动UI,改动数据状态即可实现UI的变更;


演进(ViewModel模型) - 在原有MVC 基础上多了一层,Controller不直接控制Model, 直接操作ViewModel 来间接管理; ViewModel (NSObject)的职责:数据逻辑处理,控制,算法,解析,网络处理等等; 不直接管理View;

三层模式可有效降低项目偶尔度,有效业务规整,分类; 大大提高项目开发效率;