swift MaterialComponents.MDCTabBarView mdc_customView is not working fine - swift

I'm trying to add customView to the MDCTabBarItem using mdc_customView but the items are not taking correct width and the results is as below
if I don't set the mdc_customView value then the result is as expected but without the custom design
Code with mdc_customView
override func parseTabBarItems(data: [SubCategory]) -> [MDCTabBarItem] {
var result: [MDCTabBarItem] = []
var nextX: CGFloat = 15
for cat in data {
guard let count = cat.sub?.count, count > 0 else { continue }
let item = MDCTabBarItem()
item.tag = result.count
let customeView = MDCTabBarCustomView()
customeView.frame = CGRect(x: nextX, y: 0, width: (cat.ref ?? "").sizeOfString(usingFont: .ttrSemiBold10).width, height: 50)
nextX = nextX + 15 + (cat.ref ?? "").sizeOfString(usingFont: .ttrSemiBold10).width
customeView.config(title: cat.ref ?? "")
item.mdc_customView = customeView
result.append(item)
}
return result
}
Code without mdc_customView
override func parseTabBarItems(data: [SubCategory]) -> [MDCTabBarItem] {
var result: [MDCTabBarItem] = []
var nextX: CGFloat = 15
for cat in data {
guard let count = cat.sub?.count, count > 0 else { continue }
let item = MDCTabBarItem(title: cat.ref ?? "", image: nil, tag: result.count)
result.append(item)
}
return result
}
MDCTabBarCustomView
import UIKit
import MaterialComponents.MDCTabBarView
class MDCTabBarCustomView: UIView , MDCTabBarViewCustomViewable {
var titleLabel: UILabel!
var containerView: UIView!
var contentFrame: CGRect
init() {
self.titleLabel = UILabel.newAutoLayout()
self.containerView = TTRView.newAutoLayout()
self.contentFrame = .zero
super.init(frame: .zero)
self.autoresizingMask = []
self.setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func config(title: String) {
self.titleLabel.text = title
}
func setSelected(_ selected: Bool, animated: Bool) {}
private func setup(){
self.addSubview(self.containerView)
self.containerView.addSubview(self.titleLabel)
self.containerView.snp.makeConstraints{
$0.edges.equalToSuperview()
}
self.titleLabel.snp.makeConstraints{
$0.edges.equalToSuperview().offset(5)
}
}
}
The tabBar settings:
self.tabBar.preferredLayoutStyle = .scrollable

after spending all the day trying and learning about this new customView I was able to make it work below is the working code
it was all about the intrinsicContentSize and layoutSubviews
here is the new output
final class MDCTabBarCustomView: UIView , MDCTabBarViewCustomViewable {
var contentFrame: CGRect {
return self.titleLabel.frame
}
var titleLabel: UILabel!
var containerView: UIView!
init(){
self.titleLabel = UILabel.newAutoLayout()
self.containerView = UIView.newAutoLayout()
super.init(frame: .zero)
self.autoresizingMask = []
}
override func layoutSubviews() {
super.layoutSubviews()
if self.containerView.superview != self {
self.addSubview(self.containerView)
}
if self.titleLabel.superview != self.containerView {
self.containerView.addSubview(self.titleLabel)
}
containerView.snp.makeConstraints{
$0.top.leading.equalToSuperview().offset(5)
$0.bottom.trailing.equalToSuperview().offset(-5)
}
titleLabel.snp.makeConstraints{
$0.top.equalToSuperview().offset(5)
$0.bottom.equalToSuperview().offset(-5)
$0.centerX.equalToSuperview()
}
}
override var intrinsicContentSize: CGSize {
return CGSize(width: self.titleLabel.intrinsicContentSize.width + 20, height: self.titleLabel.intrinsicContentSize.height + 20)
}
}

Try preferredLayoutStyle = .scrollableCentered

Related

reduce the cell background based on time swift

I would like to make sure that my cell has a background related to the remaining time. in the sense that the closer I get to 0, the more I would like it to be reduced, so that we understand that the timer is about to expire.
according to the elapsed time it automatically reduces from right to left.
this is the code I use in managing the Cell
class TimerCell: UITableViewCell {
#IBInspectable var defaultBackgroundColor: UIColor = .white
#IBInspectable var runningBackgroundColor: UIColor = .white
#IBInspectable var pausedBackgroundColor: UIColor = .white
#IBInspectable var animationDuration: Double = 0
#IBOutlet var timeLabel: UILabel!
#IBOutlet var nameLabel: UILabel!
#IBOutlet var startButton: UIButton!
#IBOutlet var pauseButton: UIButton!
#IBOutlet var stopButton: UIButton!
weak var timer: Timer? {
didSet {
guard let timer = timer else {
updater?.invalidate()
return
}
if case .running = timer.state {
startUpdater()
}
configure(animated: false)
}
}
private weak var updater: Foundation.Timer?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setEditing(_ editing: Bool, animated: Bool) {
print("*** \(Date()) setEditing(\(editing), animated: \(animated)) (timer?.name: \(String(describing: timer?.name)))")
super.setEditing(editing, animated: animated)
configure(animated: animated)
}
func configure(animated: Bool = true) {
guard let timer = timer else {
return
}
UIView.animate(withDuration: animated ? animationDuration : 0) {
guard !self.isEditing else {
self.timeLabel.text = timer.initialTime.hmsString
self.startButton.safelySetIsHidden(true)
self.pauseButton.safelySetIsHidden(true)
self.stopButton.safelySetIsHidden(true)
self.backgroundColor = self.defaultBackgroundColor
return
}
self.timeLabel.text = ceil(timer.timeForState).hmsString
self.nameLabel.text = timer.name
switch timer.state {
case .stopped:
self.stopButton.safelySetIsHidden(true)
self.pauseButton.safelySetIsHidden(true)
self.startButton.safelySetIsHidden(false)
self.backgroundColor = self.defaultBackgroundColor
case .running:
self.startButton.safelySetIsHidden(true)
self.stopButton.safelySetIsHidden( ceil(timer.timeForState) == 0 ? true : false )
self.pauseButton.safelySetIsHidden( ceil(timer.timeForState) == 0 ? true : false )
self.backgroundColor = self.runningBackgroundColor
case .paused:
self.pauseButton.safelySetIsHidden(true)
self.startButton.safelySetIsHidden(false)
self.stopButton.safelySetIsHidden(false)
self.backgroundColor = self.pausedBackgroundColor
}
}
}
#IBAction private func startTimer() {
timer?.state = .running
configure()
startUpdater()
}
#IBAction private func pauseTimer() {
timer?.state = .paused
configure()
}
#IBAction private func stopTimer() {
timer?.state = .stopped
configure()
}
private func startUpdater() {
guard let timer = timer else {
return
}
let date = Date(timeIntervalSinceNow: timer.timeForState.truncatingRemainder(dividingBy: 1))
let updater = Foundation.Timer(fire: date, interval: 1, repeats: true) {
[weak timer] updater in
self.configure()
if timer?.state != .running {
updater.invalidate()
}
}
self.updater = updater
RunLoop.main.add(updater, forMode: .common)
}
}
I think you're after something like this:
That's not trivial to achieve. I did it by adding a CAGradientLayer to the view and animating its locations property. At the same time, I ran the timer to change the label value.
So you might do it that way; you would probably want to tweak the values, of course. This is just a proof-of-concept demo.

