Detail Disclosure loading on top of the previous detail disclosure - ios

I have a map view with annotations and every annotation has a corresponding date with day, month, and time.
I'm pushing the day to the new view controller then using a switch to see what day of the month it falls on and then displaying the corresponding calendar icon. I can't put the func anywhere other than viewDidAppear which cause a brief lag and two, prevents me from using
for view in self.view.subviews {
view.removeFromSuperview()
}
So two questions:
how can I call the dayCalendar function before viewDidAppear (anywhere else I've tried to put the code, it returns a nil)
how do I prevent my detailDisclosures from appearing on top of one another?
This is the code responsible for showing the image:
override func viewDidAppear(animated: Bool) {
dayCalendar()
}
func dayCalendar() {
switch day {
case 1:
let calendarDay = "Calendar 1-50.png"
let calendarDayImage = UIImage(named: calendarDay)
let calendarDayImageView = UIImageView(image: calendarDayImage!)
calendarDayImageView.frame = CGRect(x: 25 , y: 350, width: 50, height: 50)
self.view.addSubview(calendarDayImageView)
case 8:
let calendarDay = "Calendar 8-50.png"
let calendarDayImage = UIImage(named: calendarDay)
let calendarDayImageView = UIImageView(image: calendarDayImage!)
calendarDayImageView.frame = CGRect(x: 25 , y: 350, width: 50, height: 50)
self.view.addSubview(calendarDayImageView)
case 11:
let calendarDay = "Calendar 11-50.png"
let calendarDayImage = UIImage(named: calendarDay)
let calendarDayImageView = UIImageView(image: calendarDayImage!)
calendarDayImageView.frame = CGRect(x: 25 , y: 350, width: 50, height: 50)
self.view.addSubview(calendarDayImageView)
case 12:
blahh blahh blahh all the way down
case 31:
let calendarDay = "Calendar 30-50.png"
let calendarDayImage = UIImage(named: calendarDay)
let calendarDayImageView = UIImageView(image: calendarDayImage!)
calendarDayImageView.frame = CGRect(x: 25 , y: 350, width: 50, height: 50)
self.view.addSubview(calendarDayImageView)
default:
println("Default")
break
}
}

First off: why are you actually switching? As far as I can tell the code can be reduced to
func dayCalendar() {
if day < 1 || day > 31 {
return
}
let calendarDay = "Calendar \(day)-50.png"
let calendarDayImage = UIImage(named: calendarDay)
let calendarDayImageView = UIImageView(image: calendarDayImage!)
calendarDayImageView.frame = CGRect(x: 25 , y: 350, width: 50, height: 50)
self.view.addSubview(calendarDayImageView)
}
You might want to move the call to viewWillAppear.
To fix the overlaying of the detail closures you have two options:
remove the added calendarDayImageView every time you display a new day.
or (much better)
reuse the already added image view if it is already present (might want to create a lazy variable) and just change the image it is displaying.
The later one you can achieve in the following way:
// add this variable to your class
lazy var calendarDayImageView: UIImageView = {
[unowned self] in
let imageV = UIImageView(frame: CGRect(x: 25 , y: 350, width: 50, height: 50))
self.view.addSubview(imageV)
return imageV
}()
// change the dayCalender to make use of the newly added variable
func dayCalendar() {
if day < 1 || day > 31 {
return
}
let calendarDay = "Calendar \(day)-50.png"
let calendarDayImage = UIImage(named: calendarDay)
calendarDayImageView.image = calendarDayImage
}
Alternatively you can just move the initialization of the view inside viewDidLoad and have dayCalender again only set the actual image - but i like lazy vars ;)

Related

object value doesn't change after didset

