Swift redraw view - ios

I have a theme class with a few fields.
Background color, font color and element color.
This class also has few static variations of those
class Themes {
static let defaultTheme = Themes(topColor: "5856D6",bottomColor: "C644FC",elementColor: "1F1F21",fontColor: "2B2B2B", name:"one")
static let defaultTheme1 = Themes(topColor: "5AD427",bottomColor: "FFDB4C",elementColor: "1F1F21",fontColor: "2B2B2B", name:"two")
static let defaultTheme2 = Themes(topColor: "FB2B69",bottomColor: "FF5B37",elementColor: "1F1F21",fontColor: "2B2B2B", name:"three")
static let defaultTheme3 = Themes(topColor: "52EDC7",bottomColor: "5AC8FB",elementColor: "1F1F21",fontColor: "2B2B2B", name:"four")
static let defaultTheme4 = Themes(topColor: "5AD427",bottomColor: "FFDB4C",elementColor: "1F1F21",fontColor: "2B2B2B", name:"five")
let topColor:UIColor
let bottomColor:UIColor
let elementColor:UIColor
let fontColor:UIColor
}
My view controller loads the first one by default.
override func viewDidLoad() {
super.viewDidLoad()
self.initView(ThemeManagement.sharedInstance.getTheme())
}
I have a button which changes the theme by going to the next one of the defaults and looping around back to defaultTheme after defaultTheme4
The original run through inside initView is setting the view correctly. But each pass does not update the view. Despite the log messages of the function being printed.
func initView(objectColor : Themes){
println("initing theme "+objectColor.name+"\n"+objectColor.topColor.debugDescription)
let background = CAGradientLayer().gradient(objectColor.topColor, bottomColorCode: objectColor.bottomColor)
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, atIndex: 0)
codes.delegate = self
codes.placeholder = "####"
codes.textAlignment = NSTextAlignment.Center
codes.font = Themes.defaultThemeWithFontSize(.H2)
codes.textColor = objectColor.fontColor
segments.tintColor = objectColor.elementColor
segments.setTitle(String.fontAwesomeIconWithName(.Male), forSegmentAtIndex: 0)
segments.setTitle(String.fontAwesomeIconWithName(.VenusMars), forSegmentAtIndex: 1)
segments.setTitle(String.fontAwesomeIconWithName(.Female), forSegmentAtIndex: 2)
var font = UIFont.fontAwesomeOfSize(fontsize/2);
var attr = NSDictionary(object: font, forKey: NSFontAttributeName)
segments.setTitleTextAttributes(attr as [NSObject : AnyObject], forState:UIControlState.Normal)
acceptButton.titleLabel?.font = UIFont.fontAwesomeOfSize(fontsize)
acceptButton.setTitle(String.fontAwesomeIconWithName(FontAwesome.ArrowCircleORight), forState: .Normal)
acceptButton.tintColor = UIColor.greenColor()
workedButton.setTitle("yes", forState: UIControlState.Normal)
workedButton.tintColor = objectColor.fontColor
workedButton.layer.cornerRadius = 5
workedButton.titleLabel?.font = Themes.defaultThemeWithFontSize(.H2)
workedButton.backgroundColor = UIColor(red:0.333, green:0.937, blue:0.796, alpha: 0.2)
didntWorkButton.setTitle("no", forState: UIControlState.Normal)
didntWorkButton.tintColor = objectColor.fontColor
didntWorkButton.layer.cornerRadius = 5
didntWorkButton.titleLabel?.font = Themes.defaultThemeWithFontSize(.H2)
didntWorkButton.backgroundColor = UIColor(red:0.333, green:0.937, blue:0.796, alpha: 0.2)
locationButton.setTitle("Current Location", forState: UIControlState.Normal)
locationButton.tintColor = objectColor.fontColor
locationButton.layer.cornerRadius = 5
locationButton.titleLabel?.font = Themes.defaultThemeWithFontSize(.H3)
didItWork.text = "Did it work?"
didItWork.font = Themes.defaultThemeWithFontSize(.H2)
didItWork.textColor = objectColor.fontColor
self.view.setNeedsDisplay()
}
Do I have to do something else to get the background layer to update and the font colors to change?