Swift Custom UIView Inherited by UILabel

I have an inheritance problem that I'm trying to solve. Typically, I'd just use multi-inheritance here, but Swift doesn't really do that.
Custom UIView
import UIKit
class ValidationView: UIView {
var required:Bool = false
var validRegex:String? = nil
var requiredLbl:UILabel?
private var requiredColor:UIColor = UIColor.red
private var requiredText:String = "*"
private var requiredFont:UIFont = UIFont.systemFont(ofSize: 16.0, weight: UIFont.Weight.bold)
override init(frame: CGRect) {
super.init(frame: frame)
self.setupValidationViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupValidationViews()
}
private func setupValidationViews() {
self.requiredLbl = UILabel(frame: CGRect(x: self.frame.width - 30, y: 30, width: 20, height: 20))
self.styleRequiredLabel()
self.addSubview(self.requiredLbl!)
}
func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?) {
self.requiredColor = color ?? self.requiredColor
self.requiredText = text ?? self.requiredText
self.requiredFont = font ?? self.requiredFont
self.styleRequiredLabel()
}
private func styleRequiredLabel() {
self.requiredLbl?.textColor = self.requiredColor
self.requiredLbl?.text = self.requiredText
self.requiredLbl?.font = self.requiredFont
}
}
Custom UITextField
import Foundation
import UIKit
#IBDesignable open class CustomTextField: UITextField {
#IBInspectable public var borderWidth: CGFloat = 2.0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable public var borderColor: UIColor = UIColor.lightGray {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable public var cornerRadius: CGFloat = 4.0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}
}
I want that Custom UITextField to also be a ValidationView. I know I could do a protocol and extension and then have my CustomTextField implement that protocol, but that doesn't allow for init overrides. I'd rather not have to change the inits on ever view that implements ValidationView.
Something like this can be accomplished using #arturdev answer. I ended up with this:
import UIKit
class ValidatableProperties {
var required:Bool
var validRegex:String?
var requiredColor:UIColor
var requiredText:String
var requiredFont:UIFont
init(required:Bool, validRegex:String?, requiredColor:UIColor, requiredText:String, requiredFont:UIFont) {
self.required = required
self.validRegex = validRegex
self.requiredText = requiredText
self.requiredColor = requiredColor
self.requiredFont = requiredFont
}
}
protocol Validatable : UIView {
var validatableProperties:ValidatableProperties! { get set }
var requiredLbl:UILabel! { get set }
func setupValidationDefaults()
func setupValidationViews(frame:CGRect)
func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?)
}
extension Validatable {
func setupValidationDefaults() {
let props = ValidatableProperties(required: false, validRegex: nil, requiredColor: UIColor.red, requiredText: "*", requiredFont: UIFont.systemFont(ofSize: 16.0, weight: .bold))
self.validatableProperties = props
}
func setupValidationViews(frame:CGRect) {
self.requiredLbl = UILabel(frame: CGRect(x: frame.width, y: 0, width: 20, height: 20))
self.styleRequiredLabel()
self.addSubview(self.requiredLbl)
}
func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?) {
self.validatableProperties.requiredColor = color ?? self.validatableProperties.requiredColor
self.validatableProperties.requiredText = text ?? self.validatableProperties.requiredText
self.validatableProperties.requiredFont = font ?? self.validatableProperties.requiredFont
self.styleRequiredLabel()
}
private func styleRequiredLabel() {
self.requiredLbl.textColor = self.validatableProperties.requiredColor
self.requiredLbl.text = self.validatableProperties.requiredText
self.requiredLbl.font = self.validatableProperties.requiredFont
}
}
open class ValidationTextField:UITextField, Validatable {
var requiredLbl: UILabel!
var validatableProperties: ValidatableProperties!
override public init(frame: CGRect) {
super.init(frame: frame)
self.setupValidationDefaults()
self.setupValidationViews(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupValidationDefaults()
self.setupValidationViews(frame: self.frame)
}
}
But this requires extending all classes you want validatable into their own custom classes, needing to overwrite the inits every time and call the methods. It works, but it's not ideal and, while not exactly anti-pattern inheritance, certainly has some code-smell to it.
You should make ValidationView as protocol instead of class, and conform your custom classes to that protocol.
ValidatableView.swift
import UIKit
fileprivate var requiredColor = UIColor.red
fileprivate var requiredText = "*"
fileprivate var requiredFont = UIFont.systemFont(ofSize: 16.0, weight: UIFont.Weight.bold)
fileprivate struct AssociatedKeys {
static var lblKey = "_lblKey_"
}
protocol ValidatableView: class {
var required: Bool {get}
var validRegex: String? {get}
var requiredLbl: UILabel? {get}
}
extension ValidatableView where Self: UIView {
var required: Bool {
return false
}
var validRegex: String? {
return nil
}
var requiredLbl: UILabel? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.lblKey) as? UILabel
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.lblKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
func setupValidation() {
self.requiredLbl = UILabel(frame: CGRect(x: self.frame.width - 30, y: 30, width: 20, height: 20))
self.requiredLbl?.autoresizingMask = .flexibleWidth
self.styleRequiredLabel()
self.addSubview(self.requiredLbl!)
}
func styleRequiredLabel(color:UIColor? = requiredColor, text:String? = requiredText, font:UIFont? = requiredFont) {
self.requiredLbl?.textColor = requiredColor
self.requiredLbl?.text = requiredText
self.requiredLbl?.font = requiredFont
}
}
CustomTextField.swift
#IBDesignable open class CustomTextField: UITextField {
#IBInspectable public var borderWidth: CGFloat = 2.0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable public var borderColor: UIColor = UIColor.lightGray {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable public var cornerRadius: CGFloat = 4.0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
setupValidation()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupValidation()
}
}
extension CustomTextField: ValidatableView { //<- Magic line :)
}
You can create an instance of ValidationView when you instantiate CustomTextField.
Something like this:
#IBDesignable open class CustomTextField: UITextField {
var validationView: ValidationView
override init(frame: CGRect) {
super.init(frame: .zero)
self.validationView = ValidationView()
}
}

