CALayer within UIButton - ios

I am building a menu based upon buttons. I am building them purely in code.
let menu00 = UIButton()
menu00.frame=menuHome
let imageSubLayer = CALayer()
imageSubLayer.contents = UIImage(named: "menuIcon.png")!.CGImage
menu00.layer.insertSublayer(drawHexFill(menu00.frame, fillColor: UIColor.lightGrayColor()), atIndex: 2)
menu00.layer.insertSublayer(drawHexBorder(menu00.frame, fillColor: UIColor.clearColor()), atIndex: 1)
menu00.layer.insertSublayer(imageSubLayer, atIndex: 0)
menu00.titleLabel!.text="MENU"
menu00.titleLabel!.textColor=UIColor.whiteColor()
menu00.tag=100
self.view.addSubview(menu00)
drawHexFill and drawHexBorder are two UIBezierPath methods to draw the graphical layers, which I do as the button may have different colour border and fill depending on the situation.
The problem I have is that the text and the image are not visible. If I add the image as the button image, it is visible, however behind the hex border and hex fill layers.
I have tried different positions "atIndex" for all objects but simply cannot get what I want. I would like hexFill in the background, hexBorder next and image or text on top.
Any suggestions?

The problem is possibly that your subLayers have different values of their .zPosition property. From
Adds a new sublayer object to the current layer. The sublayer is added
to the end of the layer’s list of sublayers. This causes the sublayer
to appear on top of any siblings with the same value in their
zPosition property.
From here. You could double-check that the .zPosition property has the same value for all your three sublayers.
If this is not the issue, try using insertSubLayer:above instead of insertSubLayer:atIndex (see documentation).

Related

change popover viewcontroller cornerRadius in swift

i am trying change cornerRadius of popUPViewController but not change
how to change that? at the same time i have another doubt also is it possible to change cornerRadius??
Your question seems too broad to help with a specific answer. I'm trying to help assuming what your needs are:
If you're referring to the new iOS 13 presentation style as "popUPViewController", you CANNOT change the corner radius of it
But, you can write your own custom transition adding your touches to it. This is a nice place to get started with.
And yes, you can easily change the corner radius of a view:
yourView.layer.cornerRadius = 10 //Whatever radius you'd want
yourView.layer.masksToBounds = true //This line is important. Doing this will restrain yourView's layer and sublayers to the cornered mask
You can additionally also specify corners to apply the radius with:
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] //This will round the top-left and top-right corners
try doing popupViewController.layer.cornerRadius = 10.0

Best way to add multiple diagonal connection lines between TableViewCells

