I am lost.
I have a TableViewController that is filled programmatically from a collection. I need to show a form when the tableView is pulled down. Right now I have a InsertControl that contains two textFields.
InsertControl
class InsertControl: UIView {
//MARK: Properties
var name = ""
var quantity = 0
//MARK: Initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let nameTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 260, height: 44))
nameTextField.backgroundColor = UIColor.redColor()
let quantityTextField = UITextField(frame: CGRect(x: 270, y: 0, width: 60, height: 44))
quantityTextField.backgroundColor = UIColor.redColor()
addSubview(nameTextField)
addSubview(quantityTextField)
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: 350, height: 44)
}
}
then I found that I can put this control in the tableView header, so I have this code in my
tableViewController
let insertControl = InsertControl()
self.tableView.tableHeaderView = insertControl
self.tableView.contentOffset = CGPoint(x: 0, y: 44)
The problem is that I need to give to the InsertControl constructor a NSCoder which I don't have.
After I manage to get the form in the header, how can I pass a further swipe down to the control and make it add the content to the tableView collection?
I hope I was clear enough.
To answer your first question, in InsertControl, use override init(frame: CGRect) instead of required init?(coder aDecoder: NSCoder). It'll look like this:
override init(frame: CGRect) {
super.init(frame: frame)
let nameTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 260, height: 44))
nameTextField.backgroundColor = UIColor.redColor()
let quantityTextField = UITextField(frame: CGRect(x: 270, y: 0, width: 60, height: 44))
quantityTextField.backgroundColor = UIColor.redColor()
addSubview(nameTextField)
addSubview(quantityTextField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: 350, height: 44)
}
Related
When I create a UITextField using storyboard, it looks like the image below. However, when I create a UITextField programmatically it has absolutely no style. I know that I can apply custom styles to the text field, but is there an easy way to get this standard style when creating the text field programmatically?
Something like this:
let t = UITextField()
t.frame = CGRect(x: 10, y: 20, width: self.view.frame.width - 20, height: 40)
t.layer.cornerRadius = 5
t.layer.borderColor = UIColor.lightGray.cgColor
t.layer.borderWidth = 1
t.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: t.frame.height))
t.leftViewMode = .always
t.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: t.frame.height))
t.rightViewMode = .always
t.clearButtonMode = .whileEditing
self.view.addSubview(t)
EDIT:
Add this class below somewhere such that it is easily / globally accessible.
class StyledTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.borderWidth = 1
self.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: t.frame.height))
self.leftViewMode = .always
self.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: t.frame.height))
self.rightViewMode = .always
self.clearButtonMode = .whileEditing
}
}
Then you can call this UITextField from where ever you want as follows
let t = StyledTextField()
t.frame = CGRect(x: 10, y: 20, width: self.view.frame.width - 20, height: 40)
self.view.addSubview(t)
EDIT 2:
Use UIEdgeInsets to get padding on all four sides.
class StyledTextField: UITextField {
let insetConstant = UIEdgeInsets(top: 4, left: 10, bottom: 4, right: 10)
override func textRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, insetConstant)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, insetConstant)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, insetConstant)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor(white: 2/3, alpha: 0.5).cgColor
self.layer.borderWidth = 1
self.clearButtonMode = .whileEditing
self.keyboardType = UIKeyboardType.decimalPad
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You can add borderStyle as .roundedRect and use White background. Something like this:
class MyCustmUITextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
self.borderStyle = .roundedRect
self.backgroundColor = .white
}
}
You can create a subclass from UITextField to serve as your custom text field.
Following this, you need only to instantiate your custom class and then you're good to go.
Something like this:
class MyCustmUITextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor.black
self.font = UIFont.systemFont(ofSize: 20)
self.adjustsFontSizeToFitWidth = true
// then add whatever styles you wish
}
}
From there all you need is to create a new instance of MyCustomUITextField
I am trying to animate the frame change of a subview within a view. Within the animation block I am setting the size of the subview to half its original width and height. However, when I run the code, the subview unexpectedly started with a larger size and shrunk to its original size instead of becoming half its original size. I am using the code below:
My custom view:
import UIKit
class TestView: UIView {
var testSubview: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.testSubview.bounds.size = CGSize(width: 50, height: 50)
}, completion: nil)
}
}
My view controller:
import UIKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let testView = TestView()
view.addSubview(testView)
testView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
testView.center = view.center
testView.layoutIfNeeded()
testView.testAnimate()
}
}
Am I doing something wrongly?
The problem is that your view gets laid out two times, you can check that by printing a debug message inside layoutSubviews().
I would suggest something like this, setting a size variable and use that in both layoutSubviews() and testAnimate():
class TestView: UIView {
var testSubview: UIView!
var mySize = CGSize(width: 100, height: 100)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.yellow
testSubview = UIView(frame: .zero)
testSubview.backgroundColor = UIColor.red
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
testSubview.frame = CGRect(x: 0, y: 0, width: mySize.width, height: mySize.height)
}
func testAnimate() {
UIView.animate(withDuration: 3, delay: 0, options: [], animations: { [unowned self] in
self.mySize = CGSize(width: 50, height: 50)
self.testSubview.bounds.size = self.mySize
}, completion: nil)
}
}
EDIT: Reformulating my whole answer because there are many things I feel should be changed here.
first of all the difference between setting the bounds of a view and it's frame.
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 200, height: 200) // This will set the position and size this view will take relative to it's future parents view
view.bounds = CGRect(x: 50, y: 50, width: 100, height: 100) // This will set position and size of it's own content (i.e. it's background) relative to it's own view
Then comes your testView class variables. Unless you are certain that a variable won't be nil don't use UIView! as the typing, you would end up missing some initialisers and thus your class would not work consistently.
Consider init methods as default values for your variables and you can simply alter them afterwards.
import UIKit
class TestView: UIView {
var testSubview: UIView
override init(frame: CGRect) {
// Initialise your variables before calling super
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.red
super.init(frame: frame)
// Once super is called you can start using class methods or variable such as addSubview or backgroundColor
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
required init?(coder aDecoder: NSCoder) {
testSubview = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testSubview.backgroundColor = UIColor.blue
super.init(coder: aDecoder)
backgroundColor = UIColor.yellow
addSubview(testSubview)
}
}
now finally to the animation part
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) // Simply make the view half its size over 3 seconds
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.transform = CGAffineTransform(scaleX: 1, y: 1) // Upon completion, resize the view back to it's original size
})
}
}
}
or if you really want to use CGRect to do animation on it's position
import UIKit
class TestView: UIView {
var testSubview: UIView
func testAnimation() {
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 50, height: 50) // use frame not bounds to resize and reposition the frame
}) { (_) in
UIView.animate(withDuration: 3, animations: {
self.testSubview.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
})
}
}
}
you can test the effect of changing the bounds attribute as such
import UIKit
class ViewController: UIViewController {
var testView: TestView!
override func viewDidLoad() {
testView = TestView(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 200)))
testView.bounds = CGRect(x: 50, y: 50, width: 100, height: 100)
testView.clipsToBounds = true
view.addSubview(testView)
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
testView.testAnimation()
}
}
simply play around with clipsToBounds, testView.bounds, testView.subView.frame and testView = TestView(frame: CGRect(x:y:width:height) to see all of this
I am unable to add a subclass to my parent UIView class. I am trying to build a BOOK class and have various UIView and UIImageView classes to build the covers and the pages. I get an error when adding the subclass to SELF. Would love some insight. PS - total swift noob
//book
class bookview : UIView {
var cover: UIView!
var backcover: UIView!
var page: UIImageView!
init (pages: Int) {
//backcover cover
backcover = UIView(frame: CGRect(x: 200, y: 200, width: bookwidth, height: bookheight))
backcover.backgroundColor = UIColor.blue
self.addSubview(backcover) //ERROR HERE
//pages
for i in 0 ..< pages {
page = UIImageView(frame: CGRect(x: bookwidth * i/10, y: bookheight * i/10, width: bookwidth, height: bookheight))
page.backgroundColor = UIColor.red
self.addSubview(page) //ERROR HERE
}
//front cover
cover = UIView(frame: CGRect(x: 0, y: 0, width: bookwidth, height: bookheight))
cover.backgroundColor = UIColor.blue
self.addSubview(cover) //ERROR HERE
super.init(frame: CGRect(x: 0, y: 0, width: bookwidth, height: bookheight))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//add book
let book = bookview(pages: 3)
The issue is that you can't call methods on self in the initializer until there is a self to call them on. There is no established value for self until you have called the superclass initializer. In other words, how does a subclass of UIView know how to "addSubview" when it has not yet been initialized as a UIView yet?
So, in your code example, just move the line:
super.init(frame: CGRect(x: 0, y: 0, width: bookwidth, height: bookheight))
to be before any time you call self.addSubview()
addSubview() is a method on UIView. UIView is your view's superclass. You cannot call methods on a superclass until it has been fully initialized.
To fix this, call super.init(frame:) earlier in your own init() function (before you call addSubview()).
I have created a custom UIView :
import UIKit
class IndicatorView: UIView {
var image_view_indicator: UIImageView!
var label_indicator: UILabel!
required init(coder decoder: NSCoder) {
super.init(coder: decoder)!
}
init(frame:CGRect, imageName: String, indicatorValue: String)
{
super.init(frame: frame)
self.frame = frame
image_view_indicator = UIImageView(frame: CGRect(x: 0, y: 0, width: 15, height: 15))
image_view_indicator.image = UIImage(named: imageName)
self.addSubview(image_view_indicator)
self.bringSubviewToFront(image_view_indicator)
label_indicator = UILabel(frame: CGRect(x: image_view_indicator.frame.size.width + 3, y: 0, width: 25, height: 15))
label_indicator.text = indicatorValue
label_indicator.textColor = UIColor.blackColor()
self.addSubview(label_indicator)
self.bringSubviewToFront(label_indicator)
}
}
This is my call from UIViewController :
view_indicator_friends = IndicatorView(frame: view_indicator_friends.frame, imageName: "friend.png", indicatorValue: "20")
I cross checked the IBOutlet connections. They seem to be fine.
But nothing is showing up in the custom view.
When you take your custom view from Xib through IBOutlet connection then your custom init will not invoked that why your image_view_indicator and label_indicator does not created. Thats why your custom view does not showing up properly. One solution is that implement your code under init(frame:CGRect, imageName: String, indicatorValue: String) method in the awakeFromNib method.
override func awakeFromNib() {
let frame = self.frame
image_view_indicator = UIImageView(frame: CGRect(x: 0, y: 0, width: 15, height: 15))
image_view_indicator.image = UIImage(named: imageName)
self.addSubview(image_view_indicator)
self.bringSubviewToFront(image_view_indicator)
label_indicator = UILabel(frame: CGRect(x: image_view_indicator.frame.size.width + 3, y: 0, width: 25, height: 15))
label_indicator.text = indicatorValue
label_indicator.textColor = UIColor.blackColor()
self.addSubview(label_indicator)
self.bringSubviewToFront(label_indicator)
}
I subclass a class of UIView called:AURTabView. Then I drag 3 views(white background) to storyboard to auto layout them with 1/3 screen width of each. Then i add a UIButton on the init method, it works well so far as below:
Then i want to add another two UIViews on to them. The weird thing is the first and third AUTRabView works as expect, but the middle one doesn't.
This is really weird. I checked UIView hierarchy like below:
Any point?
Here is the code:
class AURTabView: UIView {
let tabButton = UIButton()
let smallCircle = UIView()
let largeCircle = UIView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(tabButton)
self.addSubview(smallCircle)
self.addSubview(largeCircle)
}
override func layoutSubviews() {
super.layoutSubviews()
let height = self.frame.height
tabButton.frame = CGRect(x: (self.frame.width-height)/2, y: 0, width: height, height: height)
tabButton.backgroundColor = UIColor.greenColor()
smallCircle.frame = CGRect(x: CGRectGetMidX(self.frame)-2.5, y: height-10-8, width: 5, height: 5)
smallCircle.backgroundColor = UIColor.redColor()
largeCircle.frame = CGRect(x: CGRectGetMidX(self.frame)-5, y: height-8, width: 10, height: 10)
largeCircle.backgroundColor = UIColor.redColor()
print(smallCircle)
print(largeCircle)
}
override func drawRect(rect: CGRect) {
tabButton.layer.cornerRadius = tabButton.frame.width/2
}
}
Use this
import UIKit
class AURTabView: UIView {
let tabButton = UIButton()
let smallCircle = UIView()
let largeCircle = UIView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(tabButton)
self.addSubview(smallCircle)
self.addSubview(largeCircle)
}
override func layoutSubviews() {
super.layoutSubviews()
let height = self.frame.height
tabButton.frame = CGRect(x: (self.frame.width-height)/2, y: 0, width: height, height: height)
tabButton.backgroundColor = UIColor.greenColor()
smallCircle.frame = CGRect(x: self.frame.width/2 - 2.5, y: height-10-8, width: 5, height: 5)
smallCircle.backgroundColor = UIColor.blackColor()
largeCircle.frame = CGRect(x: self.frame.width/2 - 5, y: height-8, width: 10, height: 10)
largeCircle.backgroundColor = UIColor.redColor()
print(smallCircle)
print(largeCircle)
}
override func drawRect(rect: CGRect) {
tabButton.layer.cornerRadius = tabButton.frame.width/2
}
}