Reputation: 37
Error occurs when I use IBOutlet for connection to DefaultTextField in iOS 12.5.7. But if I use programmatic application, no error occurs.
This error usually occurs iPhone 6 and iPhone 6 Plus in iOS 12.5.7. Apple gives a meaningless error. When I use programmatic methods, no error occur. I think this error may be due to @IBDesignable.
Code with error like this.
@IBOutlet weak var usernameTextField: DefaultTextField!
@IBOutlet weak var passwordTextField: DefaultTextField!
Code without error like this.
private let usernameTextField: DefaultTextField = {
let textField = DefaultTextField()
textField.placeholder = "Üye No / TC Kimlik No"
textField.stringFormat = "XXXXXXXXXXX"
textField.keyboardType = .numberPad
textField.compareOption = .regularExpression
textField.autocapitalizationType = .none
textField.textContentType = .username
return textField
}()
private let passwordTextField: DefaultTextField = {
let textField = DefaultTextField()
textField.placeholder = "Şifre"
textField.rightIcon = R.Image.TJK.passwordTextFieldEyeIcon
textField.isSecureTextEntry = true
textField.autocapitalizationType = .none
textField.returnType = .done
return textField
}()
Error like this.
Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x1d528 objc_msgSend + 8
1 UIKitCore 0xd3e5e0 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 172
2 UIKitCore 0xd3e7c4 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
3 UIKitCore 0xd3e6d8 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 420
4 UIKitCore 0xd3e7c4 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
5 UIKitCore 0xd3e6d8 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 420
6 UIKitCore 0xd3e7c4 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
7 UIKitCore 0xd3e6d8 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 420
8 UIKitCore 0xd3e7c4 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
9 UIKitCore 0xd3e6d8 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 420
10 UIKitCore 0xd4dbf4 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 456
11 UIKitCore 0x3501a0 -[_UIParallaxDimmingView didMoveToWindow] + 176
12 UIKitCore 0xd4b760 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1604
13 UIKitCore 0xd4b3b4 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 664
14 UIKitCore 0xd3edc4 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 156
15 UIKitCore 0xd3ec8c -[UIView(Hierarchy) _postMovedFromSuperview:] + 760
16 UIKitCore 0xd4e0f8 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1740
17 UIKitCore 0x34b884 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke_2 + 1716
18 UIKitCore 0xd468f0 +[UIView(Animation) performWithoutAnimation:] + 104
19 UIKitCore 0x34b17c __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 228
20 UIKitCore 0xd4c87c +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:] + 220
21 UIKitCore 0x34ab98 -[_UINavigationParallaxTransition animateTransition:] + 1096
22 UIKitCore 0x271090 -[UINavigationController _startCustomTransition:] + 3484
23 UIKitCore 0x284714 -[UINavigationController _startDeferredTransitionIfNeeded:] + 708
24 UIKitCore 0x285b3c -[UINavigationController __viewWillLayoutSubviews] + 164
25 UIKitCore 0x268d4c -[UILayoutContainerView layoutSubviews] + 224
26 UIKitCore 0xd54170 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1292
27 QuartzCore 0x13ec60 -[CALayer layoutSublayers] + 184
28 QuartzCore 0x143c08 CA::Layer::layout_if_needed(CA::Transaction*) + 332
29 QuartzCore 0xa63e4 CA::Context::commit_transaction(CA::Transaction*) + 348
30 QuartzCore 0xd4620 CA::Transaction::commit() + 640
31 QuartzCore 0xd515c CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92
32 CoreFoundation 0xaa4fc __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
33 CoreFoundation 0xa5224 __CFRunLoopDoObservers + 412
34 CoreFoundation 0xa57a0 __CFRunLoopRun + 1228
35 CoreFoundation 0xa4fb4 CFRunLoopRunSpecific + 436
36 GraphicsServices 0xa79c GSEventRunModal + 104
37 UIKitCore 0x8bcc38 UIApplicationMain + 212
38 Hipodrom 0x80bc main + 15 (main.swift:15)
39 libdyld.dylib 0x18e0 start + 4
This is my custom component code.
import UIKit
@MainActor
@objc protocol DefaultTextFieldDelegate: AnyObject {
@objc optional func didTapRightButton(_ textField: DefaultTextField)
@objc optional func textFieldDidEndEditing(_ textField: DefaultTextField)
@objc optional func textField(_ textField: DefaultTextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
@objc optional func textFieldShouldReturn(_ textField: DefaultTextField) -> Bool
@objc optional func textFieldDidChange(_ textField: DefaultTextField)
@objc optional func textFieldDidBeginEditing(_ textField: DefaultTextField)
@objc optional func didTapDoneButton(_ textField: DefaultTextField)
}
@IBDesignable
@objc(DefaultTextField)
class DefaultTextField: UIView {
enum Status {
case `default`
case success
case failure(message: String?)
case focus
}
// MARK: - PROPERTIES(s)
var keyboardType: UIKeyboardType = .default {
didSet {
textField.keyboardType = keyboardType
}
}
var autocapitalizationType: UITextAutocapitalizationType = .words {
didSet {
textField.autocapitalizationType = autocapitalizationType
}
}
var status: Status = .default {
didSet {
handleRigthViewStatus(with: status)
}
}
var compareOption: NSString.CompareOptions?
var textContentType: UITextContentType? {
didSet {
textField.textContentType = textContentType
}
}
var returnType: UIReturnKeyType = .default {
didSet {
textField.returnKeyType = returnType
}
}
weak var delegate: DefaultTextFieldDelegate?
// MARK: - IBINSPECTABLE(s)
/// use with X
@IBInspectable
var stringFormat: String?
@IBInspectable
var autoLocalization: Bool = true
@IBInspectable
var placeholder: String? {
didSet {
placeholderLabel.text = placeholder
}
}
@IBInspectable
var isSecureTextEntry: Bool = false {
didSet {
textField.isSecureTextEntry = isSecureTextEntry
}
}
@IBInspectable
var rightIcon: UIImage? {
didSet {
rightButton.setImage(rightIcon, for: .normal)
}
}
@IBInspectable
var text: String? {
get {
textField.text
} set {
DispatchQueue.main.async {[weak self] in
self?.textField.text = newValue
self?.updateFloatingLabel()
}
}
}
// MARK: - COMPONENT(s)
var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 2
return stackView
}()
var backgroundView: UIView = {
let view = UIView()
view.layer.cornerRadius = 2.0
view.borderWidth = 1.0
view.layer.masksToBounds = true
return view
}()
var placeholderLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textColor = R.Color.gray100
label.textAlignment = .left
label.font = R.Fonts.SFCompactDisplay.medium(size: 16)
label.isUserInteractionEnabled = true
label.sizeToFit()
return label
}()
var errorLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.textColor = .red
label.textAlignment = .left
label.font = R.Fonts.SFCompactDisplay.regular(size: 12)
label.sizeToFit()
return label
}()
var rightButton: UIButton = {
let button = UIButton()
button.imageView?.contentMode = .scaleAspectFit
if #available(iOS 15.0, *) {
button.configuration = nil
}
button.tintColor = R.Color.TJK.loginDisable
return button
}()
var textField: UITextField = {
let textField = UITextField()
textField.borderStyle = .none
textField.tintColor = R.Color.textFieldTextColor
return textField
}()
// MARK: - INIT(s)
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupUI()
}
// MARK: - OVERRIDE(s)
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupUI()
}
@discardableResult
internal override func becomeFirstResponder() -> Bool {
let becameFirstResponder = super.becomeFirstResponder()
textField.becomeFirstResponder()
updateFloatingLabel()
return becameFirstResponder
}
@discardableResult
internal override func resignFirstResponder() -> Bool {
let resignedFirstResponder = super.resignFirstResponder()
textField.resignFirstResponder()
updateFloatingLabel()
return resignedFirstResponder
}
// MARK: - PUBLIC METHOD(s)
func stringFormatter(
_ text: String,
range: NSRange,
replacementString string: String) -> String {
let input = (text as NSString)
guard let stringFormat = stringFormat else { return "" }
let newString = input.replacingCharacters(in: range, with: string)
return newString.stringFormatted(with: stringFormat)
}
// MARK: - PRIVATE METHOD(s)
private func setupUI() {
// setup view
stackViewAddSubview()
backgroundViewAddSubview()
errorLabelAddSubview()
status = .default
// setup placeholder
placeholderAddSubview()
updatePlaceholder()
// setup textfield
textFieldAddSubview()
updateTextField()
textField.delegate = self
textField.tag = self.tag
textField.returnKeyType = returnType
// setup right button
rightButtonAddSubview()
updateFloatingLabel()
addToolbarToTextField()
if autoLocalization == true, let key = placeholder {
placeholderLabel.text = NSLocalizedString(key, comment: "")
}
// Targets
textField.addTarget(self, action: #selector(self.valueChanged), for: .editingDidBegin)
textField.addTarget(self, action: #selector(self.valueChanged), for: .editingDidEnd)
textField.addTarget(self, action: #selector(self.valueChanged), for: .editingChanged)
let placeholderTapped = UITapGestureRecognizer(target: self, action: #selector(self.placeholderTapped))
placeholderLabel.addGestureRecognizer(placeholderTapped)
}
private func stackViewAddSubview() {
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
private func backgroundViewAddSubview() {
stackView.addArrangedSubview(backgroundView)
backgroundView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
backgroundView.heightAnchor.constraint(equalToConstant: 45.0)
])
}
private func placeholderAddSubview() {
backgroundView.addSubview(placeholderLabel)
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
placeholderLabel.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 20.0),
placeholderLabel.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -32.0),
placeholderLabel.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor)
])
}
private func errorLabelAddSubview() {
stackView.addArrangedSubview(errorLabel)
errorLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
errorLabel.heightAnchor.constraint(equalToConstant: 18.0)
])
}
private func textFieldAddSubview() {
backgroundView.addSubview(textField)
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
textField.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 20.0),
textField.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -32.0),
textField.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor)
])
}
private func rightButtonAddSubview() {
backgroundView.addSubview(rightButton)
rightButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
rightButton.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -12.0),
rightButton.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor),
rightButton.widthAnchor.constraint(equalToConstant: 20)
])
rightButton.addTarget(self, action: #selector(tappedRightButton), for: .touchUpInside)
}
/// If the text is empty, it brings the placeholder to the middle. If it is full, it brings it up.
private func updateFloatingLabel() {
UIView.animate(withDuration: 0.2) {[weak self] in
guard let self = self else { return }
placeholderLabel.font = R.Fonts.SFCompactDisplay.semibold(size: 12)
if textField.text?.isEmpty == false {
self.placeholderLabel.transform = CGAffineTransform(
translationX: 0,
y: -(backgroundView.frame.height/2 - placeholderLabel.frame.height/2) + 4
)
self.textField.transform = CGAffineTransform(
translationX: 0,
y: (backgroundView.frame.height/2 - textField.frame.height/2) - 4
)
} else {
self.placeholderLabel.transform = .identity
self.textField.transform = .identity
self.placeholderLabel.font = R.Fonts.SFCompactDisplay.medium(size: 16)
}
setNeedsDisplay()
}
}
/// deletes the actual placeholder and writes it to placeholderLabel
private func updatePlaceholder() {
if placeholder?.isEmpty == false {
placeholderLabel.text = placeholder
placeholder = nil
}
}
/// deletes the actual text and writes it to text
private func updateTextField() {
if text?.isEmpty == false {
textField.text = text
text = nil
}
}
private func handleRigthViewStatus(with status: Status?) {
guard let status = status else { return }
switch status {
case .default:
backgroundView.viewBorderColor = R.Color.gray25
backgroundView.backgroundColor = R.Color.gray25
errorLabel.isHidden = true
case .success:
backgroundView.viewBorderColor = R.Color.gray25
backgroundView.backgroundColor = R.Color.gray25
errorLabel.isHidden = true
case .failure(let message):
backgroundView.viewBorderColor = R.Color.red25
backgroundView.backgroundColor = R.Color.red50
errorLabel.text = message
errorLabel.isHidden = message == nil
case .focus:
backgroundView.viewBorderColor = R.Color.gray200
backgroundView.backgroundColor = R.Color.gray25
errorLabel.isHidden = true
}
DispatchQueue.main.async {[weak self] in
self?.stackView.layoutIfNeeded()
}
}
func addToolbarToTextField() {
// Create the toolbar
let toolbar = UIToolbar()
toolbar.sizeToFit()
// Create a flexible space to push the button to the right
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
// Create the button
let doneButton = UIBarButtonItem(title: "Tamam", style: .done, target: self, action: #selector(doneButtonTapped))
doneButton.tintColor = R.Color.tjkRed
// Add the items to the toolbar
toolbar.items = [flexibleSpace, doneButton]
// Assign the toolbar to the text field's input accessory view
textField.inputAccessoryView = toolbar
}
// MARK: - OBJC(s)
@objc
private func valueChanged() {
updateFloatingLabel()
delegate?.textFieldDidChange?(self)
}
@objc
private func tappedRightButton() {
if rightIcon != nil {
delegate?.didTapRightButton?(self)
} else {
self.becomeFirstResponder()
}
}
@objc
private func doneButtonTapped() {
if let shouldTapped = delegate?.didTapDoneButton?(self) {
return shouldTapped
} else {
textField.endEditing(true)
}
}
@objc
private func placeholderTapped() {
self.becomeFirstResponder()
}
}
extension DefaultTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let shouldChange = delegate?.textField?(self, shouldChangeCharactersIn: range, replacementString: string) {
if !shouldChange {
updateFloatingLabel()
}
return shouldChange
} else {
return true
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
status = .default
delegate?.textFieldDidEndEditing?(self)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let shouldReturn = delegate?.textFieldShouldReturn?(self) {
return shouldReturn
} else {
return true
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
status = .focus
delegate?.textFieldDidBeginEditing?(self)
}
}
Upvotes: 0
Views: 31