Controlling place holder text color from custom #IBDesignable UITextField class. Swift 4.2, Xcode 10

I have created Cocoa Touch Class:
Sub Class Of UITextField
And the file Name is MyTextFieldStyle
And here is the MyTextFieldStyle.swift file:
import UIKit
#IBDesignable
class MyTextFieldStyle: UITextField {
#IBInspectable var FavoriteTextColor : UIColor = UIColor.white {
didSet {
self.textColor = FavoriteTextColor
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.textColor = self.FavoriteTextColor
}
}
How can I add these 3 controls,
placeHolder text color
place holder font type
place holder font size
like 'FavoriteTextColor' above ? (Swift 4.2 & Xcode 10)
I'd do my IBDesignables differently. I would put all my final assignment in the same function for an example here is my own custom UITextField I use.
#IBDesignable public class HooopTextfield: UITextField, UITextFieldDelegate {
#IBInspectable public var fontName: String? = "AvenirNext-Bold" {
didSet {
decorate()
}
}
#IBInspectable public var fontSize: CGFloat = 15 {
didSet {
decorate()
}
}
#IBInspectable public var fontColor: UIColor = UIColor.white {
didSet {
decorate()
}
}
#IBInspectable public var customTextAlignment: Int = 0 {
didSet {
decorate()
}
}
#IBInspectable public var borderColor: UIColor = UIColor.white {
didSet {
decorate()
}
}
#IBInspectable public var letterSpacing: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var cornerRadius: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var customPlaceholder: String? = nil {
didSet {
decorate()
}
}
#IBInspectable public var horizontalInset: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var verticalInset: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var selfDelegate: Bool = false {
didSet {
if selfDelegate {
self.delegate = self
}
}
}
#IBInspectable public var borderWidth: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var baseLineOffset: CGFloat = 0 {
didSet {
decorate()
}
}
#IBInspectable public var placeholderColor: UIColor? = nil {
didSet {
decorate()
}
}
#IBInspectable public var requiredColor: UIColor? = nil {
didSet {
decorate()
}
}
#IBInspectable public var requiredCharacter: String = "*"{
didSet {
decorate()
}
}
#IBOutlet public var nextField:HooopTextfield?
/*** more inspectable var can be added **/
override public func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: horizontalInset, dy: verticalInset)
}
override public func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: horizontalInset, dy: verticalInset)
}
func decorate() {
// Setup border and corner radius
self.layer.cornerRadius = cornerRadius
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.cgColor
// Setup text style
let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
switch customTextAlignment {
case 2:
paragraphStyle.alignment = .right
break
case 1:
paragraphStyle.alignment = .center
break
default:
paragraphStyle.alignment = .left
break
}
var titleAttributes:[NSAttributedStringKey : Any] = [
NSAttributedStringKey.foregroundColor: fontColor,
NSAttributedStringKey.kern: letterSpacing,
NSAttributedStringKey.baselineOffset: baseLineOffset,
NSAttributedStringKey.paragraphStyle: paragraphStyle
]
if let _ = fontName {
titleAttributes[NSAttributedStringKey.font] = UIFont(name: fontName!, size: fontSize)
}
if let _ = customPlaceholder {
var placeholderAttributes = titleAttributes
if let _ = placeholderColor {
placeholderAttributes[NSAttributedStringKey.foregroundColor] = placeholderColor
}
let attributedPlaceholder = NSMutableAttributedString(string: customPlaceholder!, attributes: placeholderAttributes)
if let _ = requiredColor {
let range = (customPlaceholder! as NSString).range(of: requiredCharacter)
attributedPlaceholder.addAttribute(NSAttributedStringKey.foregroundColor, value: requiredColor!, range: range)
}
self.attributedPlaceholder = attributedPlaceholder
}
self.defaultTextAttributes = titleAttributes
}
// MARK: - UITexfieldDelegate
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if (nextField != nil) {
nextField?.becomeFirstResponder()
}
return true
}
}
I have basic stuff like fontColor, fontSize, fontName, same things for the placeholder. Then I have more visual things like borderColor, cornerRadius, borderWidth, etc. Finally I also have some inset rect positioning to align everything as I wish right from my storyboard.
I use NSMutableAttributedString for these because they are the most customisable, allowing me to also be able to color required fields and such.
Finally I also have a little #IBOutlet that also allows me to jump from one textfield to the next with the next button on the keyboard when combined with a boolean called selfDelegate.
Ask me any questions you want about this and play around with it you should be able to do anything you want with #IBDesignable I think.
EDIT 1:
Before I forget I recommend using a decorate or equivalent function because the order you apply your changes matter most of the time.
EDIT 2:
Fixed the errors, this was due most likely to swift changing how to use NSAttributedString attributes but strangely enough it seems the placeholderAttributes needed to be [NSAttributedStringKey: Any] but the defaultTextAttributes needed to be [String: Any] don't know why this would be the case. But it should work now.
EDIT 3:
Probably because I have a different version of Xcode or because it was a playground I had a message for defaultTextAttributes but I've refixed it by removing the .rawValue if it was the opposite that needed to be done, know that NSAttributedStringKey.key.rawValue will be a string and you can get the NSAttributedStringKey by using NSAttributedStringKey.init(rawValue:String)