by this
self.view.layer.insertSublayer(background, atIndex: 0)
every time you insert a new background layer behind the old one so you won't see it.
and change the tintColor of a view won't change the view's color immediately so try to change it's textColor direacly
Edit by cripto
The answer above is correct. I just wanted to add my solution to it.
I simply keep a reference to the layer and remove it as long as its not null. This guarantees that I can set it on the first pass and change it every time there after.
if((backgroundLayer) != nil){
backgroundLayer.removeFromSuperlayer()
}
backgroundLayer = CAGradientLayer().gradient(objectColor.topColor, bottomColorCode: objectColor.bottomColor)
backgroundLayer.frame = self.view.bounds
self.view.layer.insertSublayer(backgroundLayer, atIndex: 0)

Related

External screen on iOS not updating

This function gets run when an external screen gets connected to the device (I can print to console when the function runs), but the external screen doesn't display the label and the view isn't white. What am I missing?
func displayConnected(notification: Notification) {
let extScreen = notification.object as! UIScreen
let extWindow = UIWindow(frame: extScreen.bounds)
extWindow.screen = extScreen
let extVC = UIViewController()
extWindow.rootViewController = extVC
let extView = UIView(frame: extWindow.frame)
//customize extView
extView.backgroundColor = UIColor.white
extWindow.addSubview(extView)
extWindow.isHidden = false
let externalLabel = UILabel()
externalLabel.textAlignment = NSTextAlignment.center
externalLabel.font = UIFont(name: "Helvetica", size: 50.0)
externalLabel.frame = extView.bounds
externalLabel.text = "Hello"
externalLabel.textColor = UIColor.red
extView.addSubview(externalLabel)
}
It needs a reference to the window in a local array.
So, add this to your vc class:
var additionalWindows = [UIWindow]()
And this to your function:
self.additionalWindows.append(extWindow)

Create a masked UIView

I am attempting to do something that I thought would be possible, but have no idea how to start.
I have created a UIView and would like it to be filled with colour, but in the shape defined by an image mask. i.e. I have a PNG with alpha, and I would like the UIView I have created to be UIColor.blue in the shape of that PNG.
To show just how stupid I am, here is the absolute rubbish I have attempted so far - trying to generate just a simple square doesn't even work for me, hence it's all commented out.
let rocketColourList: [UIColor] = [UIColor.blue, UIColor.red, UIColor.green, UIColor.purple, UIColor.orange]
var rocketColourNum: Int = 0
var rocketAngleNum: Int = 0
var rocketAngle: Double {
return Double(rocketAngleNum) * Double.pi / 4
}
var rocketColour = UIColor.black
public func drawRocket (){
rocketColour.set()
self.clipsToBounds = true
self.backgroundColor = UIColor.red
imageView.image = UIImage(named: ("rocketMask"))
addSubview(imageView)
//maskimage.frame = self.frame
//let someRect = CGRect (x: 1, y: 1, width: 1000, height: 1000)
//let someRect = CGRect (self.frame)
//let fillPath = UIBezierPath(rect:someRect)
//fillPath.fill()
//setNeedsDisplay()
}
}
Set image as template, then set tint color for UIImageView. Something like that:
guard let let image = UIImage(named: "rocketMask")?.withRenderingMode(.alwaysTemplate) else { return }
imageView.image = image
imageView.tintColor = .blue
Also you can do that through the images assets:

Label not display in subview of UITableView

