Creating Variable Constraints for a UIImageView in Swift? - ios

I'm trying to make some UIImageViews in Xcode that fill the screen based on different conditions. For example, I might have a 3x3 square of images that have to fill the screen, or a 4x4 square that must fill the screen, based on different initial conditions. Every time I try to accomplish this, the ImageViews just end up being the same size for both conditions. I've tried many different solutions but the one I'm currently trying is:
if fieldDimensions == 3 {
let spacing = screenWidth / 16
let boxsize = screenWidth / 4
let xadjust = spacing / 2 //Value to help align view
let interval = spacing + boxsize
Button1Image.translatesAutoresizingMaskIntoConstraints = false
button1height.constant = boxsize
button1width.constant = boxsize
button2height.constant = boxsize
button2width.constant = boxsize
}
else if fieldDimensions == 4 {
let spacing = screenWidth / 20
let boxsize = screenWidth * 3/16
let interval = spacing + boxsize
button1height.constant = boxsize
button1width.constant = boxsize
button2height.constant = boxsize
button2width.constant = box size
All of the button heights and widths are linked to the height and width constraints in the storyboard (I just control-dragged them). Any help would be really appreciated, I've been working on this problem for almost a week now, thanks!!

Related

danielgindi/Charts: get left yAxis width

Is there a way to get the left yAxis width? I'm drawing custom markers and would like to avoid to draw over the left yAxis.
There is a solution for the android version of this library but it doesn't work in iOS because of different render methods: How to get the width of y-axis label in MPAndroidChart
You can retrieve the axis width by the requiredSize() method.
chartView.leftAxis.requiredSize()
This is what requiredSize() method actually does:
#objc open func requiredSize() -> CGSize
{
let label = getLongestLabel() as NSString
var size = label.size(withAttributes: [NSAttributedString.Key.font: labelFont])
size.width += xOffset * 2.0
size.height += yOffset * 2.0
size.width = max(minWidth, min(size.width, maxWidth > 0.0 ? maxWidth : size.width))
return size
}
Use this only after you set the chart data, cause the chart will automatically calculate the width with the longest label in your axis.
Hope it helps
Does this give you what you need?
let yourBarChartView = BarChartView()
let leftAxis = yourBarChartView.leftAxis
let width = leftAxis.axisLineWidth
Hope this helps!

positioning a button programmatically with unexpected result

I'm trying to position a button at a fixed position inside a UIImageView (AspectFit) which itself is inside a UIScrollView. This worked perfectly on my first try when the UIScrollView and the UIImageView containers both covered the whole screen, the button was pinned to a certain location of the image and stayed at position during zooming. You can see the result in the below image.
As you can obviously see there are white borders above and below the image (related to aspect fit) therefore I had to do some calculation to get the margin from top to calculate the "real" y position of the red square. My code looks like this:
let originalImageSize: CGSize = CGSize(width: image.size.width, height: image.size.height)
let aspectRatio = image.size.width/image.size.height;
let requiredHeight = self.view.bounds.size.width / aspectRatio;
let screenHeight = self.view.bounds.height;
let marginTop = (screenHeight - requiredHeight) / 2;
let renderedImageSize: CGSize = CGSize(width: self.view.bounds.width, height: requiredHeight)
let x:Double = 0
let y:Double = 0
let button = UIButton()
button.frame = CGRect(x: Double(renderedImageSize.width/originalImageSize.width) * x,
y: Double(renderedImageSize.height/originalImageSize.height) * y + Double(marginTop),
width: 10, height: 10)
button.backgroundColor = UIColor.red
imageView.addSubview(button)
As you see I calculated the "marginTop" to get the real y position. The square is perfectly located on x: 0 and y:0 (relative to the image). So far so good, this example worked perfectly.
Now I created a new view which contains a navigation bar and tab bar. The scrollView is in between and no longer covers the whole screen but only the area between my navigation and my tab bar. The imageView has the same size like my scrollView. Pretty much the same as in the example above. Now I tried to set my button a specific location again, but this time there is an offset on the y axis of exactly 6 pixels and I have no idea what I'm doing wrong. And to make it even worse when testing it on other devices the offset on the y axis is even bigger than 6 pixels, while the first example works perfectly accross all devices I tested. You can see the result with the "wrong" y-axis value here.
I changed my code to the following, based on the fact that sizes should be calculated according to the "new" scrollView size.
let originalImageSize: CGSize = CGSize(width: image.size.width, height: image.size.height)
let aspectRatio = image.size.width/image.size.height;
let requiredHeight = scrollView.bounds.size.width / aspectRatio;
let screenHeight = scrollView.bounds.height;
let marginTop = (screenHeight - requiredHeight) / 2;
let renderedImageSize: CGSize = CGSize(width: scrollView.bounds.width, height: requiredHeight)
let x:Double = 0
let y:Double = 0
let button = UIButton()
button.frame = CGRect(x: Double(renderedImageSize.width/originalImageSize.width) * x,
y: Double(renderedImageSize.height/originalImageSize.height) * y + Double(marginTop),
width: 10, height: 10)
button.backgroundColor = UIColor.red
imageView.addSubview(button)
A quick workaround would be something likes this, but it is hacky as hell and doesn't work for other device sizes and I obviously want to learn how to do it the right way:
[...] y: Double(renderedImageSize.height/originalImageSize.height) * y + Double(marginTop) - 6, [...]
I've been sitting on this for hours now and still don't have any idea why the y-axis is off even though the calculation of the top margin should be right and why the y axis offset is even bigger now on different devices. I'm thankful for any advice as I'm pretty new to iOS developing and I guess I'm missunderstanding something related to calculating correct sizes.
Turns out, that the solution to my problem is rather simple. I first started wondering when I saw that scrollView.bounds.size.height
returned a bigger value than the actual screen height on devices smaller than the iPhone X, which seemed pretty strange. Then I tried to figure out how to find the "real" size of my scrollView on different devices, because it is obviously not really bigger than the whole screen according to the simulator visual result.
So instead of doing the above quoted calculation inside viewDidLoad() doing all my calculation in viewDidLayoutSubviews() instead and this solved the whole problem.
The only thing I'm still wondering about is, why there was an offset even on the iPhone X as it was my default template in Xcode.

How can I put a coin label in the corner of every device in my universal app?

I am wanting to create a SKLabelNode that is in the top left corner of every device. Also, to the left of the label in the top left corner I want an image that is a coin to show that this is the current coin count label. Every time I place my label in the corner on my iPhone 6s+ it isn't in the corner on my iPad.
Here is my code so far:
cornerCoin.position = CGPoint(x: screenWidth / -3.1, y: screenHeight / 3.175)
cornerCoin.zPosition = 10
cameraNode.addChild(cornerCoin)
coinLabel.position = CGPoint(x: screenWidth / -2.4, y: screenHeight / 8)
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
cameraNode.addChild(coinLabel)
Personally, I would not be tracking screenWidth/screenHeight, it goes against the foundation of SpriteKit to begin with (Your scene behaves as your virtual screen, this is how you should handle everything inside of it) Instead, what you should do is convert from screen to scene, this way regardless of scale mode or anchor point, your label is where you want it to be.
To get the top right corner of your view converted to your scene, you do
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
}
Now we know where our top right position is:
We can do
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
coinLabel.position = camTopRight
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
coinLabel.horizontalAlighmentMode = .right
coinLabel.verticalAlighmentMode = .top
cameraNode.addChild(coinLabel)
}
to right align our label so that the text always extends to the left, then to add the coin, we do:
if let view = scene.view
{
let topRightPos = view.convert(CGPoint(x:view.frame.maxX,y:view.frame.minY),to:scene)
let camTopRight = scene.convert(topRightPos,to:cameraNode)
coinLabel.position = camTopRight
coinLabel.zPosition = 1
coinLabel.fontSize = 50
coinLabel.fontColor = UIColor.black
coinLabel.fontName = "04b19"
coinLabel.horizontalAlighmentMode = .right
coinLabel.verticalAlighmentMode = .top
cameraNode.addChild(coinLabel)
cornerCoin.position = CGGPoint(x:0.0,y:coinLabel.frame.midY)
cornerCoin.anchorPoint = CGPoint(x:1.0,y:0.5)
cornerCoin.zPosition = 10
cameraNode.addChild(cornerCoin)
}
Our label should now be in the top right position of our camera, and the coin should be to the left of our label centered vertically with the text.
You may want to play with how your label is positioned if you do not like the look of top alignment.

