Swift子类UIView

时间:2014-11-21 08:18:44

标签: ios swift

我刚刚开始使用Swift。我已经阅读了这本书,而且在学习的过程中我学得更好。我开始做的很简单。

我想继承UIView并显示类似于视图的登录信息。我在Objective-C中创建了这个,但现在我想把它移植到Swift。我不使用故事板,所以我在代码中创建了所有的UI。

但第一个问题是我必须实施initWithCoder。我给它一个默认的实现,因为它不会被调用。现在,当我运行该程序时,它崩溃了,因为我也要实现initWithFrame。现在我明白了:

override init() {
    super.init()
    println("Default init")
}

override init(frame: CGRect) {
    super.init(frame: frame)
    println("Frame init")
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    println("Coder init")
}

我的问题是我应该在哪里创建我的文本字段等...如果我从未实现框架和编码器,我怎样才能隐藏"此?

6 个答案:

答案 0 :(得分:163)

我经常这样做,有点冗长。

class MyView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        addBehavior()
    }

    convenience init() {
        self.init(frame: CGRect.zero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("This class does not support NSCoding")
    }

    func addBehavior() {
        print("Add all the behavior here")
    }
}



let u = MyView(frame: CGRect.zero)
let v = MyView()

(编辑:我已经编辑了我的答案,以便初始化者之间的关系更加明确)

答案 1 :(得分:15)

这更简单。