I am designing an iOS hamburger menu with a table view in swift3
I want to display the number of notifications in menu tab.
let pieNotifi = UIImageView(frame: CGRectMake(cell.contentView.frame.size.width-40, cell.contentView.frame.size.height/2-7, 30, 20))
let pieLabNotifi = UILabel(frame: CGRectMake(cell.contentView.frame.size.width-40, cell.contentView.frame.size.height/2-7, 30, 20))
let subviews = self.view.viewWithTag(100001)
if myGlobal.unreaddot != 0 { //just a count for notifcaiton
if subviews == nil {
pieNotifi.tag = 100001;
cell.addSubview(pieNotifi)
let pieShape = pieNotifi.layer;
pieShape.backgroundColor = UIColor.red.cgColor
pieShape.cornerRadius = pieShape.bounds.width / 4
pieLabNotifi.textColor = UIColor.white
pieLabNotifi.adjustsFontSizeToFitWidth = true;
pieLabNotifi.minimumScaleFactor = 0.5
pieLabNotifi.textAlignment = NSTextAlignment.center
pieNotifi.addSubview(pieLabNotifi)
pieLabNotifi.text = String(myGlobal.unreaddot)
}
else{
pieLabNotifi.text = String(myGlobal.unreaddot)
}
}else{
let subviews = cell.viewWithTag(100001)
subviews?.removeFromSuperview()
print("remove red dot")
}
however, it only display the red colour layer but do not have the label. refer to the picture shown below
enter image description here
I had tried to set
.sendSubview(to back:)
.bringSubview(toFront: )
.layer.zPosition = 1
all these key word not work for me.
Can anyone advise me to solve this problem?
You are not adding subview to UITableView. According to your code you are adding a subview to the cell of UITableView.
Try to add the subview to the content view of the cell, like:
cell.contentView.addSubView(pieNotifi)
i think the issue is with the positions of your label. You are giving your label x coordinate as cell.contentView.frame.size.width-40, but you want to add it inside the red image view. Changing the x position and y position to 0 (or whatever value you need )for your label will solve your problem. I have modified your code accordingly :
let pieNotifi = UIImageView(frame: CGRectMake(cell.contentView.frame.size.width-40, cell.contentView.frame.size.height/2-7, 30, 20))
let pieLabNotifi = UILabel(frame: CGRectMake(0,0, 30, 20))
let subviews = self.view.viewWithTag(100001)
if myGlobal.unreaddot != 0 { //just a count for notifcaiton
if subviews == nil {
pieNotifi.tag = 100001;
cell.addSubview(pieNotifi)
let pieShape = pieNotifi.layer;
pieShape.backgroundColor = UIColor.red.cgColor
pieShape.cornerRadius = pieShape.bounds.width / 4
pieLabNotifi.textColor = UIColor.white
pieLabNotifi.adjustsFontSizeToFitWidth = true;
pieLabNotifi.minimumScaleFactor = 0.5
pieLabNotifi.textAlignment = NSTextAlignment.center
pieNotifi.addSubview(pieLabNotifi)
pieLabNotifi.text = String(myGlobal.unreaddot)
}
else{
pieLabNotifi.text = String(myGlobal.unreaddot)
}
}else{
let subviews = cell.viewWithTag(100001)
subviews?.removeFromSuperview()
print("remove red dot")
}
Also try to see your label position in UiDebugger , you might discover that label is added at wrong position.

UIView Not Rounded, UITableView Cell Recycling