I am using some cocoa pods frame to make UICollectionView. I am able to get the right data for each cell and setup views.
I want to have different ani
The animation end point should be related to the value of dataSource.The problem is that I can't use the didSet value as I wish. The frame of subviews I added are (0,0,0,0), the values of objects keep the same as initial value outside the didSet. I am not able to access the dataSource Value in setupView function.
Expected effect is that the red bar will move from left side to the right position:
class CereCell : DatasourceCell {
override var datasourceItem: Any?{
didSet {
guard let cere = datasourceItem as? CeremonyDay else { return }
nameLabel.text = cere.name
nameLabel.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
currentBar.frame = CGRect(x: 100, y: 100, width: cere.percentage*100 , height: 10)
position.x = cere.percentage
}
}
let currentBar: UIView = {
let currentBar = UIView(frame: CGRect(x: 290, y: 100, width: 300, height: 10))
currentBar.backgroundColor = .red
return currentBar
}()
override func setupViews() {
super.setupViews()
contentView.addSubview(currentBar)
let frame = currentBar.frame
print(frame)
It just print the (290, 100, 300, 10), which is not the value in didSet.

convert(_ rect: CGRect, to view: UIView?) does nothing when I pass nil to the view parameter

I am trying to get coordinates of the view in superview's coordinate local. So according to the docs if view is nil, this method will instead convert to window base coordinates. So In my case it did nothing, but if I pass view to it it works.
let zoomImageView = UIImageView()
let xoomImageView2 = UIImageView()
let xoomImageView3 = UIImageView()
let stratingFrame2 = CGRect(x: 20, y: 10, width: 150, height: 130)
let startingFrame1 = CGRect(x: 40, y: 20, width: 200, height: 160)
let startingFrame = CGRect(x: 0, y: 200, width:375, height: 200)
override func viewDidLoad() {
super.viewDidLoad()
xoomImageView3.frame = stratingFrame2
xoomImageView3.backgroundColor = UIColor.purple
xoomImageView2.frame = startingFrame1
xoomImageView2.backgroundColor = UIColor.black
zoomImageView.frame = startingFrame
zoomImageView.backgroundColor = UIColor.red
zoomImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(animate)))
zoomImageView.isUserInteractionEnabled = true
// zoomImageView.image = UIImage(named: "zuckdog")
zoomImageView.contentMode = .scaleAspectFill
zoomImageView.clipsToBounds = true
view.addSubview(zoomImageView)
zoomImageView.addSubview(xoomImageView2)
xoomImageView2.addSubview(xoomImageView3)
print(zoomImageView.frame)
print(xoomImageView2.frame)
print(xoomImageView3.frame)
let newFrame = zoomImageView.convert(xoomImageView3.frame, to: nil)
print(newFrame)
Maybe someone can tell what is happening?
if you want to convert in coordinates one major rule is
View must be a subview to user coordinates conversion methods
Now look at your code
let newFrame = zoomImageView.convert(xoomImageView3.frame, to: nil)
print(newFrame)
zoomImageView is never been subview xoomImageView3 in your code and one more thing xoomImageView3 has no DIRECT relation with zoomImageView object
What you need to do is
First Convert xoomImageView3 Frame to zoomImageView
let xoomImageView3FrameAccordingTo_zoomImageView = xoomImageView2.convert(xoomImageView3.frame, to: zoomImageView)
now
let newFrame = zoomImageView.convert(xoomImageView3FrameAccordingTo_zoomImageView, to: self.view)

Assign tags for dynamic UIButtons

I found this example of a dynamic view of UIButtons at: http://helpmecodeswift.com/advanced-functions/generating-uibuttons-loop
I inserted this code into my app but unfortunately I can't find a way to set a tag for each buttons.
My target is to know which button is tapped, count the amount of clicks for each button and save this information into my DB.
override func viewDidLoad() {
super.viewDidLoad()
var arrayOfVillains = ["santa", "bugs", "superman", "batman"]
var buttonY: CGFloat = 20
for villain in arrayOfVillains {
let villainButton = UIButton(frame: CGRect(x: 50, y: buttonY, width: 250, height: 30))
buttonY = buttonY + 50 // we are going to space these UIButtons 50px apart
villainButton.layer.cornerRadius = 10 // get some fancy pantsy rounding
villainButton.backgroundColor = UIColor.darkGrayColor()
villainButton.setTitle("Button for Villain: \(villain)", forState: UIControlState.Normal) // We are going to use the item name as the Button Title here.
villainButton.titleLabel?.text = "\(villain)"
villainButton.addTarget(self, action: "villainButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(villainButton) // myView in this case is the view you want these buttons added
}
}
func villainButtonPressed(sender:UIButton!) {
if sender.titleLabel?.text != nil {
println("You have chosen Villain: \(sender.titleLabel?.text)")
} else {
println("Nowhere to go :/")
}
}
}
So how it is possible to set and get the tag for/from each button in code (in this example)?
Thank you in advance!
You can use enumerated array to access indexes of the current item in iteration and use that as a tag:
override func viewDidLoad() {
super.viewDidLoad()
var arrayOfVillains = ["santa", "bugs", "superman", "batman"]
var buttonY: CGFloat = 20
for (index, villain) in arrayOfVillains.enumerated() {
let villainButton = UIButton(frame: CGRect(x: 50, y: buttonY, width: 250, height: 30))
buttonY = buttonY + 50 // we are going to space these UIButtons 50px apart
// set the tag to current index
villainButton.tag = index
villainButton.layer.cornerRadius = 10 // get some fancy pantsy rounding
villainButton.backgroundColor = UIColor.darkGrayColor()
villainButton.setTitle("Button for Villain: \(villain)", forState: UIControlState.Normal) // We are going to use the item name as the Button Title here.
villainButton.titleLabel?.text = "\(villain)"
villainButton.addTarget(self, action: "villainButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(villainButton) // myView in this case is the view you want these buttons added
}
}
}
#objc func villainButtonPressed(sender:UIButton!) {
// tag of pressed button
print(">>> you pressed button with tag: \(sender.tag)")
if sender.titleLabel?.text != nil {
println("You have chosen Villain: \(sender.titleLabel?.text)")
} else {
println("Nowhere to go :/")
}
}
You should use an integer-based for loop for this.
for i in 0 ..< arrayOfVillains.count {
let villain = arrayOfVillains[i]
let villainButton = UIButton(frame: CGRect(x: 50, y: buttonY, width: 250, height: 30))
villainButton.tag = i
}
You can do as follows,
var buttonTag = 1
for villain in arrayOfVillains {
let villainButton = UIButton(frame: CGRect(x: 50, y: buttonY, width: 250, height: 30))
villainButton.tag = buttonTag
buttonTag += 1
}