Calculate width and height of AttributeText does not correct

I'm making an app chat and I have a problem with the chat message's size.
I've calculate my message's height like this and I've got the correct height
maximumMessageWidth = 300.0
let size = CGSize(width: maximumMessageWidth, height: 1000.0)
let messageAttributeText = messageBody.attributedText
let height = messageAttributeText?.boundingRect(with: size, options: .usesLineFragmentOrigin, context: nil).height
To avoid the special case like this image. I have to calculate the width
It has the big space after my message
I want it to look like this:
This is the code I use to calculate the message width (I used the correct height which I've calculated the width)
let size = CGSize(width: 1000.0, height: height)
let messageAttributeText = messageBody.attributedText
var width = messageAttributeText?.boundingRect(with: size, options: .usesLineFragmentOrigin, context: nil).width
but width in this code seem not right. Because I think it does care about my height property. It assume that my text is in only 1 line.
But I want to calculate the width, so that my text will `fill the whole label' just like the second image
Does anyone know how to calculate the width in my case ?
You can do that by casting String to NSString
var string = "Hello, World"
let nsString = string as NSString
let size = nsString.size(attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14)])
The size will give you width and height of the string.
You can apply this to your NSAttributedStrings string property.
After a bit of research, I find out this:
The calculate width code doesn't work right because it do not care about the height property.
If height = 100 or height = 1000 it will return the same result because it assume to draw all the text in just one line.
So to calculate the width, I used binary search
var minWidth:CGFloat = 0.0
var maxWidth = maxWidth
while true {
if (minWidth >= maxWidth) {
width = minWidth
break
}
let testWidth = (maxWidth + minWidth) / 2.0
if calculateMessageHeight(width: testWidth) > messageHeight {
minWidth = testWidth + 2.0
continue
} else {
maxWidth = testWidth - 2.0
continue
}

iOS : Adaptive Collection View cell width

I'm trying to make a grid view on iOS with adaptive column width.
On Android it's possible to do this by setting stretchMode attribute to spacingWidth .
This attribute take the cell width as minimum width and grow cell automatically if there is free space available but no enough to add another column keeping the same space between column everywhere.
I didn't found any way to do that on iOS.
It look like that (left image) on iPhone 6, but on iPhone 5 (right image) the space is very big and ugly. I wan't to auto resize cells to avoid this big space.
How can i do that on iOS ?
(I'm using Xcode 6.1)
Thanks
EDIT :
This i why i wan't (black space is approximatively desired additional cell width, sorry my draw is ugly I did it quickly)
EDIT 2:
I tried to calculate new size with this code, but the result is "strange" (wrong size, position) , i think i missed something
let CELL_SIZE : Float = 92
let CELL_MARGIN : Float = 10
let COLLECTION_VIEW_MARGIN : Float = 20 //Left right margin
let screenWidth = Float(UIScreen.mainScreen().bounds.width)
let numberOfCell = Float(Int(screenWidth / (CELL_SIZE + CELL_MARGIN + COLLECTION_VIEW_MARGIN)))
let oldCellSize = Float(cell.frame.width)
var newCellSize : Float
if(numberOfCell >= 2){
newCellSize = (screenWidth / numberOfCell) - (CELL_MARGIN * (numberOfCell-1))
} else {
newCellSize = (screenWidth / numberOfCell) }
let indexPathRow = Float(indexPath.row)
var xOffsetMultiplier = indexPathRow % numberOfCell
if(xOffsetMultiplier == 0){
xOffsetMultiplier = numberOfCell
}
var newX : Float = 0
if(xOffsetMultiplier == 1){
newX = COLLECTION_VIEW_MARGIN / 2
} else {
newX = (newCellSize + CELL_MARGIN) * (xOffsetMultiplier-1) + (COLLECTION_VIEW_MARGIN / 2)
}
var frame = CGRectMake(CGFloat(newX), cell.frame.minY, CGFloat(newCellSize), cell.frame.height)
cell.frame = frame
This code is written in func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell in my ViewController
You should use UICollectionViewFlowLayout to adopt cell size according to collection view size.

Resources