1
\$\begingroup\$

There are many side controllers in my project, like the following images. They share the same head view appearance and layout.

0

8

Use protocol to extract common code .

protocol SideHeaderDelegate: class {

    var h: SideHeader{ get set }
    var view: UIView! {get}
    var follow: UIView {get}

    func back()
}

Use protocol extension to extract the common layout code.

extension SideHeaderDelegate{

    func doLayout(_ offset: CGFloat = 0){
        view.addSubview(h)
        view.addSubview(follow)
        let top: CGFloat = 40
        h.translatesAutoresizingMaskIntoConstraints = false
        follow.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            h.topAnchor.constraint(equalTo: view.topAnchor),
            h.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            h.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            h.heightAnchor.constraint(equalToConstant: top)
        ])

        NSLayoutConstraint.activate([
            follow.topAnchor.constraint(equalTo: view.topAnchor, constant: top),
            follow.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            follow.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            follow.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: offset)
        ])

    }

}

Code of the common head appearance:

class SideHeader: UIView {


    weak var delegate: SideHeaderDelegate?

    let title: String
    let avatar: String

    lazy var headline = { () -> UILabel in
        let lbl = UILabel()
        lbl.textAlignment = .center
        lbl.text = title
        return lbl
    }()

    lazy var arrow = { () -> UIImageView in
        let img = UIImageView()
        img.image = UIImage(named: avatar)
        img.isUserInteractionEnabled = true
        return img
    }()

    let line: UIView = {
        let string = UIView()
        string.backgroundColor = UIColor(rgb: 0xD8D8D8)
        return string
    }()

    init(title name: String, icon: String = "mine_fork") {
        title = name
        avatar = icon
        super.init(frame: CGRect.zero)
        backgroundColor = UIColor.white
        headline.translatesAutoresizingMaskIntoConstraints = false
        arrow.translatesAutoresizingMaskIntoConstraints = false
        line.translatesAutoresizingMaskIntoConstraints = false

        addSubview(headline)
        addSubview(arrow)
        addSubview(line)

        NSLayoutConstraint.activate([
            headline.centerXAnchor.constraint(equalTo: centerXAnchor),
            headline.topAnchor.constraint(equalTo: topAnchor, constant: 10)
        ])

        NSLayoutConstraint.activate([
            arrow.centerYAnchor.constraint(equalTo: centerYAnchor),
            arrow.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
            arrow.widthAnchor.constraint(equalToConstant: 26),
            arrow.heightAnchor.constraint(equalToConstant: 26)
        ])


        NSLayoutConstraint.activate([
            line.bottomAnchor.constraint(equalTo: bottomAnchor),
            line.leadingAnchor.constraint(equalTo: leadingAnchor),
            line.trailingAnchor.constraint(equalTo: trailingAnchor),
            line.heightAnchor.constraint(equalToConstant: 1)
        ])

        let tap = UITapGestureRecognizer()
        arrow.addGestureRecognizer(tap)
        tap.addTarget(self, action: #selector(SideHeader.pop))
    }



    func change(title name: String){
        headline.text = name
    }

    @objc
    func pop(){
        delegate?.back()
    }
}

Usage code:

one controller instance

class VIPCenterCtrl: UIViewController, SideHeaderDelegate {

    lazy var follow: UIView = { () -> UITableView in
        let tb = UITableView(frame: CGRect.zero, style: UITableView.Style.grouped)
        tb.register(for: VIPCenterCell.self)
        tb.separatorStyle = .none
        tb.delegate = self
        tb.dataSource = self
        return tb
    }()


    lazy var h = { () -> SideHeader in
        let head = SideHeader(title: "VIP 中心")
        head.delegate = self
        return head
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        doLayout()
        // ...
    }


    func refresh(){
         // ... 
         let vCenter = self.follow as! UITableView
         vCenter.reloadData()
    }
}

I want some suggestions. Do it more Swifter.

The refresh data part is not very elegant. I need cast it first, from UIView to UITableView

More code on github

\$\endgroup\$
2
\$\begingroup\$

U can go forward.

111

To simplify the question, you can handle the top part like this

protocol SideH: class{

    var headline: UILabel! {get set}
    var arrow: UIImageView! {get set}
    var line: UIView! {get set}

    // provided by UIViewController
    var view: UIView! { get }

}



extension SideH{


    func layout(t title: String){
        view.backgroundColor = UIColor.white
        headline = { () -> UILabel in
            let lbl = UILabel()
            lbl.font = UIFont.regular(ofSize: 18)
            lbl.textColor = UIColor.textHeavy
            lbl.textAlignment = .center
            lbl.text = title
            return lbl
        }()


        arrow = { () -> UIImageView in
            let img = UIImageView()
            img.image = UIImage(named: "mine_fork")
            img.isUserInteractionEnabled = true
            return img
        }()

        line = {
            let string = UIView()
            string.backgroundColor = UIColor(rgb: 0xD8D8D8)
            return string
        }()

        ///

        view.addSubs([headline, arrow, line])

        headline.snp.makeConstraints { (maker) in
            maker.centerX.equalToSuperview()
            maker.top.equalToSuperview().offset(20)
            maker.size.equalTo(CGSize(width: 80 + 15, height: 25))
        }

        arrow.snp.makeConstraints { (maker) in
            maker.size.equalTo(CGSize(width: 30, height: 30))
            maker.top.equalToSuperview().offset(17)
            maker.leading.equalToSuperview().offset(16)
        }

        line.snp.makeConstraints { (maker) in
            maker.leading.trailing.equalToSuperview()
            maker.height.equalTo(1)
            maker.top.equalToSuperview().offset(63)
        }

    }


    func back(){
        if let ctrl = view.parentViewController{
            let tap = UITapGestureRecognizer()
            arrow.addGestureRecognizer(tap)

            tap.rx.event.bind { (event) in
                NotificationCenter.default.post(name: .close, object: nil)
            }.disposed(by: ctrl.rx.disposeBag)
        }

    }

}

The RxSwift part is a little anoying.

If u call it in UIViewController,

  // self.rx.disposeBag, or self has a DisposeBag
  tap.rx.event.bind { (event) in
               }.disposed(by: rx.disposeBag)

extension code part:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder?.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}


extension UIView{


    func addSubs(_ views: [UIView]){
        views.forEach(addSubview(_:))
    }

}


extension Notification.Name {
      static let close = Notification.Name("close")
}

call like this:

class OnUs: UIViewController, SideH{
    // the 3 props, just decorates
    var headline: UILabel!
    var arrow: UIImageView!
    var line: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        layout(t: "关于我们")

        back()

    }

now u had UI Appearance, UI layout , target action with ( RxSwift, NSObject+Rx)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.