iOS 重构: 视图子元素动态化

2020-06-26  本文已影响0人  SoaringHeart

当我们布局时,总需要布局各种各样的子视图(UIView/UIImageView/UIButton/UIlabel 及其子类),随思考有没有一种方法通过字符串/类型为参数生成子视图的方法实现,然后子视图还可以二次属性设置,经过不断(半年时间)思考,然后在一个周末,灵光一闪,在 swift 中泛型首先突破实现预期效果,(以九宫格视图为例)代码如下:

//
//  UIView+Add.swift
//  SwiftTemplet
//
//  Created by Bin Shang on 2018/8/27.
//  Copyright © 2018年 BN. All rights reserved.
//

import UIKit
public extension UIView{
    
    ///更新各种子视图
    ///更新各种子视图
    final func updateItems<T: UIView>(_ count: Int, type: T.Type, hanler: ((T) -> Void)) -> [T] {
        if count == 0 {
            return []
        }
        
        if let list = self.subviews.filter({ $0.isMember(of: type) }) as? [T] {
            if list.count == count {
                list.forEach { hanler($0) }
                return list
            }
        }
        
        subviews.filter { $0.isMember(of: type) }.forEach { $0.removeFromSuperview() }
        
        var arr: [T] = [];
        for i in 0..<count {
            let subview = type.init()
            subview.tag = i
            self.addSubview(subview)
            arr.append(subview)
            
            hanler(subview)
        }
        return arr;
    }
    
    ///更新各种子类按钮
    final func updateButtonItems<T: UIButton>(_ count: Int, type: T.Type, hanler: ((T) -> Void)) -> [T] {
        return updateItems(count, type: type) {
            if $0.title(for: .normal) == nil {
                $0.titleLabel?.font = UIFont.systemFont(ofSize: 15)
                $0.setTitle("\(type)\($0.tag)", for: .normal)
                $0.setTitleColor(.black, for: .normal)
                $0.setBackgroundColor(.gray, for: .disabled)
            }
            hanler($0)
        }
    }

🌰🌰:
//
//  UITableViewCellSudokuButton.swift
//  SwiftTemplet
//
//  Created by Bin Shang on 2020/6/19.
//  Copyright © 2020 BN. All rights reserved.
//

import UIKit

///九宫格
@objcMembers class UITableViewCellSudokuButton: UITableViewCell {

    var numOfRow: Int = 3
    
    var row: Int = 3

    var inset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

    var itemType: UIButton.Type = UIButton.self

    // MARK: -lazy
    lazy var items: [UIButton] = {
        return self.contentView.updateItems(self.row*self.numOfRow, type: self.itemType) {
            $0.titleLabel?.font = UIFont.systemFont(ofSize: 15)
            $0.setTitle("\(self.itemType)\($0.tag)", for: .normal)
            $0.setTitleColor(.systemBlue, for: .normal)
        }
    }()
    
    // MARK: -life cycle
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier);
  
    }
        
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder);
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews();
        
        setupConstraint()
    }
    
    func setupConstraint() {
        if bounds.height <= 0.0 {
            return;
        }
        
        items.snp.distributeSudokuViews(fixedLineSpacing: 5, fixedInteritemSpacing: 10, warpCount: numOfRow, edgeInset: inset)
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
        // Configure the view for the selected state
    }
    
    // MARK: -funtions
    
}

Objc 版本:

//
//  UIView+Ext.m
//  Xcode11Project
//
//  Created by Bin Shang on 2020/6/25.
//  Copyright © 2020 Bin Shang. All rights reserved.
//

#import "UIView+Ext.h"
#import "NSArray+Ext.h"

@implementation UIView (Ext)

- (NSArray<__kindof UIView *> *)updateItems:(NSInteger)count aClassName:(NSString *)aClassName handler:(void(^)(__kindof UIView *obj))handler {
    if (count == 0) {
        return @[];
    }

    Class cls = NSClassFromString(aClassName);
    NSArray *list = [self.subviews filter:^BOOL(UIView * obj, NSUInteger idx) {
        return [obj isKindOfClass:cls.class];
    }];
    
    if (list.count == count) {
        [list enumerateObjectsUsingBlock:^(UIView * obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (handler) {
                handler(obj);
            }
        }];
        return list;
    }
    
    for (UIView *view in self.subviews) {
        if ([view isMemberOfClass:cls]) {
            [view removeFromSuperview];
        }
    }

    NSMutableArray *marr = [NSMutableArray array];
    for (NSInteger i = 0; i < count; i++) {
        UIView *subview = [[cls alloc]init];
        subview.tag = i;
        
        [self addSubview:subview];
        [marr addObject:subview];
        if (handler) {
            handler(subview);
        }
    }
    return marr;
}