override init (frame : CGRect) {
    super.init(frame : frame)
    // Do what you want.
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

答案 2 :(得分:9)

自定义UIView子类示例

我通常在不使用故事板或笔尖的情况下创建iOS应用程序。我将分享一些我学会回答你问题的技巧。

隐藏不需要的init方法

我的第一个建议是声明一个基础UIView来隐藏不需要的初始值设定项。我在my answer to "How to Hide Storyboard and Nib Specific Initializers in UI Subclasses"中详细讨论了这种方法。注意:此方法假设您不会在故事板或笔尖中使用BaseView或其后代,因为它会故意导致应用程序崩溃。

class BaseView: UIView {

    // This initializer hides init(frame:) from subclasses
    init() {
        super.init(frame: CGRect.zero)
    }

    // This attribute hides `init(coder:)` from subclasses
    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
}

您的自定义UIView子类应继承自BaseView。它必须在其初始化程序中调用super.init()。它不需要实现init(coder:)。这在以下示例中进行了演示。

添加UITextField

我为init方法之外引用的子视图创建存储属性。我通常会为UITextField这样做。我更喜欢在子视图属性的声明中实例化子视图,如下所示:let textField = UITextField()

除非您通过调用addSubview(_:)将其添加到自定义视图的子视图列表中,否则UITextField将不可见。这在以下示例中进行了演示。

无自动布局的编程布局

除非您设置其大小和位置,否则UITextField将不可见。我经常在layoutSubviews method内进行代码布局(不使用自动布局)。最初调用layoutSubviews()并且每当调整resize事件时调用frame.height。这允许根据CustomView的大小调整布局。例如,如果CustomView在各种尺寸的iPhone和iPad上显示全宽并调整旋转,则需要适应许多初始尺寸并动态调整大小。

您可以参考frame.width中的layoutSubviews()init?(coder:)来获取CustomView的尺寸以供参考。这在以下示例中进行了演示。

示例UIView子类

包含UITextField的自定义UIView子类,不需要实现class CustomView: BaseView { let textField = UITextField() override init() { super.init() // configure and add textField as subview textField.placeholder = "placeholder text" textField.font = UIFont.systemFont(ofSize: 12) addSubview(textField) } override func layoutSubviews() { super.layoutSubviews() // Set textField size and position textField.frame.size = CGSize(width: frame.width - 20, height: 30) textField.frame.origin = CGPoint(x: 10, y: 10) } }

fatalError

具有自动布局的编程布局

您还可以在代码中使用自动布局实现布局。由于我不经常这样做,我不会举一个例子。您可以在Stack Overflow上的代码和Internet上的其他地方找到实现Auto Layout的示例。

程序化布局框架

有开源框架在代码中实现布局。我感兴趣但尚未尝试的是LayoutKit。它是由开发团队编写的LinkedIn。来自Github存储库:“LinkedIn创建了LayoutKit,因为我们发现自动布局对于可滚动视图中的复杂视图层次结构而言不够高效。”

为什么将init(coder:)放在init(coder:)

创建永远不会在故事板或nib中使用的UIView子类时,可能会引入具有fatalError方法无法调用的不同参数和初始化要求的初始值设定项。如果你没有使用required init?(coder aDecoder: NSCoder) { fatalError("NSCoding not supported") } 使init(编码器:)失败,如果在故事板/笔尖中意外使用,可能会导致非常混乱的问题。 fatalError断言了这些意图。

class CustomView: UIView {
    override init (frame: CGRect) {
        super.init(frame: frame)
        initCommon()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initCommon()
    }

    func initCommon() {
        // Your custom initialization code
    }
}

如果要在创建子类时运行某些代码,无论它是在代码中创建还是在storyboard / nib中创建,那么您可以执行以下操作(基于Jeff Gu Kang’s answer

Standalone.java:22: error: package org.apache.commons.collections15 does not exist 

答案 3 :(得分:4)

您的UIView可以通过界面构建​​器/故事板或代码创建。我发现使用class RedView: UIView { override init (frame: CGRect) { super.init(frame: frame) setup() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! setup() } func setup () { backgroundColor = .red } } 方法来减少重复任何设置代码是很有用的。 e.g。

### Download current LOVE framework executable ###

# By default PowerShell supports only SSL3 and TLS1.0, add TLS1.1 and TLS1.2 support.
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'

# Get current version of the LOVE framework

$request = Invoke-WebRequest "https://love2d.org/"
$downloadUri = (Select-String -InputObject $request.Content -Pattern "https://bitbucket\.org/rude/love/downloads/love-.*-win64\.zip").Matches[0].Value
$loveBinaryStream = (Invoke-WebRequest $downloadUri).RawContentStream

# Extract love.exe
Add-Type -AssemblyName System.IO.Compression
$zipArchive = New-Object System.IO.Compression.ZipArchive($loveBinaryStream)
$zipEntry = $zipArchive.Entries | ? { $_.Name -eq "love.exe" }

$stream = $zipEntry.Open()
$sr = New-Object System.IO.StreamReader($stream)
$sw = New-Object System.IO.StreamWriter("love.exe")
$sw.Write($sr.ReadToEnd())
$sw.Close()
$sr.Close()
$stream.Close()

答案 4 :(得分:2)

Swift 4.0,如果你想使用xib文件中的视图,那么这是给你的。我创建了UIView的CustomCalloutView类Sub类。我创建了一个xib文件,在IB中只选择文件所有者,然后选择属性检查器设置类名到CustomCalloutView,然后在你的类中创建出口。

    import UIKit
    class CustomCalloutView: UIView {

        @IBOutlet var viewCallout: UIView! // This is main view

        @IBOutlet weak var btnCall: UIButton! // subview of viewCallout
        @IBOutlet weak var btnDirection: UIButton! // subview of viewCallout
        @IBOutlet weak var btnFavourite: UIButton! // subview of viewCallout 

       // let nibName = "CustomCalloutView" this is name of xib file

        override init(frame: CGRect) {
            super.init(frame: frame)
            nibSetup()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            nibSetup()
        }

        func nibSetup() {
            Bundle.main.loadNibNamed(String(describing: CustomCalloutView.self), owner: self, options: nil)
            guard let contentView = viewCallout else { return } // adding main view 
            contentView.frame = self.bounds //Comment this line it take default frame of nib view
           // custom your view properties here
            self.addSubview(contentView)
        }
    }

//现在添加

    let viewCustom = CustomCalloutView.init(frame: CGRect.init(x: 120, y: 120, 50, height: 50))
    self.view.addSubview(viewCustom)

答案 5 :(得分:-1)

以下是我通常如何构建子类(UIView)的示例。我将内容作为变量,因此可以稍后在其他类中访问和调整它们。我还展示了如何使用自动布局和添加内容。

例如,在ViewController中,我在ViewDidLoad()中初始化了此视图,因为只有在视图可见时才调用一次。然后我使用我在这里创建的这些函数addContentToView()然后activateConstraints()来构建内容并设置约束。如果我稍后在ViewController中想要让按钮变成红色的颜色,我只是在那个ViewController中的特定函数中执行此操作。 类似于:func tweaksome(){ self.customView.someButton.color = UIColor.red}

class SomeView: UIView {


var leading: NSLayoutConstraint!
var trailing: NSLayoutConstraint!
var bottom: NSLayoutConstraint!
var height: NSLayoutConstraint!


var someButton: UIButton = {
    var btn: UIButton = UIButton(type: UIButtonType.system)
    btn.setImage(UIImage(named: "someImage"), for: .normal)
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

var btnLeading: NSLayoutConstraint!
var btnBottom: NSLayoutConstraint!
var btnTop: NSLayoutConstraint!
var btnWidth: NSLayoutConstraint!

var textfield: UITextField = {
    var tf: UITextField = UITextField()
    tf.adjustsFontSizeToFitWidth = true
    tf.placeholder = "Cool placeholder"
    tf.translatesAutoresizingMaskIntoConstraints = false
    tf.backgroundColor = UIColor.white
    tf.textColor = UIColor.black
    return tf
}()
var txtfieldLeading: NSLayoutConstraint!
var txtfieldTrailing: NSLayoutConstraint!
var txtfieldCenterY: NSLayoutConstraint!

override init(frame: CGRect){
    super.init(frame: frame)
    self.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    //fatalError("init(coder:) has not been implemented")
}



/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
    // Drawing code

}
*/
func activateConstraints(){
    NSLayoutConstraint.activate([self.btnLeading, self.btnBottom, self.btnTop, self.btnWidth])
    NSLayoutConstraint.activate([self.txtfieldCenterY, self.txtfieldLeading, self.txtfieldTrailing])
}

func addContentToView(){
    //setting the sizes
    self.addSubview(self.userLocationBtn)

    self.btnLeading = NSLayoutConstraint(
        item: someButton,
        attribute: .leading,
        relatedBy: .equal,
        toItem: self,
        attribute: .leading,
        multiplier: 1.0,
        constant: 5.0)
    self.btnBottom = NSLayoutConstraint(
        item: someButton,
        attribute: .bottom,
        relatedBy: .equal,
        toItem: self,
        attribute: .bottom,
        multiplier: 1.0,
        constant: 0.0)
    self.btnTop = NSLayoutConstraint(
        item: someButton,
        attribute: .top,
        relatedBy: .equal,
        toItem: self,
        attribute: .top,
        multiplier: 1.0,
        constant: 0.0)
    self.btnWidth = NSLayoutConstraint(
        item: someButton,
        attribute: .width,
        relatedBy: .equal,
        toItem: self,
        attribute: .height,
        multiplier: 1.0,
        constant: 0.0)        


    self.addSubview(self.textfield)
    self.txtfieldLeading = NSLayoutConstraint(
        item: self.textfield,
        attribute: .leading,
        relatedBy: .equal,
        toItem: someButton,
        attribute: .trailing,
        multiplier: 1.0,
        constant: 5)
    self.txtfieldTrailing = NSLayoutConstraint(
        item: self.textfield,
        attribute: .trailing,
        relatedBy: .equal,
        toItem: self.doneButton,
        attribute: .leading,
        multiplier: 1.0,
        constant: -5)
    self.txtfieldCenterY = NSLayoutConstraint(
        item: self.textfield,
        attribute: .centerY,
        relatedBy: .equal,
        toItem: self,
        attribute: .centerY,
        multiplier: 1.0,
        constant: 0.0)
}
}