I have a UIView within the cell of a UITableView. My cellForRowAtIndexPath rounds that view. When I open the viewcontroller and see the table, all the visible views are rounded. However, when I scroll down, the first new cell to appear does not have a rounded view. How do I fix it?
My cellForRowAtIndex looks like this:
let cell = tableView.dequeueReusableCell(withIdentifier: "RepeatingCell", for: indexPath) as! CustomTableViewCell
cell.delegate = self
tableView.contentInset = UIEdgeInsetsMake(10, 0, 0, 0)
if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
let task = tableListInfo_Context[(indexPath as NSIndexPath).row
let accState = task.value(forKey: "state") as? String
//Removed assigning labels their text
let mainView = cell.mainView
mainView.isHidden = false
let circleView = cell.viewToRound
circleView?.isHidden = false
circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
circleView?.layer.masksToBounds = true
cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
} else {
//My last cell hides several views to reveal a view under them
//If I comment this one line out, I get the not-rounded problem only once and it does not show up again even if I continue scrolling up / down
let mainView = cell.mainView
mainView.isHidden = true
let circleView = cell.viewToRound
circleView?.isHidden = true
}
I understand that UITableViews recycle cells and I think that's likely causing me problems. I think it's related to that last cell where I hide views.
In a list of 20 cells, sometimes a non-rounded view shows up as the first to scroll on screen, sometimes the second. As I scroll up/down, randomly a view isn't rounded! I know there's a reason/pattern, but I don't know what.
I also tried adding the rounding code to willDisplayCell:
let circleView = myCell.mainCircle
circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
circleView?.layer.masksToBounds = true
However, that did not solve it. How inconsistent this is is very frustrating. Any help would be appreciated.
FULL CODE AS REQUESTED BY DUNCAN:
func buildCell_RepeatableCell (indexPath: IndexPath) -> CustomTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RepeatingCell", for: indexPath) as! CustomTableViewCell
cell.delegate = self
tableToSV_Left.constant = 10
tableToSV_Right.constant = 10
tableView.contentInset = UIEdgeInsetsMake(10, 0, 0, 0)
if (indexPath as NSIndexPath).row < curLifeList_Context.count {
resetDataCell_ToDefaultLaylout(cell: cell, hideExpand: true)
let task = curLifeList_Context[(indexPath as NSIndexPath).row]
let accState = task.value(forKey: "state") as? String
cell.nameLabel!.text = task.value(forKey: "name") as? String
cell.descLabel!.text = task.value(forKey: "desc") as? String
let redTrashIcon = UIImage(named: "trash")
let maskedIcon = redTrashIcon?.maskWithColor(color: .red)
cell.row5_LeftBtnImg?.image = maskedIcon
let doneGreen = UIColor(colorLiteralRed: 83/255, green: 223/255, blue: 56/225, alpha: 0.9)
let greenCheck = UIImage(named: "check_Green")
let maskedCheck = greenCheck?.maskWithColor(color: doneGreen)
cell.row5_RightBtnImg?.image = maskedCheck
roundQtyCircle(view: cell.mainCircle)
if accState == "Selected" {
cell.circleLine.backgroundColor = doneGreen
cell.mainCircle.borderColor = doneGreen
cell.hdrCrossOutLine.isHidden = false
cell.row5_RightBtnImg.alpha = 0.5
} else {
cell.circleLine.backgroundColor = UIColor(colorLiteralRed: 211/255, green: 211/255, blue: 211/255, alpha: 0.9)
cell.mainCircle.borderColor = UIColor(colorLiteralRed: 211/255, green: 211/255, blue: 211/255, alpha: 0.9)
cell.hdrCrossOutLine.isHidden = true
cell.row5_RightBtnImg.alpha = 1.0
}
cell.dataHdr_Left!.text = doneColon_lczd
cell.dataHdr_Right!.text = lastDoneColon_lczd
cell.dataVal_Left!.text = "0"
cell.dataVal_Right!.text = "-"
if let lastCompDate = task.value(forKey: "lastCompleted") as? Date {
cell.dataVal_Left!.text = "\(task.value(forKey: "timesCompleted") as! Int)"
if NSCalendar.current.isDateInToday(lastCompDate) {
cell.dataVal_Right!.text = "Today"
cell.dataBotDtl_Right.text = ""
} else {
let secondsUntilChange = (lastCompDate.seconds(from: now)) * -1
print("Seconds ago \(secondsUntilChange)")
var timeAgo = getDateString(secondsUntilChange, resetDate: lastCompDate)
timeAgo.remove(at: timeAgo.startIndex)
cell.dataVal_Right!.text = timeAgo
}
}
cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
} else {
buildAddNew_ForRepeatableCell(cell: cell)
}
return cell
}
func resetDataCell_ToDefaultLaylout (cell: CustomTableViewCell, hideExpand: Bool) {
cell.addnewBtn.isHidden = true
cell.nameLabel.isHidden = false
cell.dataHdr_Left.isHidden = false
cell.dataHdr_Right.isHidden = false
cell.dataTopDtl_Right.isHidden = false
cell.dataBotDtl_Right.isHidden = false
cell.mainBox.isHidden = false
}
func buildAddNew_ForRepeatableCell (cell: CustomTableViewCell) {
cell.addnewBtn.isHidden = false
cell.descLabel.text = addNew_lczd //For editor
cell.mainBox.isHidden = true
cell.layoutMargins = UIEdgeInsetsMake(0, 1000, 0, 1000)
cell.container.layoutIfNeeded()
}
Instead of setting layer properties of the view in cellForRowAtIndexPath, set them in your CustomTableViewCell class. You can set them in awakeFromNib Method.
In short move your below code to awakeFromNib.
let circleView = cell.viewToRound
circleView?.layer.cornerRadius = (circleView?.frame.size.width)!/2
circleView?.layer.masksToBounds = true
The sudo code could be something like this:
awakeFromNib(){
self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
viewToRound?.layer.masksToBounds = true
}
#Adeel already told you the problem and the solution in his comment.
You always need to fully configure your cells in every case. Remember that when you get a recycled cell, it might be in any possible leftover state.
In your case, the key bit is:
if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
//Configure cells that are not the last cell
} else {
//Configure cells the last cell
mainView.isHidden = true
}
You hide mainView in the case where you're configuring the last cell, but don't un-hide it if it's not the last cell. So, if you configure the last cell, scroll it off-screen, then scroll back towards the top of the screen, that recycled cell will get reused for an indexPath other than the last one, and it's mainView will never get un-hidden.
if (indexPath as NSIndexPath).row < tableListInfo_Context.count {
//Configure cells that are not the last cell
//-----------------------------
// This is what you are missing
mainView.isHidden = false
//-----------------------------
} else {
//Configure cells the last cell
mainView.isHidden = true
}
#VishalSonawane's answer is good, but to make that work you should use 2 different identifiers, one for all cells but the last one, and a different identifier, with a cell pre-configured with mainView hidden, for the last cell. That way you won't get a recycled last cell and try to use it for one of the other positions in your table view.
Try this, it will work.
override func awakeFromNib() {
super.awakeFromNib()
self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
viewToRound?.layer.masksToBounds = true
}
override func prepareForReuse() {
super.prepareForReuse()
self.viewToRound?.layer.cornerRadius = (viewToRound?.frame.size.width)!/2
viewToRound?.layer.masksToBounds = true
}

