How to adjust UIImageView margin programmatically with Swift - ios

I have a UIImageView as part of a UICollectionViewCell that I would like to use to visually display a quantity (like a progress bar). In this case the ImageView and its offset represent the percentage remaining of a given item represented by the cell.
Imagine an image of water inside a glass. When the glass is full you see all of the water texture. When the glass is half full, you see half of the water image. so on and so forth...
I want to adjust the top margin of the ImageView based on the percentage remaining. for example:
var image : UIImageView
var item = inventoriedItem(percentageRemaining : 100)
var pctLeft = item.percentageRemaining
pctLeft = 42
// PSEUDOCODE:
image.topMarginOffset = 100 - pctLeft

You could try adjusting the frame of the UIImageView based on the percentage left.
var image = UIImageView(frame: CGRect(x: 20, y: 10, width: 50, height: 100))
var pctLeft = 0.42
//assuming you want a vertical progress bar (like a glass of water).
image.frame = CGRect(x: 20, y: 10 + (100 * (1 - pctLeft)), width: 50, height: 100 * pctLeft)
Here I programmatically push the Y value down based on the reduced size of the bar. Let me know if this is what you were looking for...

Related

Swift cropping an image to remove bottom part

Here is the image I wish to crop (to get rid of the options at the bottom. The Back, Draw and Delete are actual menu items, the ones above it are part of the image
this is the result of changing y: 100 and height : 1948
I want to remove the bottom 100 coordinates of an image. My application is on the iPad and all of the images are saved horizontally.
This code is one I took from stack overflow on a similar question, However it does not work for any values of x,y,width and height. The image is never cropped from the bottom.
Changing the values tends to only crop the image from the left and right (the 1536 pixel part of the iPad and not the 2048)
func cropImage(image: UIImage) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1536, height: 2048) // 1536 x 2048 pixels
let cgImage = image.cgImage!
let croppedCGImage = cgImage.cropping(to: rect)
return UIImage(cgImage: croppedCGImage!)
}
Does anyone know what is missing? All i need it to crop out the bottom part as the images are saves of a previous view (however the menu options appear in a a=stack view in the bottom which are still there when I save the image, hence the crop. Thanks
The "image I wish to crop" image you posted is 2048 x 1536 pixels...
If you want to crop the "bottom 100 pixels" your crop rect should be
CGRect(x: 0, y: 0, width: 2048, height: 1536 - 100)

Making a very simple graph with uibezierpath

my question was
I want to create a simple line graph with certain values. This is done in a view within the mainviewcontroller. I created a UIview named chart. I pass the data to the chart when its retrieved from the API. I figured out how to draw the axis but I am stuck now. I cant find anything on google on how to set labels on intervals and to make the points appear dynamically.
draw the xasis and its labels.
draw the dots in the graph.
My salution
i figured out how to do all the things i asked for.
The code I have now:
class ChartView: UIView {
//some variables
var times: [String] = []
var AmountOfRain: [Double] = []
let pathy = UIBezierPath()
let pathx = UIBezierPath()
var beginwitharray = Array<CGFloat>()
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
//draw the y line
pathy.move(to: CGPoint(x: 30, y: 10))
pathy.addLine(to: CGPoint(x: 30, y: 10))
pathy.addLine(to: CGPoint(x: 30, y: frame.size.height - 30))
UIColor.black.setStroke()
pathy.lineWidth = 1.0
pathy.stroke()
//draw the x line
pathx.move(to: CGPoint(x: 30, y: frame.size.height - 30))
pathx.addLine(to: CGPoint(x: 30, y: frame.size.height - 30))
pathx.addLine(to: CGPoint(x: frame.size.width - 30, y: frame.size.height - 30))
UIColor.black.setStroke()
pathx.lineWidth = 1.0
pathx.stroke()
//when the data arrives form the SUPER slow duienradar API refresh it with the data
if beginwitharray != []{
//remove the label retriving data
let label = viewWithTag(1)
DispatchQueue.main.sync {
label?.removeFromSuperview()
}
//create the dots in the graph
var point = CGPoint()
//simple way to do 2 loop in 1 loop.
var intforbeginarray = 0
let stoke = UIBezierPath()
//get the first 6 itmes out of the rain array cuz of space issues
let first6aumountarray = AmountOfRain[0...5]
stoke.move(to: CGPoint(x: 30, y: self.frame.size.height - 30))
//loop trough the data in the amounts array
for amount in first6aumountarray{
//determen the hight of the dot
let InitialHeight = (CGFloat(amount) * (self.frame.size.height - 30))/6
let pointHeight = (frame.size.height - 30) - InitialHeight
//make the point so we can draw it using UIbezierpath()
point = CGPoint(x: beginwitharray[intforbeginarray] + 20, y: pointHeight)
intforbeginarray += 1
//create the dot
let dot = UIBezierPath()
dot.addArc(withCenter: point, radius: CGFloat(5), startAngle: CGFloat(0), endAngle: CGFloat(360), clockwise: true)
UIColor.black.setFill()
dot.lineWidth = 30
dot.fill()
//create the line between dots will give a warning on the last one cuz the last one doenst go anyway
stoke.addLine(to: point)
stoke.move(to: point)
stoke.lineWidth = 1
UIColor.black.setStroke()
}
//make the strokes
stoke.stroke()
}
}
func getvalues(RainData: [Double], TimesData:[String]){
//assing the data to the subview
self.AmountOfRain = RainData
self.times = TimesData
//xaxis values
let maxint = [0, 1, 2, 3, 4, 5, 6]
//calculate the hight spacing to fit the graph
let heightperstep = ((self.frame.size.height - 5)/6)-5
var beginheight = self.frame.size.height - 35
//calculate the width spacing to fit the graph
let widthperstep = ((self.frame.size.width - 5)/6)-5
var beginwith = CGFloat(30)
//extra check to see if we have data at all.
if times != []{
//get the first 6 items out of the times array for use in our graph
let first6 = times[0...5]
//draw the label on the main queue
DispatchQueue.main.sync {
//draw the xaxis labels accroding to the spacing
for number in maxint{
let label = UILabel(frame: CGRect(x: 5, y: beginheight, width: 25, height: 15))
label.text = "\(number)"
self.addSubview(label)
beginheight = beginheight - heightperstep
}
//draw the yaxis labels according to the spacing
for time in first6{
let label = UILabel(frame: CGRect(x: beginwith, y: self.frame.size.height - 20, width: 55, height: 15))
label.text = time
self.addSubview(label)
beginwitharray.append(beginwith)
beginwith = beginwith + widthperstep
}
}
}
//redrawthe graph with new data.
setNeedsDisplay()
}}
Any help would be appreciated. I also can't use a lib or a pod since this is a school project and I need to create a simple graph.
EDIT:
Completed my code, cleared up an error when running this code
What I did first was to draw the x-asis and the y-axis. After this I considered reasonable values for the aumountofrain data. this turns out cannot really be higher then 6. Since I could fit around 6 labels in the space I have the steps where easy go down by 1 till I hit 0. The calculations I did are for my specific frame height. After I figured it all out and the padding for the y-asxis. It was a matter of figuring out how to get the dots in the right place. Since I already have the data in the beginwitharray I just needed to calculate the height. Then it was simply loop trough the data and draw each dot. Then I just had to connect the dots using the uibezierpath.
i hope my troubles will save someone a lot of time when they read how i done it.
This might be helpful: Draw Graph curves with UIBezierPath
Essentially what you need to do is for every data set you have you need to know the y-axis range of values and based on those ranges assign each value a CGFloat value (in your case inches of rain needs to correlate to a certain CGFloat value). Let's say you have your set amountOfRain = [0.1, 1.3, 1.5, 0.9, 0.1, 0] so your range is var rangeY = amountOfRain.max() - amountOfRain.min(). now lets find out where your first data point 0.1 should go on your graph by converting inches of rain to a CGFloat value that corresponds to the axis you've drawn already, this equation is just basic algebra: let y1 = (amountOfRain[0]/rangeY)*((frame.size.height-30) - 10) + 10 now it looks like your rain samples are at regular intervals so maybe let x1:CGFloat = 10 now you can add a dot or something at the CGPoint corresponding with (x1,y1). If you did this with all the data points it would create a graph that has your maximum value at the top of the graph and minimum value at the bottom. Good Luck!