How can I get the game to show the word after all guesses have been used up?

so I'm new to coding and I've been doing practice games a such to build my skills. I've created this word guessing game and I'm trying to make the game show the word after all guesses have been used up. However, the program doesn't read the code I write to set the label to display the answer. Here is the code I've written so far:
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
In addition, I have a structure that is written like this:
import Foundation
struct Game {
var word : String
var incorrectMovesRemaining : Int
var guessedLetters: [Character]
mutating func playerGuessed (letter: Character){
guessedLetters.append(letter)
if !word.characters.contains(letter){
incorrectMovesRemaining -= 1
}
}
var formattedWord: String {
var guessedWord = ""
for letter in word.characters {
if guessedLetters.contains(letter) {
guessedWord += "\(letter)"
} else {
guessedWord += "_"
}
}
return guessedWord
}
}
You need to redraw your UI, this is done with self.setNeedsDisplay(). It notifies the system that the view's contents needs to be redrawn. In your updateUI() you can add this.
Regarding setNeedsDisplay you can get more information here
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
self.setNeedsDisplay()
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
Create a variable that will keep track of how many times you have guessed wrong. Then you can do this:
Use a while statement:
while numberOfTimesGuessedWrong <= 7{
}
//when you have guessed incorrectly 7 times, the compiler will move here:
wordLabel.text = "\(correctAnswer)"
So when you guess incorrectly 7 times, on the 8th time, it will then show the correct answer.