I'm creating an app that needs to show a tableview like below image
Similar colored circles are to be matched with a line.
Which view i can add the lines?
Or need to create a new view above tableview? But still my tableview needs to be scrolled.
How can i achieve this?
Update for Bounty
I want to implement the same with incliend lines between neighbouring circles. How to achieve the same?
Demonstration below:
create design like this
Based on your requirement just hide upper line and lower line of circle
You need to create collection view in tableview cell. In collection view you create one cell. Design the same user interface like your design. Show and hide the view with matching of rule. It will not affect tableview scrolling. and with this approach you can also provide scroll in collection view cell. i can provide you coded solution if you able to provide me more information. Thanks
You can use this Third Party LIb
You need to use a combination of collection view and a table view to give support for all devices.
1.Create one collection view cell with following layout
Hide upper and lower lines as per your need
Add collection view in table view cell and managed a number of cells in collection view depending upon the current device width and item's in between spacing.
You can create a vertical label without text, set the background color with black and place it behind the circle in view hierarchy and set a width of the label as per your requirement. Then you can hide unhide the label whenever you want.
P.S.: Make sure to hide your cell separator.
I have created a demo project. You can find it here. I tried to match your requirements. You can update the collection view settings to handle the hide and show of labels.
Let me know if you have any questions.
Hope this help.
Thanks!
To connect any circle with any other circle in the cell above / below, it will be easier and cleaner to create the connection lines dynamically rather than building them into the asset as before. This part is simple. The question now is where to add them.
You could have the connection lines between every two cells be contained in the top or bottom cell of each pair, since views can show content beyond their bounds.
There's a problem with this though, regardless of which cell contains the lines. For example, if the top cell contains them, then as soon as it is scrolled up off screen, the lines will disappear when didEndDisplayingCell is called, even though the bottom cell is still completely on screen. And then scrolling slightly such that cellForRow is called, the lines will suddenly appear again.
If you want to avoid that problem, then here is one approach:
One Approach
Give your table view and cells a clear background color, and have another table view underneath to display a new cell which will contain the connection lines.
So you now have a background TVC, with a back cell, and a foreground TVC with a fore cell. You add these TVC's as children in a parent view controller (of which you can set whatever background color you like), disable user interaction on the background TVC, and peg the background TVC's content offset to the foreground TVC's content offset in an observation block, so they will stay in sync when scrolling. I've done this before; it works well. Use the same row height, and give the background TVC a top inset of half the row height.
We can make the connection lines in the back cell hug the top and bottom edges of the cell. This way circles will be connected at their centre.
Perhaps define a method in your model that calculates what connections there are, and returns them, making that a model concern.
extension Array where Element == MyModel {
/**
A connection is a (Int, Int).
(0, 0) means the 0th circle in element i is connected to the 0th circle in element j
For each pair of elements i, j, there is an array of such connections, called a mesh.
Returns n - 1 meshes.
*/
func getMeshes() -> [[(Int, Int)]] {
// Your code here
}
}
Then in your parent VC, do something like this:
class Parent_VC: UIViewController {
var observation: NSKeyValueObservation!
var b: Background_TVC!
override func viewDidLoad() {
super.viewDidLoad()
let b = Background_TVC(model.getMeshes())
let f = Foreground_TVC(model)
for each in [b, f] {
self.addChild(each)
each.view.frame = self.view.bounds
self.view.addSubview(each.view)
each.didMove(toParent: self)
}
let insets = UIEdgeInsets(top: b.tableView.rowHeight / 2, left: 0, bottom: 0, right: 0)
b.tableView.isUserInteractionEnabled = false
b.tableView.contentInset = insets
self.b = b
self.observation = f.tableView.observe(\.contentOffset, options: [.new]) { (_, change) in
let y = change.newValue!.y
self.b.tableView.contentOffset.y = y // + or - half the row height
}
}
}
Then of course there's your drawing code. You could make it a method of your back cell class (a custom cell), which will take in a mesh data structure and then draw the lines that represent it. Something like this:
class Back_Cell: UITableViewCell {
/**
Returns an image with all the connection lines drawn for the given mesh.
*/
func createMeshImage(for mesh: [(Int, Int)]) -> UIImage {
let canvasSize = self.contentView.bounds.size
// Create a new canvas
UIGraphicsBeginImageContextWithOptions(canvasSize, false, 0)
// Grab that canvas
let canvas = UIGraphicsGetCurrentContext()!
let spacing: CGFloat = 10.0 // whatever the spacing between your circles is
// Draw the lines
for each in mesh {
canvas.move(to: CGPoint(x: CGFloat(each.0) * spacing, y: 0))
canvas.addLine(to: CGPoint(x: CGFloat(each.1) * spacing, y: self.contentView.bounds.height))
}
canvas.setStrokeColor(UIColor.black.cgColor)
canvas.setLineWidth(3)
canvas.strokePath()
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
You'd probably want to create a Mesh class and store the images in that model, to avoid redrawing.

UIScrollView with Horizontal Zoom - Bezier Paths Drawn within Subviews Are Blurry

I have a scroll view that zooms only horizontally on pinch (based on this answer)
The content view has several children that are evenly spaced horizontally, and placed at different heights. Basically, I'm plotting a graph with dots.
Each marker is a custom UIView subclass that has a label as subview and draws a red circle inside drawRect(_:).
(The scroll view and its only child the content view use Autolayout, but all subviews of the content view are placed with frames calculated at runtime; no constraints except the label positioning respect to the marker)
I have modified the answer linked above, so that -when zooming- the dots get more spaced horizontally, but stay the same size.
This is part of the code for my content view:
override var transform: CGAffineTransform {
get {
return super.transform
}
set {
if let unzoomedViewHeight = unzoomedViewHeight {
// ^Initial height of the content view, captured
// on layoutSubviews() and used to calculate a
// a zoom transform that preserves the height
// (see linked answer for details)
// 1. Ignore vertical zooming for this view:
var modified = newValue
modified.d = 1.0
modified.ty = (1.0 - modified.a) * unzoomedViewHeight/2
super.transform = modified
// 2. Ignore zooming altogether in marker subviews:
var markerTransform = CGAffineTransformInvert(newValue)
markerTransform.d = 1.0 // No y-scale
for subview in subviews where subview is MarkerView {
subview.transform = markerTransform
}
}
}
}
Next, I want to connect my dots with straight line segments. The problem is, when I zoom the scroll view the segments become blurry.
In addition to the blurring, and because I am only zooming horizontally, the segments "shear" a bit (depending on their their slope), and thus the line width becomes uneven:
(Right now I am placing an intermediate view between each pair of dots, that draws a single segment - but the result is the same with a single path)
I have tried making the segment-drawing views aware of the transform that is applied to them by their parent, revert it, and instead modify their frame accordingly (and redraw the path within the new bounds); however it doesn't seem to work.
What is the best way to draw zoom-resistant bezier paths inside a scroll view?
Update:
I got rid of the pixelation by following this answer to a similar question (almost duplicate?); in my case the code translates to:
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat)
{
for segment in contentView.subviews where segment is SegmentView {
segment.layer.contentsScale = scale
segment.setNeedsDisplay()
}
}
...However, I still have the uneven line width issue due to stretching the (diagonal) segments only in the horizontal direction.
My initial thought, having not done this before exactly, is to store the graph as a bezier path rather than as a set of view line and point views, and based on a single unit axis. You could convert your data into this form just for drawing.
So, all points on the bezier path are normalised into the range 0 to 1.
Once you've done that you can apply a transform to the bezier path to translate (move) and scale (zoom) to the part you want and then draw the bezier path at full resolution.
This will work and is different to your current situation because your current code draws the views and then scales then so you get artifacts. The above option scales the path and then draws it.

How add background image to UIControl

I create 3rd party keyboard and i try add background to my keys. I use UIControl for my keys, without storyboard.
I have several types of buttons and try to add background UIView for my characters.
let img = UIImage(named:"KeyBackground")
var bgImage: UIImageView?
bgImage = UIImageView(image: img)
bgImage!.frame = keyboardKey.frame
keyboardKey.addSubview(bgImage!)
keyboardKey.sendSubviewToBack(bgImage!)
keyboardKey.backgroundColor = UIColor.clearColor()
But is don't work.
I try to add background image like this:
keyboardKey.backgroundColor = UIColor(patternImage: UIImage(named:"KeyBackground")!)
But is looks bad...
What am i doing wrong?
The background looks like that because the size of your image is a little smaller than the size of each key, and the image is being tiled (repeated to fill the space - so each key contains the background image and pieces of the surrounding tiles to the right, bottom, and bottom-right).
The UIColor(patternImage:) docs say that
During drawing, the image in the pattern color is tiled as necessary to cover the given area.
Your first solution probably didn't work because you inserted your UIImageView as the bottom-most subview - I assume another (opaque) view was on top of it. What type of UIControl are you using? UIButton, for example, already provides some "background image" support out of the box.
Oh, it's so easy. My keyboardKey.frame, at the time of the call this method, have frame = 0, 0, 0, 0.
Night a bad time for work:)

The text jumps when changing the height of a CATextLayer

When I change the height of my CATextLayer, a new text comes in from above (or below) as is depicted in the image below. How can I prevent this?
#IBAction func Tap(sender: UIButton) {
counter += 1
CATransaction.begin()
CATransaction.setAnimationDuration(8.0)
txtLay!.frame = frameFromCounter()
CATransaction.commit()
}
CATextLayer draws itself via drawInContext: method, so any change into the rendered representation (e.g. changing string property) will also modify the contents of the layer. In your case you're resizing the layer causing the backing store resize which changes contents which adds an implicit animation to that property.
If you don't want the animation to happen you can use the actions dictionary to disable the implicit contents animation:
txtLay!.actions = ["contents" : NSNull()]
However, disabling contents animation will cause a jump in that case, so you'll probably better of not changing the bounds of the CATextLayer and just embed it into a superlayer to provide any additional styling/layout you want.

Resources