Programmatically display UIScrollView in Swift

I have view controller with views: top level View Contains Container View which contains two views: bottomView, topView, as shown.
Scene
I want to display from: to: date range in the topView. In the bottomView I want to display a UIScrollView that contains two columns which I can scroll. I did that but the topView and BottomView overlap when I introduce scrollView. When I scroll I can see the views getting separated and as soon as i Let go the scrollbar they overlap again.
can someone tell me how to fix it? I just don't seem to understand how the scrollView and bottomView are to be associated.
Code below:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
//scrollView.frame = innerView.bounds
innerView.frame = CGRect(x:0, y:0, width:scrollView.contentSize.width, height:scrollView.contentSize.height)
}
func buildBottomView () {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let ht:Int = 21
let incrX:Int = 5
let incrY:Int = 5
let gapCol1:Int = 5
let col1Width:Int = 65
let col2Width:Int = 65
let startY:Int = 5
let col1StartX:Int = 10
let col2StartX:Int = col1StartX + col1Width + gapCol1
var loadRowStartY: Int = 0
// column headers
categoryColumnLabel.text = "Interval"
categoryColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
//categoryColumnLabel.font = UIFont.systemFont(ofSize: 14)
categoryColumnLabel.frame = CGRect(x: col1StartX, y:startY, width: col1Width, height: ht)
categoryColumnLabel.textAlignment = NSTextAlignment.left
categoryColumnLabel.tag = 1
innerView.addSubview(categoryColumnLabel)
valueColumnLabel.text = "Values"
valueColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
//valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
valueColumnLabel.frame = CGRect(x: col2StartX, y:startY, width: col2Width, height: ht)
valueColumnLabel.textAlignment = NSTextAlignment.center
valueColumnLabel.tag = 3
innerView.addSubview(valueColumnLabel)
let sepLine:UIView = UIView()
sepLine.frame = CGRect(x: col1StartX, y:startY+ht+incrY, width: Int(screenWidth-20), height: 2)
sepLine.backgroundColor = UIColor.darkGray
sepLine.tag = 60
loadRowStartY = startY+ht+incrX+ht
innerView.addSubview(sepLine)
for i in 0 ..< 24 {
let timeIntervalLabel = UILabel()
let value2Label = UILabel()
print("display load profile")
let loadStruct = loadDict[String(i)] as! CommercialProfile
print (loadStruct.timeInterval)
timeIntervalLabel.text = loadStruct.timeInterval
timeIntervalLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)
//valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
timeIntervalLabel.frame = CGRect(x: col1StartX, y:loadRowStartY, width: col1Width, height: Int(timeIntervalLabel.font.lineHeight))
timeIntervalLabel.textAlignment = NSTextAlignment.center
innerView.addSubview(timeIntervalLabel)
print(loadStruct.value)
value2Label.text = loadStruct.value
value2Label.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)
//value2Label = UIFont.systemFont(ofSize: 14)
value2Label.frame = CGRect(x: col2StartX, y:loadRowStartY, width: col2Width, height: Int(value2Label.font.lineHeight))
value2Label.textAlignment = NSTextAlignment.center
innerView.addSubview(value2Label)
loadRowStartY = loadRowStartY + incrY + Int(value2Label.font.lineHeight)
}
you are setting the bounds of the scrollView to the size of the whole view with this code: scrollView.frame = view.bounds.
The scrollView only needs to scroll the content in the bottom view. Scroll Views have their own content, that is normally larger than the viewable area of the screen/view. The scroll view just allows you to pan the viewport of that view.
So add the bottom view and setup your constraints on that. add the scrollView to the bottom view and then add your content into the scrollView.
Make sure that your bottom view has clipToBounds set to true and then you should be able to keep the headers in place and just scroll the content.
I'll try and put an example together for you shortly.
EDIT:
I've just created this simple example which shows the scroll behaviour you need. This works in a playground or just as a simple view controller. I've intentionally not used auto layout or setup constraints due to time, but you will see what you need to solve your issue
class ViewController: UIViewController {
var topView: UIView!
var bottomView: UIView!
var scrollView: UIScrollView!
var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let screenSize = UIScreen.main.bounds
self.topView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: 100))
self.bottomView = UIView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
self.scrollView = UIScrollView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
self.contentView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height * 3))
self.view.backgroundColor = .white
self.view.addSubview(self.topView)
self.view.addSubview(self.bottomView)
self.bottomView.addSubview(self.scrollView)
self.scrollView.addSubview(self.contentView)
self.bottomView.clipsToBounds = true
self.scrollView.contentSize = CGSize(width: screenSize.width, height: screenSize.height * 3)
self.contentView.backgroundColor = .gray
}
}

