I have a view controller which has a programmatically created label like below.
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupUI()
}
func setupUI() {
// added an setup the view constraints.
}
}
This works properly. Then I tried to move all the UI element of the view controller to it's extension by creating a extension like below :
private extension MyController {
var label: UILabel = {
**// tried to initialize the label like this. but getting cannot use stored property in extension error**
}()
// then tried like below
var mylabel: UILabel! {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}
func setupUI() {
// with the second option I getting nil value error.
}
}
How can I initialize UI elements in viewcontroller extentions programmatically to access internally.
Try this:
uielements.swift
extension MyController {
static let myLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}()
}
myController.swift
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.addSubview(RootVC.myLabel)
// Add layout constraints here
}
}
As the error says you are not allowed to declare variables in extension.
You mention that you want to initialize the UI element in the
extensions. of course you can do that. But, what you have in the
extension is a declaration not only initialization.
Have the declaration in the controller and the initialization in an extension method.
Related
When I try to compile this code I get compile time error that's saying:
Protocol 'KeyboardScrollManagable' can only be used as a generic constraint because it has Self or associated type requirements
If I remove lines with //One and //Two comments it will work, but then AbcView doesn't have to be Scrollable anymore. I want to enforce that if you use KeyboardScrollManagable on any a ViewController's subclass, it's View will need to be Scrollable!
protocol KeyboardScrollManagable: UIViewController {
associatedtype View where View: Scrollable //One
var customView: View { get } //Two
func doSomething()
}
class ViewController<ViewModel, View: UIView>: UIViewController {
let viewModel: ViewModel
let customView: View
init(view: View, viewModel: ViewModel) {
self.viewModel = viewModel
self.customView = view
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
view = customView
}
override func viewDidLoad() {
super.viewDidLoad()
(self as? KeyboardScrollManagable)?.doSomething() //ERROR:
//Protocol 'KeyboardScrollManagable' can only be used as a generic constraint because it has Self or associated type requirements
}
}
protocol Scrollable: UIView {
var scrollView: UIScrollView { get }
}
final class AbcView: UIView, Scrollable {
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .clear
return scrollView
}()
//rest of the impl
}
final class AbcViewController: ViewController<AbcViewModel, AbcView> {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension AbcViewController: KeyboardScrollManagable {}
I'm stuck! Any ideas? Or am I asking too much from the Swift?
I wrote a class that shall handle UIBarButtonItem taps.
The initializer takes a reference to an UINavigationItem. All buttons etc. are attached to this UINavigationItem. I tried to connect them with actions (didPressMenuItem()), but when I click the button, the action is not triggered (nothing is written to the console nor the breakpoint I set is triggered).
How can I link the UIBarButtonItem to the function defined in this class?
internal final class NavigationBarHandler {
// MARK: Properties
private final var navigationItem: UINavigationItem?
// MARK: Initializers
required init(navigationItem: UINavigationItem?) {
self.navigationItem = navigationItem
}
internal final func setupNavigationBar() {
if let navigationItem = navigationItem {
let menuImage = UIImage(named: "menu")?.withRenderingMode(.alwaysTemplate)
let menuItem = UIBarButtonItem(image: menuImage, style: .plain, target: self, action: #selector(didPressMenuItem(sender:)))
menuItem.tintColor = .white
navigationItem.leftBarButtonItem = menuItem
}
}
#objc func didPressMenuItem(sender: UIBarButtonItem) {
print("pressed")
}
}
This is what happens in the view controller to which navigationItem the buttons etc. are attached.
class ContactsController: UIViewController {
// MARK: View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
self.title = "Kontakte"
let navigationBarHandler = NavigationBarHandler(navigationItem: self.navigationItem)
navigationBarHandler.setupNavigationBar()
}
}
Th problem here is that you're instantiating NavigationBarHandler inside viewDidload() which is why the memory reference dies after viewDidLoad() finishes. What you should do is to create the variable outside like this.
class ContactsController: UIViewController {
var navigationBarHandler: NavigationBarHandler!
// MARK: View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
self.title = "Kontakte"
self.navigationBarHandler = NavigationBarHandler(navigationItem: self.navigationItem)
navigationBarHandler.setupNavigationBar()
}
}
This way the memory reference stays.
Now I'm practicing build IOS app without using storyboard , but I have a problem want to solve , I created a custom UIView called BannerView and added a background(UIView) and a title(UILabel) , and called this BannerView in the MainVC , but run this app , it crashes at the function setupSubviews() and I don't know why.
import UIKit
import SnapKit
class BannerView: UIView {
var background: UIView!
var title: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupSubviews()
}
func setupSubviews() {
background.backgroundColor = .gray
title.text = "INHEART"
self.addSubview(background)
self.addSubview(title)
}
override func layoutSubviews() {
background.snp.makeConstraints { make in
make.width.equalTo(ScreenWidth)
make.height.equalTo(BannerHeight)
make.left.top.right.equalTo(0)
}
title.snp.makeConstraints { make in
make.width.equalTo(100)
make.center.equalTo(background.snp.center)
}
}
}
class MainVC: UIViewController {
var bannerView:BannerView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView = BannerView(frame: CGRect.zero)
view.addSubview(bannerView)
}
}
Your properties do not appear to be initialised
var background: UIView!
var title: UILabel!
You should initialize these in your init method
self.background = UIView()
self.title = UILabel()
If you use force unwrapping on a class property you must initialize in the init method. XCode should be complaining to you about this and your error message should show a similar error
You are not initialised the background view please initialised them
self.background = UIView()
self.title = UILabel()
and if you want to create custom view by use of xib the follow them Custum View
You must have to initialised the self.background = UIView() and self.title = UILabel() first.
You can initalised them in setupSubviews() function before the set/assign values to them.
I want to make a button that makes the background switch between red and green but I get this error:
missing return in a function expected to return UIView
I'm pretty new to coding, I've googled a bit but I didn't find anything.
Language:Swift
my code:
var timeLeft = 0
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var buttonColor: UIView! {
timeLeft = 1
}
func update() {
if timeLeft > 0 {
view.backgroundColor = UIColor.greenColor()
}
else {
view.backgroundColor = UIColor.redColor()
}
timeLeft = timeLeft - 1
}
}
thanks for helping
You declare buttonColor as a computed property which in its body requires you to return a UIView instance (since its type is UIView).
Read here or here about computed property and make sure it fits your needs as posted in your case
Hi and welcome to stackoverflow
Remember to make an action connecting your UIButton to the code. Otherwise your code will not run when you click on the button. Also, consider using a Bool (true/false) variable to keep track of the background color.
Try the following code (remember to connect the 'click'-function to the button via the storyboard):
import UIKit
class ViewController: UIViewController {
var isGreen = true
#IBAction func click() {
if isGreen {
view.backgroundColor = UIColor.redColor()
isGreen = false
} else {
view.backgroundColor = UIColor.greenColor()
isGreen = true
}
}
}
var buttonColor: UIView! {
get() {
return (Object of UIView)
}
}
Looking for a way to use Swift extensions in a separate file or an alternative solution. Creating an extension only works as long as the extension is written in the same file it is being used.
Here is an example of the ViewController.swift that works.
import UIKit
var TestHelper: String = "Start Value"
extension UIView {
var testValue:String{
set{
TestHelper = newValue
}
get{
return TestHelper
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.testValue = "Some Value"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Taking the extension out of this file and placing in a new file results in a crash giving this error:
Program ended with exit code: 9
This error is saying it doesn't exist I think. Creating the extension in each separate file that the extension is need obviously creates issues with invalid redeclaration.
Thanks in advance!
If you move the extension to another file then you have to move TestHelper, too. Or simplier: put TestHelper into the extension
I also encountered the same error and trying to solve it I found that adding static in front of the extension variable declaration seems to do the trick:
import UIKit
static var TestHelper: String = "Start Value"
extension UIView {
static var testValue: String {
set {
TestHelper = newValue
}
get{
return TestHelper
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.testValue = "Some Value"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Hope it helps!
I tried out the problem thinking it would be easy, but it seems to more complex than I would have initially thought.
I ended up having to subclass UIView. I couldn't create an extension for UIView that added a var. I think maybe they are forcing us to subclass UIView, because of how the init or the get/set works for the class. I just made this. It's not the same but it has equivalent functionality
import UIKit
class MyView: UIView {
var testValue: String?
init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.greenColor()
self.bringSubviewToFront(self.superview)
}
}
and then used it as such
override func viewDidLoad() {
super.viewDidLoad()
let myRect = self.myContent.frame
let myNewView: MyView = MyView(frame: myRect)
myNewView.testValue = "has been set"
self.myView.addSubview(myNewView)
NSLog("%#", myNewView.testValue!)
}
I can extend Array with a var
extension Array {
var last: T? {
if self.isEmpty {
NSLog("array crash error - please fix")
return self [0]
}
else {
return self[self.endIndex - 1]
}
}
}
It is easy to create an extension for UIKit if you only add functions and not a variable
extension UIView {
func layerborders() {
let layer = self.layer
let frame = self.frame
let myColor = self.backgroundColor
layer.borderColor = UIColor.whiteColor().CGColor
layer.borderWidth = 0.8
layer.cornerRadius = frame.width / MyConstants.CornerRadius.toRaw()
}
}
"Extensions cannot add stored properties" (from "The Swift Programming Language")