Tips on putting spacing between cells

How do i add spacing between each cell here -
for i in 0...50 {
let cell = UIView()
cell.backgroundColor = .lightGray
cell.frame = CGRect(x: Double(i) * Double(32.52), y: 90, width: 350, height: 500)
view.addSubview(cell)
}
Because with the code above the view looks like this
Your cells are much too wide for the increment you're using horizontally. Your increment offsets them by 32.52 (why the fractional part?) and the views have a width of 350.
So your view spans will be:
From X up to
------ -----
0 350
32.52 403.52 (overlapping from 32.53 to 350)
65.04 456.04 (overlapping from 65.04 to 403.52)
and so on ...
You probably meant to use a narrower frame width (or a larger increment).
The spacing will be the difference between your increment and the frame width.
For example: an increment of 32 with a width of 30 will give you a 2 pixel gap between views.

How to make text the same size on all devices in SpriteKit?

In SpriteKit, is there a way to make an SKLabelNode look the same size, regardless of the device, eg: Looks the same size on a iPhone 5 as a 6Plus?
I've tried using this method someone else recommended:
let textRect = CGRect(x: 0, y: 0, width: frame.width * 0.4, height: frame.height * 0.045)
let scalingFactor = min(textRect.width / text.frame.width, textRect.height / text.frame.height)
text.fontSize *= scalingFactor
But it doesn't make all text the same size, as words like "man" aren't as physically tall as words like "High" (due to it's "y" and "h" sticking out).
So is there a method to make text look the same size on all devices? At the moment I create the SKLabelNode like so:
let text = SKLabelNode(text: "Start")
text.fontSize = 30
text.position = CGPoint(x: 0, y: 0)
addChild(text)
The issue here is that you are trying to scale the fontSize, and this does not really play well with complex decimal numbers. Instead, after you create your label, just scale that to the scale factor that you are using to scale everything else
let text = SKLabelNode(text: "Start")
text.fontSize = 30
text.position = CGPoint(x: 0, y: 0)
text.xScale = xScaleFactor
text.yScale = yScaleFactor
where xScaleFactor and yScaleFactor are the factors you are using to determine your scale. (This number should only have to be calculated once, and then stored, if you are not doing that, I would recommend making that change)
Basically in the code you provided it is done like this:
let textRect = CGRect(x: 0, y: 0, width: frame.width * 0.4, height: frame.height * 0.045)
let scaleFactorX = textRect.width / text.frame.width
let scaleFactorY = textRect.height / text.frame.height
I think it's more like an algorithm question. Think about you need to implement the same thing in TV, iPad or in the iPhone device. You should think about storing its absolute value rather than its actual value.
The formula should be width for store value = actual width for this device / device width. The same with the height. Then, if you use the same image data in other devices. You will just need to multiply the new device width/height.

Resize parent UIView based on its children size

I'm new in iOS programming and still don't know how to handle correctly basic stuff like this so I hope someone has good practice to share.
I created a custom keyboard for the user to answer to a question.
It's an UIView containing some UIButton.
How does one center the whole keyboard?
I know I could calculate the width of the keyboard by adding up the width of each key but this seems quite complicated for such a simple action.
Here's my code (in Swift) for now, thanks.
keyboardContainer = UIView(frame: CGRectMake(0, 0, self.frame.width, 200))
keyboardContainer!.center = CGPoint(x: keyboardContainer!.frame.width / 2, y: self.frame.height - keyboardContainer!.frame.height / 2)
self.addSubview(keyboardContainer!)
let gap = Int(self.frame.width) / 8
for i in 0..<keyboardLetters!.count {
let posX = Int(i % 7) * gap + 22
let posY = Int(i / 7) * gap + 25
let key = Key(frame: CGRectMake(0, 0, 44, 44))
key.letter = keyboardLetters![i]
key.tag = i
key.center = CGPoint(x: posX, y: posY)
keyboardContainer!.addSubview(key)
key.addTarget(self, action: "onKeyboardTap:", forControlEvents: .TouchUpInside)
}
Move the center of keyboardContainer and all the buttons will move with it.

Resources