How come I can't access the methods of my subview?

In my ViewDidLoad:
let spiralDimension = CGFloat(ScreenWidth! * 0.10)
let spiralName = "spiral.png"
let spiralImage = UIImage(named: spiralName)
spiralView = UIImageView(image: spiralImage!)
spiralView!.frame = CGRect(x: ScreenWidth! / 2 - spiralDimension/2, y: (ScreenHeight!-TabBarHeight!) / 2 - spiralDimension/2 , width: spiralDimension, height: spiralDimension)
spiralView!.tintColor = UIColor.redColor()
self.view.addSubview(spiralView!) //works!!!
Somewhere later...
func fadeBackground(){
UIView.animateWithDuration(self.fadeTime, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: {
var randomIndex = Int(arc4random_uniform(UInt32(CONSTANTS.MainColorScheme.count)))
var subviews = self.view.subviews
for v in subviews{
if v.isKindOfClass(UIImageView){
println(v) //correctly prints my spiral!
v.tintColor = CONSTANTS.MainColorScheme[randomIndex] //can't do it. XCode won't even auto-complete
}
}
}) { (stuff Bool) -> Void in
}
}
I can't assign tintColor to v, even though it prints my class correctly. Can't build successfully.
subviews returns an array of AnyObject. Thus, you need to cast v in order to set tintColor.
Try:
for v in subviews{
if let v = v as? UIImageView {
println(v)
v.tintColor = CONSTANTS.MainColorScheme[randomIndex]
}
}
You have to typecast it.
if v.isKindOfClass(UIImageView){
let iv = v as! UIImageView
v.tintColor = ...
The problem is that the subviews property is defined in iOS as a variable of type [AnyObject] so Swift doesn't know that your subviews are members of UIView, which has the property tintColor.

Resources