I'm trying to create a view that is transparent in certain spots to see an image behind. However, for some reason in the transparent part of the view, I'm seeing black, instead of what's behind the view. I've trimmed it down to very little code and don't understand why my transparent view shows black instead of red (the color of the view behind). Here's my code:
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let redView = UIView(frame: view.frame)
redView.backgroundColor = UIColor.red
let transparentView = TransparentView(frame: view.frame)
view.addSubview(redView)
view.addSubview(transparentView)
}
}
class TransparentView: UIView {
override func draw(_ rect: CGRect) {
UIColor.clear.setFill()
UIRectFill(rect)
}
I would expect the screen to be full red, but instead it shows full black. Before someone says it's a lot easier to make a clear view, I'm actually trying to do more complex things in drawRect, just dropped down to the most basic thing to try to debug my problem. What am I missing here?
Use self.isOpaque = false; to make the view/layer transparent even when drawRect is overriden.
class TransparentView: UIView {
override init(frame: CGRect) {
super.init(frame: frame);
self.isOpaque = false; //Use this..
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
UIColor.clear.setFill()
UIRectFill(rect)
}
}
I figured it out. Apparently even if you override draw, backgroundColor seems to still be considered, and defaults to black. I added the following to my transparent view class:
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
"drawRect: Implement this method if your view draws custom content. If your view does not do any custom drawing, avoid overriding this method." Link
That been said would be better as you said just set background color on Init.
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Personally i wont subclass a view for so little customization. Just set it while creating it. Also view setup is better on viewDidLoad, not in viewWillAppear. Since it will execute every time your view will go foreground and u will end with two transparent views added.
Also keeping those line in a extension with a private function helps to keep your code clear.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
}
//MARK: - Private Methods
extension ViewController{
fileprivate func setupViews(){
let redView = UIView(frame: view.frame)
redView.backgroundColor = UIColor.red
view.addSubview(redView)
let transparentView = UIView(frame: view.frame)
transparentView.backgroundColor = UIColor.clear
view.addSubview(transparentView)
}
}
Please notice that a more clear approach would be to create those Views in the Storyboard (not by code). Keep code clear and its easier to understand and see whats going on.
Related
I am trying to add a label to UIView and I would like to preview it in InterfaceBuilder, however it doesn't seem to work. not only I don't see label but It also doesn't append one to view hierarchy
#IBDesignable
class UIFloatingLabelInput: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
let Label : UILabel?
func AddLabel(){
Label?.text = "Hello World"
Label?.center = CGPoint(x: self.center.x, y: self.center.y)
self.addSubview(Label!)
}
override init(frame: CGRect){
//Init vars
Label = UILabel(frame: frame)
//Call Initializer
super.init(frame: frame)
AddLabel()
}
required init?(coder: NSCoder){
//Init vars
Label = UILabel(coder: coder)
//CallInitializer
super.init(coder: coder)
AddLabel()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
AddLabel()
}
}
but it doesn't work in IB, but works fine on a device.
You need to give your label frame to make it work in IB
required init?(coder: NSCoder){
//Init vars
Label = UILabel(coder: coder)
//CallInitializer
super.init(coder: coder)
//Give your label a frame
Label.frame = bounds
AddLabel()
}
I have set up a subclassed UIView, and want to see the embedded image in IB - so I've set it as IBDesignable
#IBDesignable
class DieView: UIView {
#IBInspectable
var dieImage : UIImage = UIImage()
override init(frame: CGRect) {
super.init(frame: frame)
updateLayout()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateLayout()
}
// for IB
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateLayout()
}
func updateLayout() {
self.backgroundColor = .red
let profileImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
profileImageView.image = UIImage(named: "dice1")
profileImageView.contentMode = .scaleAspectFit
profileImageView.layer.masksToBounds = false
self.addSubview(profileImageView)
}
func showNumber(number: Int) {
}
}
The background colour changes, but the embedded image doesn't update. Why not?
From the documentation of the prepareForInterfaceBuilder():
Interface Builder waits until all objects in a graph have been created
and initialized before calling this method. So if your object’s
runtime configuration relies on subviews or parent views, those
objects should exist by the time this method is called.
which says that subviews should exist before this method is called. I'm not sure, but try to add image view before this is called. Also, you have to keep in mind that prepareForInterfaceBuilder() is called independently by interface builder. Read the docs for more info. Good Luck!
I know it may be the basic question but I am new to Swift.
Also, I have tried various solutions on SO but could not resolve the issue.
So if anyone can help me with my problem.
I have a custom UIVIEW class as follows:
class SearchTextFieldView: UIView, UITextFieldDelegate{
public var searchText = UITextField()
override init(frame: CGRect) {
super.init(frame: frame)
initializeUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeUI()
}
func initializeUI() {
searchText.placeholder = "Enter model no"
searchText.backgroundColor = UIColor.white
searchText.textColor = UIColor.darkGray
searchText.layer.cornerRadius = 5.0
searchText.delegate=self
self.addSubview(searchText)
}
override func layoutSubviews() {
searchText.frame = CGRect(x: 20.0, y: 5.0, width: self.frame.size.width - 40,height : self.frame.size.height - 10)
}
}
Now I want to set text to SearchText textfield from another class which is as follows:
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
// Do any additional setup after loading the view.
}
func setupUI() {
let searchTextFieldView = SearchTextFieldView()
self.view.addSubview(searchTextFieldView) //adding view containing search box view at the top
**searchTextFieldView.searchText.text = "My Text"**
}
I am using Storyboard. Also, I can see the textfield with placeholder text.only problem is I can not set text to it.
Can anybody help. Whats wrong in my code.
It is needed to call searchTextFieldView.setNeedsDisplay(), this will in turn call override func draw(_ rect: CGRect) in class SearchTextFieldView.
Add override func draw(_ rect: CGRect) {} in SearchTextFieldView, and try setting searchText.text = <someValue> in draw(). You can use a String property in SearchTextFieldView, to get <someValue> from the client (one who is using SearchTextFieldView) class.
You are creating you view via SearchTextFieldView(), while you have 2 available initializers init(frame:) and init?(coder:).
If you change
let searchTextFieldView = SearchTextFieldView()
with
let searchTextFieldView = SearchTextFieldView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
you will see the text.
You are not setting frame to the view. Also you are not loading the .xib in the view class. It should be like:-
class SearchTextFieldView: UIView, UITextFieldDelegate{
//MARK:- Initializer
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize(withFrame: self.bounds)
}
override init(frame : CGRect) {
super.init(frame: frame)
initialize(withFrame: frame)
}
//MARK: - View Initializers
func initialize(withFrame frame : CGRect) {
Bundle.main.loadNibNamed("SearchTextFieldView", owner: self, options: nil)
view.frame = frame
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view)
initializeUI()
}
}
Now you can call the below code in view controller:-
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
// Do any additional setup after loading the view.
}
func setupUI() {
let searchTextFieldView = SearchTextFieldView(frame: ?*self.view.bounds)
self.view.addSubview(searchTextFieldView)
//adding view containing search box view at the top
searchTextFieldView.searchText.text = "My Text"
}
Don't forget to create an xib with name "SearchTextFieldView.xib" as you are loading that nib in your initialize function.
Hope it helps :)
add frame for the searchTextFieldView inside setupUI() method. because the View got loaded on the view but its doesn't have a frame (x,y position, width and height). Change your UIViewController's colour to grey and u can see the your view loaded on the left corner (0,0). set frame size for the view that will solve this problem.
I have a UIView as below:
import UIKit
#IBDesignable
class CHRAlertView: UIView {
#IBOutlet var icon:UILabel!
#IBOutlet var alertText:UITextView!
override init(frame: CGRect) {
super.init(frame: frame)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialize()
}
private func initialize(){
self.backgroundColor = UIColor.green
}
}
Based on how #IBDesignable works, this should show up in IB with a green background, but I get the clear color like this:
Why is this not functioning as expected? I need the background color to show in IB based on the defaults set in my #IBDesignable.
Since backgroundColor is an IB property not created via #IBInspectable, it always seems to overwrite whatever is in the init or draw methods. Meaning, if it is "default" in IB, it causes it to be overwritten with nil. However, if set in the prepareForInterfaceBuilder method backgroundColor works and shows in IB. So, the backgroundColor, it can be reasonably assumed, must be set at runtime. To do this I have the below:
//------------------
//Setup and initialization
//------------------
override init(frame: CGRect) {
super.init(frame: frame)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialize()
}
//Setups content, styles, and defaults for the view
private func initialize(){
self.staticContent()
self.initStyle()
}
//Sets static content for the view
private func staticContent() {
}
//Styles the view's colors, borders, etc at initialization
private func initStyle(){
}
//Styles the view for variables that must be set at runtime
private func runtimeStyle(){
if self.backgroundColor == nil {
self.backgroundColor = UIColor.green
}
}
override func prepareForInterfaceBuilder() {
self.runtimeStyle()
}
override func awakeFromNib() {
super.awakeFromNib()
self.runtimeStyle()
}
This defaults the backgroundColor if it is "default" (read nil) in IB to a color, but does not use the UIColor.green if a backgroundColor is set in IB, which is exactly what I need.
Shoutout to Eridius in the #swift-lang irc for helping me get to this answer.
In my app I have several UIButtons, which I want to be with rounded corners. All of these buttons are placed in Storyboard.
I've created custom class RoundedButton, which has simple override of draw func.
import UIKit
class RoundedButton: UIButton {
override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 4.0
}
}
I've added this custom class to every button in Storyboard, but buttons corners are not rounded in simulator.
Thanks for any help!
draw method is not the right place to add this piece of code.
You should move it to awakeFromNib
Coming to your question, add self.layer.masksToBounds = true after setting cornerRadius. The rounded corner should then show up.
You should instead override init
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
layer.cornerRadius = 4.0
}
EDITED:
if you don't use storyboard, or xib's
override init(frame: CGRect) {
super.init(frame: frame)
layer.cornerRadius = 4.0
}