ViewController won't reload properly on first Simulator launch

I have a weird issue that I just found out when I re-installed and launched my project first time on Simulator
My main ViewController has a collection view that will show a list of recipes. After a recipe is added and navigated back to this screen, the list will reflect the update.
There is also a ScrollView that appears when recipe.count > 0 with subview containing buttons (to filter list by category). The scrollview reflect the update on the recipes but it won't work on the very first launch in Simulator.
I've tried reloading collectionview, fetching recipe data on viewDidAppear, viewWillAppear but no luck with the first-time launch.
How can I make sure the first app launch experience is the same with the subsequent launches? Is there any known issue with first-time launch on Simulator that I should know about?
Here is the code that creates buttons and add to ScrollView.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
fetchRecipe()
collection.reloadData()
if recipes.count == 0 {
categoryScrollView.hidden = true
categoryScrollViewHeight.constant = 0
} else {
populateCategoryScrollView()
}
}
func populateCategoryScrollView() {
//Create category sort buttons based on fetched category.name and add to scrollview
if recipes.count > 0 {
var categories: [String] = []
for recipe in recipes {
if let value = recipe.category {
let category = value as! RecipeCategory
categories.append(category.name!)
}
}
var distinctCategories = Array(Set(categories))
var widthStack = 0
if distinctCategories.count != 0 {
for subView in categoryScrollView.subviews {
subView.removeFromSuperview()
}
for i in 0..<distinctCategories.count {
//Pilot button creation to get width
let frame1 = CGRect(x: 0, y: 6, width: 80, height: 40 )
let button = UIButton(frame: frame1)
button.setTitle("\(distinctCategories[i])", forState: .Normal)
button.sizeToFit()
let buttonWidth = Int(button.frame.size.width)
var frame2: CGRect
if i == 0 {
frame2 = CGRect(x: 10, y: 6, width: buttonWidth+20, height: 30 )
} else {
frame2 = CGRect(x: 10 + widthStack, y: 6, width: buttonWidth+20, height: 30 )
}
widthStack += buttonWidth+32
let button1 = UIButton(frame: frame2)
let attributedTitle = NSAttributedString(string: "\(distinctCategories[i])".uppercaseString, attributes: [NSKernAttributeName: 1.0, NSForegroundColorAttributeName: UIColor.blackColor()])
button1.setAttributedTitle(attributedTitle, forState: .Normal)
button1.titleLabel!.font = UIFont(name: "HelveticaNeue", size: 13.0)
button1.layer.borderWidth = 2.0
button1.layer.borderColor = UIColor.blackColor().CGColor
button1.backgroundColor = UIColor.whiteColor()
button1.addTarget(self, action: "filterByCategory:", forControlEvents: UIControlEvents.TouchUpInside)
self.categoryScrollView.addSubview(button1)
}
}
}
}
The first time you enter the application, the recipes.count is 0, so you are hiding the scroll view by the constraint categoryScrollViewHeight.constant = 0. So next time you create a new recipe, the scroll view is still with a zero height. That's why you didn't see the scroll on first time launch.

Resources