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")
Related
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.
What the advantages & disadvantages to each of these approaches pertaining to creating file that takes care of the view configuration to reduce a controllers file size.
Main Focuses Are:
Memory
Performance
Testing
Usability
This is the simplest, capable of working, example to demonstrate the question, but when many views are present using many methods do any of the above concerns alter when comparing the Extension & ViewModel Class?
Reminder: The ViewModel Class or Extension would be placed in a separate file.
ViewModel Approach
class VC: UIViewController {
lazy var viewModel: ViewModel {
return (main: self)
}()
ovverride viewDidLoad() {
super.viewDidLoad()
initializeUI()
}
func initializeUI() {
viewModel.configureView()
}
}
class ViewModel {
private let main: UIViewController
init(main: UIViewController) {
self.main = main
}
func configureView() {
main.view.backgroundColor = UIColor.blue
}
}
Extension Approach
class VC: UIViewController {
lazy var viewModel: ViewModel(main: self)
ovverride viewDidLoad() {
super.viewDidLoad()
initializeUI()
}
func initializeUI() {
configureView()
}
}
extension VC {
func configureView() {
main.view.backgroundColor = UIColor.blue
}
}
I would like to have a BaseViewController that subclasses UIViewController, overrides one of it's methods, but also require it's subclasses to implement new ones.
My case is a bit more complex, but this simple case represents my problem:
My BaseViewController would override viewWillAppear to set it's title, but the title string would come from it's subclasses. I thought about some options, not sure if/which one of them is best.
1 - Class with error throwing methods (my current solution):
class BaseViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = getTitle()
}
func getTitle() -> String {
fatalError("Unimplemented method")
}
}
2 - Receive the title in constructor:
class BaseViewController: UIViewController {
var myTitle: String!
convenience init(title titleSent: String) {
self.init(nibName: nil, bundle: nil)
myTitle = sentTitle
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = myTitle
}
}
Note that this options gets really bad if there's more parameters to send
I thought that using protocol would be perfect, until I find out that (of course) protocols can't subclass a class.
Didn't anybody do anything like this before? I don't think so, please share your thoughts.
Update
I tried another way, but got stuck in a compiler error, would this ever work?
procotol BaseViewController {
var myTitle: String { get }
}
extension BaseViewController where Self: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = myTitle
}
}
The compiler says Method does not override any method from its superclass.
I usually create protocol in which I declare what would be nice to have in the controllers. Then I check in the base controller if it's actually implemented and if so, just use the values, like this:
protocol ControllerType {
var navigationTitle: String { get }
}
extension ControllerType {
var navigationTitle: String {
return "Default title"
}
}
class BaseViewController: UIViewController {
override func viewDidLoad() {
if let controller = self as? ControllerType {
self.title = controller.navigationTitle
}
}
}
class ViewController: BaseViewController, ControllerType {
var navigationTitle: String {
return "ViewController"
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Downfall is you have to implement the ControllerType protocol and there's no way to enforce it.
Something similar would work.
class BaseViewController: UIViewController {
override func viewDidLoad() { }
func setTitle(_ title: String) {
self.title = title
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setTitle("ViewController")
}
}
As a relative noob to iOS development I'm struggling to make a PHFComposeBarView control become active in my Swift iOS app.
Having squinted at the Obj-C in the PHFComposeBarView example app and this SO answer, I've managed as best I can to translate the basic setup into Swift such that the control is displayed in the main view:
However, I haven't managed to make the control become active programatically.
I've created a minimal test case project in Swift 2.0/XCode 7.0 Beta 2 whose ViewController looks like:
ViewController.swift:
import UIKit
import PHFComposeBarView
class ViewController: UIViewController, PHFComposeBarViewDelegate {
var composeBar: PHFComposeBarView {
let viewBounds = self.view.bounds
let frame = CGRectMake(0.0, viewBounds.size.height - PHFComposeBarViewInitialHeight, viewBounds.size.width, PHFComposeBarViewInitialHeight)
let composeBarView = PHFComposeBarView(frame: frame)
composeBarView.delegate = self
return composeBarView
}
override var inputAccessoryView: UIView {
return self.composeBar
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewDidAppear(animated: Bool) {
print("viewDidAppear self \(self)")
print("composeBar.delegate \(self.composeBar.delegate)")
print("composeBar.textView.delegate \(self.composeBar.textView.delegate)")
print("composeBar.canBecomeFirstResponder() \(self.composeBar.canBecomeFirstResponder())")
print("composeBar.textView.canBecomeFirstResponder() \(self.composeBar.textView.canBecomeFirstResponder())")
let res = self.composeBar.becomeFirstResponder()
print("composeBar.becomeFirstResponder() \(res)")
print("composeBar.isFirstResponder() \(self.composeBar.isFirstResponder())")
print("composeBar.textView.isFirstResponder() \(self.composeBar.textView.isFirstResponder())")
}
}
Console:
viewDidAppear self <SwiftPHFComposeBarTest.ViewController: 0x7fe088619d90>
composeBar.delegate Optional(<SwiftPHFComposeBarTest.ViewController: 0x7fe088619d90>)
composeBar.textView.delegate Optional(<PHFDelegateChain: 0x7fe088654850>)
composeBar.canBecomeFirstResponder() true
composeBar.textView.canBecomeFirstResponder() true
composeBar.becomeFirstResponder() false
composeBar.isFirstResponder() false
composeBar.textView.isFirstResponder() false
How can it be that composeBar.canBecomeFirstResponder() returns true but composeBar.becomeFirstResponder() returns false?
Also, I'm not sure if the delegate being an Optional is a problem.
EDIT: Updated to include debug output.
Well, one solution appears to be to use the storyboard instead:
ViewController.swift:
import UIKit
import PHFComposeBarView
class ViewController: UIViewController, PHFComposeBarViewDelegate {
#IBOutlet weak var composeBarView: PHFComposeBarView!
override func viewDidLoad() {
super.viewDidLoad()
composeBarView.delegate = self
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView {
composeBarView.removeFromSuperview()
return composeBarView
}
}
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)
}
}