I thought I wrote this correctly. I'm trying to get a value set in another viewcontroller (then stored in a singleton) to change the height of the rectangle in my BarGraphView, but my BarGraphView doesn't redraw when the value I'm passing in is changed...can anyone spot what I'm missing?
BarGraphViewController:
import UIKit
class BarGraphViewController: UIViewController, BarGraphViewDataSource {
#IBOutlet weak var barGraphView: BarGraphView! {
didSet {
barGraphView.datasource = self
}
}
#IBOutlet weak var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
var firstBarHeight: Int = 30 {
didSet {
firstBarHeight = SharkTankSingleton.instance.firstInvestorTotalasInt / 250
updateUI()
}
}
private func updateUI() {
barGraphView.setNeedsDisplay()
}
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat? {
return CGFloat(firstBarHeight)
}
BarGraphView:
import UIKit
protocol BarGraphViewDataSource: class {
func firstBarHeightForBarGraphView(sender: BarGraphView) -> CGFloat?
}
#IBDesignable
class BarGraphView: UIView {
#IBInspectable var firstBarColor: UIColor = UIColor.blueColor()
weak var datasource: BarGraphViewDataSource?
override func drawRect(rect: CGRect) {
let firstBarHeight = datasource?.firstBarHeightForBarGraphView(self) ?? 0
let firstBar = CGRect(x: 200, y: 400, width: 30, height: firstBarHeight)
// firstBarColor.setFill()
var firstBarPath = UIBezierPath(rect: firstBar)
UIColor.redColor().setFill()
firstBarPath.fill()
Related
I have created a simple custom UIView:
final class TestView: UIView {
var testColor: UIColor = .white {
didSet {
backgroundColor = testColor
}
}
}
Then I wrote this in my view controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
}
}
}
By doing this, I get an error:
Could you help me understanding what's wrong here and how to implement the UIAppearance proxy for any custom UIView?
Thank you for your help
You need to make the property #objc and dynamic:
final class TestView: UIView {
#objc dynamic var testColor: UIColor? = .white {
didSet {
backgroundColor = testColor
}
}
}
Worth noting: the UIAppearance proxy does NOT affect views which are already part of the view hierarchy.
So, in your example, adding #objc dynamic to your property will get rid of the crash, but you will not see any change to the two #IBOutlet views.
If you call it as part of viewDidLoad() (instead of DispatchQueue.main.asyncAfter):
override func viewDidLoad() {
super.viewDidLoad()
TestView.appearance().testColor = .red
}
The two #IBOutlet views will get the red background.
Or, if you add a new view to the hierarchy, it will get the red background:
class AppearanceTestViewController: UIViewController {
#IBOutlet weak var testView: TestView!
#IBOutlet weak var testView2: TestView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
TestView.appearance().testColor = .red
self.addAnotherTestView()
}
}
func addAnotherTestView() -> Void {
let v = TestView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalToConstant: 240.0),
v.heightAnchor.constraint(equalTo: v.widthAnchor),
v.centerXAnchor.constraint(equalTo: view.centerXAnchor),
v.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
// this newly added view WILL have a red background
}
}
I was creating animations for buttons using extension in swift but when i call the animation function it generates error.
import UIKit
extension UIButton{
func wiggle() {
let wiggleAnim = CABasicAnimation(keyPath: "psoition")
wiggleAnim.duration = 0.05
wiggleAnim.repeatCount = 5
wiggleAnim.autoreverses = true
wiggleAnim.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
wiggleAnim.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
layer.add(wiggleAnim, forKey: "position")
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var colorizeBtn: UIButton!
#IBOutlet weak var wiggleBtn: UIButton!
#IBOutlet weak var dimBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func colorizeBtnWasPressed(_ sender: Any) {
}
#IBAction func wiggleBtnWasPressed(_ sender: Any) {
wiggleBtn.wiggle()
}
#IBAction func dimBtnwasPressed(_ sender: Any) {
}
}
View Controller
Your wiggleBtn is of type UIView but you write an extension to UIButton.
Either change the extension to UIView or change the type of wiggleBtn to UIButton.
I have custom control which contains textField. Sample code looks like this:
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField: UITextField?
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
//updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateView()
}
func updateView() {
textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
self.addSubview(textField!)
textField!.text = self._text
}}
When I create an outlet to my custom control in code I can set text property, but I can't get it. What am I doing wrong ? I'm new in this so I don't understand...
EDIT:
Add MyTextField to Main.storyboard and standard textField
Create an outlets #IBOutlet var myTextField: MyTextField! and
standardTextField
Set Text property of my control in Object Inspector
My control on storyboard updates correctly and "test text" is visible
Add a button and create an action outlet
case 1: works fine
#IBAction func click(_ sender: UIButton) {
myTextField.text = standardTextField.text
}
case 2: doesn't work
#IBAction func click(_ sender: UIButton) {
standardTextField.text = myTextField.text
}
After case 2, in standardTextField is only value which i set from object inspector. If i change value of myTextField.text, standardTextField still shows a orginal value from object inspector
I replicated your setup in Xcode. Case 2 does work — the standardTextField displays MyTextField's text when the button is clicked.
Note: in MyTextField I assumed that you want to create textField once, instead of recreating it each time the text updates. Case 2 was working without this change, though.
import UIKit
class ViewController: UIViewController {
#IBOutlet var myTextField: MyTextField!
#IBOutlet var standardTextField: UITextField!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(click(_:)), for: .touchUpInside)
}
#IBAction func click(_ sender: UIButton) {
// myTextField.text = standardTextField.text // Case 1 works
standardTextField.text = myTextField.text // Case 2 works
}
}
MyTextField control:
import UIKit
#IBDesignable
class MyTextField: UIView {
private var _text: String?
private var textField = UITextField(frame: CGRect(x: 0, y: 0, width: 97, height: 30))
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.addSubview(textField)
updateView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.addSubview(textField)
updateView()
}
func updateView() {
textField.text = self._text
}
}
A simple solution for not working getter is change text property
from:
#IBInspectable var text: String? {
get {
return _text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
to
#IBInspectable var text: String? {
get {
return textField!.text
} set (newValue) {
guard let txt = newValue else { return }
self._text = txt
updateView()
}
}
But I don't know if this is correct...
Does it make any sense ?
I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.
I'm trying to set an array property that is a var not a let. Notice the setAllShopItems method:
struct ShopDisplay {
private var allShopItemCategories: [ShopItemCategory]
private var currentShopItemCategory: ShopItemCategory
private var shopItemCategoryIcon: MMImageView
private var currentShopItemGender: Gender
private var allShopItems: [ShopItem]
private var allDisplayedShopItems: [ShopItem]
init() {
allShopItemCategories = [ShopItemCategory]()
currentShopItemCategory = ShopItemCategory(rawValue: "TO")!
shopItemCategoryIcon =
MMImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
currentShopItemGender = .Female
allShopItems = []
allDisplayedShopItems = []
}
mutating func setShopItemsToDisplay() {
allDisplayedShopItems.removeAll()
for item in self.allShopItems {
let itemCategory = item.object["category"] as! String
if itemCategory == currentShopItemCategory.rawValue {
allDisplayedShopItems.append(item)
}
}
}
mutating func setAllShopItems(shopItems: [ShopItem]) {
self.allShopItems = shopItems
}
I call this setAllShopItems() method like so:
override func prepareForSegue( segue: UIStoryboardSegue,
sender: AnyObject?) {
if (segue.identifier == "dressingRoom") {
let dressingRoomViewController = segue.destinationViewController
as! DressingRoomViewController
dressingRoomViewController.getShopDisplay().setAllShopItems(self.allShopItems)
dressingRoomViewController.getShopDisplay().setAllShopItemCategories(
self.categories)
}
}
the property returned by getShopDisplay is also a var:
class DressingRoomViewController: UIViewController,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource
{
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
private let identifier = "cellIdentifier"
private let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
private let cellSpacing: CGFloat = 5
private let cellsPerRow: CGFloat = 5
private var cellSize: CGFloat = 0.00
private var shopDisplay = ShopDisplay()
#IBOutlet weak var categoryIcon: MMImageView!
func getShopDisplay() -> ShopDisplay {
return self.shopDisplay
}
So if all of these variable being set are vars, and I use the keyword mutating whenever I am setting them if they are structs, how I am still getting this error?:
/Users/Ben/Documents/Development/MirrorMirror/MirrorMirror/ChooseShopViewController.swift:40:44:
Immutable value of type 'ShopDisplay' only has mutating members named
'setAllShopItems'