How do I show activity indicator for ASNetworkImageNode?

Here is the code that I tried, but when the image has not loaded yet, nothing show up (an animated indicator should show up). What is the best practice of showing the activity indicator? I hooked into the ASNetworkImageNodeDelegate.
import AsyncDisplayKit
class WideImageFeedNode : ASCellNode, ASNetworkImageNodeDelegate {
var imageNode = ASNetworkImageNode()
var activityIndicator:UIActivityIndicatorView?
init(itemid:Int) {
super.init()
imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor()
let imgURL = URL(string:"http://...somelargeimage.jpg")
imageNode.url = imgURL
imageNode.delegate = self
self.addSubnode(imageNode)
self.automaticallyManagesSubnodes = true
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
var finalStackArr:[ASLayoutElement] = [self.imageNode]
let finalSpec = ASStackLayoutSpec(direction: .vertical, spacing: 10.0, justifyContent: .start, alignItems: .start, children: finalStackArr)
return finalSpec
}
func imageNode(_ imageNode: ASNetworkImageNode, didLoad image: UIImage) {
if let activityIndicator = self.activityIndicator {
activityIndicator.removeFromSuperview()
self.activityIndicator = nil
}
self.setNeedsLayout()
}
// helper functions
func setupActivityIndicator(bounds:CGSize) -> UIActivityIndicatorView {
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
var refreshRect = activityIndicator.frame
refreshRect.origin = CGPoint(x: (bounds.width - activityIndicator.frame.size.width) / 2.0, y: (bounds.height - activityIndicator.frame.size.height) / 2.0)
activityIndicator.frame = refreshRect
return activityIndicator
}
func imageNodeDidStartFetchingData(_ imageNode: ASNetworkImageNode) {
self.activityIndicator = setupActivityIndicator(bounds: imageNode.style.preferredSize)
imageNode.view.addSubview(self.activityIndicator!)
}
func imageNode(_ imageNode: ASNetworkImageNode, didFailWithError error: Error) {
if let activityIndicator = self.activityIndicator {
activityIndicator.removeFromSuperview()
self.activityIndicator = nil
}
}
}
Never mind, it works. I was missing one call:
func imageNodeDidStartFetchingData(_ imageNode: ASNetworkImageNode) {
self.activityIndicator = setupActivityIndicator(bounds: imageNode.style.preferredSize)
imageNode.view.addSubview(self.activityIndicator!)
self.activityIndicator!.startAnimating()
}