하위 클래스를 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")
}
내 질문은 어디에 내 텍스트 필드 등을 만들어야 하는가입니다. 프레임과 코더를 구현하지 않으면 어떻게 “숨길”수 있습니까?
답변
나는 보통 이와 같은 일을합니다. 약간 장황합니다.
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()
(편집 : 이니셜 라이저 간의 관계가 더 명확하도록 내 대답을 편집했습니다)
답변
이것은 더 간단합니다.
override init (frame : CGRect) {
super.init(frame : frame)
// Do what you want.
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
답변
사용자 지정 UIView 하위 클래스 예제
저는 보통 스토리 보드 나 펜촉을 사용하지 않고 iOS 앱을 만듭니다. 질문에 답하기 위해 배운 몇 가지 기술을 공유하겠습니다.
원치 않는 init
방법 숨기기
첫 번째 제안은 UIView
원치 않는 이니셜 라이저를 숨기는 기본 을 선언하는 것 입니다. “UI 하위 클래스에서 Storyboard 및 Nib 특정 이니셜 라이저를 숨기는 방법”에 대한 제 답변 에서이 접근 방식에 대해 자세히 설명했습니다 . 참고 :이 접근 방식 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에 대해 그렇게 할 것입니다. 다음과 같이 subview 속성의 선언 내에서 하위 뷰를 인스턴스화하는 것을 선호합니다 let textField = UITextField()
.
을 호출하여 사용자 정의보기의 하위보기 목록에 추가하지 않는 한 UITextField가 표시되지 않습니다 addSubview(_:)
. 이것은 아래 예에서 설명됩니다.
자동 레이아웃이없는 프로그래밍 레이아웃
UITextField는 크기와 위치를 설정하지 않는 한 표시되지 않습니다. 나는 종종 layoutSubviews 메서드 내에서 코드 (자동 레이아웃을 사용하지 않음)로 레이아웃을 수행합니다 . layoutSubviews()
처음에 그리고 크기 조정 이벤트가 발생할 때마다 호출됩니다. 이를 통해 CustomView의 크기에 따라 레이아웃을 조정할 수 있습니다. 예를 들어 CustomView가 다양한 크기의 iPhone 및 iPad에서 전체 너비로 표시되고 회전에 맞게 조정되는 경우 많은 초기 크기를 수용하고 동적으로 크기를 조정해야합니다.
참조를 위해 CustomView의 치수를 가져 오기 위해 frame.height
및 frame.width
내부 layoutSubviews()
를 참조 할 수 있습니다 . 이것은 아래 예에서 설명됩니다.
UIView 하위 클래스의 예
구현할 필요가없는 UITextField를 포함하는 사용자 정의 UIView 하위 클래스 init?(coder:)
입니다.
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)
}
}
자동 레이아웃이있는 프로그래밍 레이아웃
코드에서 자동 레이아웃을 사용하여 레이아웃을 구현할 수도 있습니다. 자주하지 않기 때문에 예를 보여주지 않겠습니다. Stack Overflow 및 인터넷의 다른 곳에서 코드에서 자동 레이아웃을 구현하는 예제를 찾을 수 있습니다.
프로그래밍 방식 레이아웃 프레임 워크
코드에서 레이아웃을 구현하는 오픈 소스 프레임 워크가 있습니다. 내가 관심이 있지만 시도하지 않은 것은 LayoutKit 입니다. LinkedIn 개발 팀이 작성했습니다. Github 저장소에서 : “LinkedIn은 자동 레이아웃이 스크롤 가능한보기의 복잡한보기 계층 구조에 대해 성능이 충분하지 않다는 것을 발견했기 때문에 LayoutKit을 만들었습니다.”
넣어 이유 fatalError
에init(coder:)
스토리 보드 또는 펜촉에서 사용되지 않는 UIView 하위 클래스를 만들 때 init(coder:)
메서드에서 호출 할 수없는 다른 매개 변수와 초기화 요구 사항이있는 이니셜 라이저를 도입 할 수 있습니다 . init (coder :)를으로 실패하지 않은 fatalError
경우 실수로 스토리 보드 / 니브에서 사용하는 경우 매우 혼란스러운 문제가 발생할 수 있습니다. fatalError는 이러한 의도를 주장합니다.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
코드 또는 스토리 보드 / 펜촉으로 생성되었는지 여부에 관계없이 하위 클래스가 생성 될 때 일부 코드를 실행하려면 다음과 같이 할 수 있습니다 ( Jeff Gu Kang의 답변에 따라 )
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
}
}
답변
인터페이스 빌더 / 스토리 보드 또는 코드에서 UIView를 생성하는 것이 중요합니다. setup
설정 코드 중복을 줄이는 방법 이 있으면 유용 합니다. 예 :
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
}
}
답변
Swift 4.0, xib 파일에서보기를 사용하려면 이것은 당신을위한 것입니다. UIView의 CustomCalloutView 클래스 Sub 클래스를 만들었습니다. xib 파일을 만들었고 IB에서 파일 소유자를 선택한 다음 Attribute inspector 클래스 이름을 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)
답변
다음은 일반적으로 하위 클래스 (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)
}
}
답변