- (NSArray<__kindof UIButton *> *)updateButtonItems:(NSInteger)count aClassName:(NSString *)aClassName handler:(void(^)(__kindof UIButton *obj))handler {
    return [self updateItems:count aClassName:aClassName handler:^(__kindof UIView * _Nonnull obj) {
        if (![obj isKindOfClass:UIButton.class]) {
            return;
        }
//        NSString *clsName = NSStringFromClass(obj.class);
        UIButton *sender = (UIButton *)obj;
        if (![sender titleForState:UIControlStateNormal]) {
            sender.titleLabel.font = [UIFont systemFontOfSize:15];
            NSString *title = [NSString stringWithFormat:@"%@%@", aClassName, @(obj.tag)];
            [sender setTitle:title forState:UIControlStateNormal];
            [sender setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
        }
        if (handler) {
            handler(obj);
        }
    }];
}

@end

//
//  NSArray+Ext.m
//  Xcode11Project
//
//  Created by Bin Shang on 2020/6/20.
//  Copyright © 2020 Bin Shang. All rights reserved.
//

#import "NSArray+Ext.h"

@implementation NSArray (Ext)

- (NSArray *)map:(id (^)(id obj, NSUInteger idx))handler{
    __block NSMutableArray *marr = [NSMutableArray array];
    [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (handler) {
            id blockResult = handler(obj, idx) ? : obj;
            [marr addObject:blockResult];
        }
    }];
//    DDLog(@"%@->%@", self, marr.copy);
    return marr.copy;
}

- (NSArray *)filter:(BOOL(^)(id obj, NSUInteger idx))handler{
    __block NSMutableArray *marr = [NSMutableArray array];
    [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (handler && handler(obj, idx) == true) {
            [marr addObject:obj];
        }
    }];
    return marr.copy;
}

- (NSNumber *)reduce:(NSNumber *(^)(NSNumber *num1, NSNumber *num2))handler{
    __block CGFloat result = 0.0;
    [self enumerateObjectsUsingBlock:^(NSNumber *_Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (idx < self.count - 1) {
            NSNumber *num1 = idx == 0 ? obj : @(result);
            NSNumber *num2 = self[idx+1];
            if (handler) {
                result = handler(num1, num2).floatValue;
//                DDLog(@"handler_%@_%@_%@_%@",num1, num2, handler(num1, num2), @(result));
            }
        }
    }];
    return @(result);
}

@end

🌰🌰:
//
//  NNHomeViewController.m
//  MasonryExtend_Example
//
//  Created by Bin Shang on 2019/12/13.
//  Copyright © 2019 shang1219178163. All rights reserved.
//

#import "NNHomeViewController.h"
//#import "Masonry.h"
#import "MasonryExtend.h"

#import "UIView+Ext.h"
#import "UIButton+Ext.h"

@interface NNHomeViewController ()

@end

@implementation NNHomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.edgesForExtendedLayout = UIRectEdgeNone;
    self.title = @"Sudoku";
        
    NSArray *list = [self.view updateItems:9 aClassName:@"NXButton" handler:^(UIView * _Nonnull obj) {
        if (![obj isKindOfClass:UIButton.class]) {
            return;
        }
        NSString *clsName = NSStringFromClass(obj.class);
        UIButton *sender = (UIButton *)obj;
        sender.titleLabel.font = [UIFont systemFontOfSize:15];
        NSString *title = [NSString stringWithFormat:@"%@%@", clsName, @(obj.tag)];
        [sender setTitle:title forState:UIControlStateNormal];
        [sender setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
        [sender setBackgroundColor:UIColor.whiteColor forState:UIControlStateNormal];
        [sender setBackgroundColor:UIColor.systemBlueColor forState:UIControlStateHighlighted];
        [sender setBackgroundColor:UIColor.grayColor forState:UIControlStateDisabled];
    }];

    [list mas_distributeSudokuViewsWithFixedLineSpacing:5
                                  fixedInteritemSpacing:5
                                              warpCount:3
                                                  inset:UIEdgeInsetsMake(10, 10, 10, 10)];
    
//    [list mas_distributeSudokuViewsWithFixedItemWidth:120
//                                      fixedItemHeight:120
//                                            warpCount:3
//                                                inset:UIEdgeInsetsMake(10, 10, 10, 10)];

    self.view.backgroundColor = UIColor.systemGreenColor;
}

@end

Swift 源码
Objc 源码

Simulator Screen Shot - iPhone 6s Plus - 2020-06-25 at 16.22.20.png Simulator Screen Shot - iPhone 6s Plus - 2020-06-25 at 16.22.47.png
上一篇下一篇

猜你喜欢

热点阅读