在layoutSubviews()内部设置子层的框架使得无法进行更改。任何解决方法?

时间:2016-08-19 12:07:07

标签: ios swift layout uitextfield

我正在开发一个可自定义的UITextField(参见下面的代码)。我在底部添加了一个边框(您可以在故事板中设置它)。但是,我在设置此边框包含的CALayer框架时遇到了问题。

如果我在var showBottomBorder的{​​{1}}方法中设置它,它就不会出现在屏幕上。我认为这是因为尚未计算(UITextField的)框架(在此之前可能会调用didSet)。

所以我把它移到layoutSubviews()方法(见下面的代码)。这非常有效。

但现在我有另一个问题。我不能再真正改变那个框架了。每当我更改它时,它都会被layoutSubviews()重置,我认为这是{。}}。

在我的代码底部,有方法textFieldDidBeginEditing。在那里,我想向上移动我的底部边框(动画)。但它不起作用。边界不会移动到任何地方。就像我说的那样,我认为这是因为我在layoutSubviews()方法中设置了框架。

有没有更好的方法来设置底部边框的框架?一种允许我改变东西的方法吗?

@IBDesignable
class CustomizableTextField: UITextField, UITextFieldDelegate {

    // MARK: - Properties
    private var bottomBorder = CALayer()

    // MARK: - @IBInspectables
    @IBInspectable var roundCorners: CGFloat = 0 {
        didSet {
            self.layer.cornerRadius = roundCorners
            self.clipsToBounds = true
        }
    }

    /** -- */
    @IBInspectable var borderWidth: CGFloat = 1.0 {
        didSet {
            self.layer.borderWidth = self.borderWidth
        }
    }
    @IBInspectable var borderColor: UIColor = UIColor.white {
        didSet {
            self.layer.borderColor = self.borderColor.cgColor
        }
    }
    /** -- */

    /** -- */
    private var showBottomBorder: Bool = false {
        didSet {
            switch showBottomBorder {
            case true:
                bottomBorder.borderColor = self.bottomBorderColor.cgColor
                bottomBorder.borderWidth = self.bottomBorderWidth

                self.layer.addSublayer(bottomBorder)
                self.layer.masksToBounds = true
                break
            case false:
                bottomBorder.removeFromSuperlayer()
                break
            }
        }
    }
    @IBInspectable var bottomBorderWidth: CGFloat = 1.0 {
        didSet {
            self.showBottomBorder = false
            self.showBottomBorder = true
        }
    }
    @IBInspectable var bottomBorderColor: UIColor = UIColor.white {
        didSet {
            self.showBottomBorder = false
            self.showBottomBorder = true
        }
    }
    /** -- */

    /** -- */
    // Somwhow, the default panel for my font color doesn't change anything, so I created this
    @IBInspectable var fixedFontColor: UIColor = UIColor.white {
        didSet {
            self.textColor = fixedFontColor
        }
    }
    @IBInspectable var placeholderFontColor: UIColor = UIColor.white {
        didSet {
            var placeholderTxt = ""
            if let txt = self.placeholder {
                placeholderTxt = txt
            }

            self.attributedPlaceholder = NSAttributedString(string: placeholderTxt, attributes: [NSForegroundColorAttributeName: placeholderFontColor])
        }
    }
    /** -- */

    // MARK: - Overrides and Initializers
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        // HERE
        bottomBorder.frame = CGRect(x: 0, y: self.frame.size.height - self.bottomBorderWidth, width:  self.frame.size.width, height: self.frame.size.height)
    }

    // setting the textField delegate to self
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        //self.borderStyle = .none
        self.delegate = self
    }

    // MARK: - Events
    func textFieldDidBeginEditing(_ textField: UITextField) {

    }

1 个答案:

答案 0 :(得分:1)

您可以在UITextFiled上使用扩展程序来设置边框。

并使用 KVC 保留对它的引用。

通过覆盖 LayoutSubview ,每个布局都会发生变化,我们会检查边框是否存在,如果是,请将其删除,然后使用新框架重新创建一个新边框:

import UIKit

let MyTopBorder = "myTopBorder"
let MyBottomBorder = "myBottomBorder"
struct Defaults {
    static let width = CGFloat(1.0)
    static func bottonBorderFrame(view: UIView)->CGRect {
     return CGRect(x: CGFloat(0), y: view.frame.size.height - Defaults.width,   width:  view.frame.size.width, height: view.frame.size.height)
    }
    static func topBorderFrame(view: UIView)->CGRect {
        return CGRect(x: CGFloat(0), y: CGFloat(0) ,   width:  view.frame.size.width, height: Defaults.width)
    }
}

extension UITextField
{
    func setBottomBorder(color:CGColor)
    {
        if let isBottomBorder = self.getBottomBorderIfExists() {
            isBottomBorder.removeFromSuperlayer()
        }
        self.setBorderWithFrame(Defaults.bottonBorderFrame(self), color: color, andKey: MyBottomBorder)
    }

    func setTopBorder(color:CGColor)
    {
        if let isTopBorder = self.getTopBorderIfExists() {
            isTopBorder.removeFromSuperlayer()
        }
        self.setBorderWithFrame(Defaults.topBorderFrame(self), color: color, andKey: MyTopBorder)
    }

    func setBorderWithFrame(frame: CGRect, color: CGColor, andKey: String) {
        self.borderStyle = UITextBorderStyle.None;
        let border = CALayer()
        border.borderColor = color
        border.frame = frame
        border.borderWidth = Defaults.width
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true
        self.layer.setValue(border, forKey: andKey)
    }

    func removeTopBorder() {
        if let isTopBorder = self.getTopBorderIfExists() {
            self.layer.setValue(nil, forKey: MyTopBorder)
            isTopBorder.removeFromSuperlayer()
        }
    }

    func removeBottomBorder() {
        if let isBottomBorder = self.getBottomBorderIfExists() {
            self.layer.setValue(nil, forKey: MyBottomBorder)
            isBottomBorder.removeFromSuperlayer()
        }
    }

    private func getBorderIfExistsByKey(key: String)->CALayer? {
        if let isBorderSet = self.layer.valueForKey(key) {
            if let borderIsCALayer = isBorderSet as? CALayer {
                return borderIsCALayer
            }
        }
        return nil
    }

    private func getTopBorderIfExists()->CALayer? {
        return self.getBorderIfExistsByKey(MyTopBorder)
    }

    private func getBottomBorderIfExists()->CALayer? {
        return self.getBorderIfExistsByKey(MyBottomBorder)
    }

    public override func layoutSubviews() {
        super.layoutSubviews()

        // Update bottom on frame change
        if let isBottomBorder = self.getBottomBorderIfExists() {
            let borderColor = isBottomBorder .borderColor
            self.removeBottomBorder()
            self.setBottomBorder(borderColor!)
        }
        // Update top on frame change
        if let isTopBorder = self.getTopBorderIfExists() {
            let borderColor = isTopBorder.borderColor
            self.removeTopBorder()
            self.setTopBorder(borderColor!)
        }
    }
}

<强>用法:

  let textField = UITextField(frame: CGRect(x: 100,y: 100, width: 100, height: 100))
        textField.backgroundColor = UIColor.blueColor() // Thie color is for visulizing better
        self.view.addSubview(textField)
        textField.setBottomBorder(UIColor.blackColor().CGColor) // Now you have a border
        textField.frame = CGRect(x: 150, y: 200, width: 200, height: 200) // And the border updated to the new frame
        // Now if you would like to change from bottom to top, simply do this:
        textField.removeBottomBorder()
        textField.setTopBorder(UIColor.blackColor().CGColor)