How to add gravity to UIViews in a UITableViewCell? - ios

I am trying to animate a few UIViews by having gravity act on them. Ideally, each view would stay inside the UITableViewCell bounds and they would bounce off of each other and the walls like boundaries. However, when I run this code, the UIView's just kind of float in place, only moving if another view happens to populate in the same spot.
class AnimateTableViewCell: UITableViewCell {
private var animator: UIDynamicAnimator!
private var gravity: UIGravityBehavior!
private var collision: UICollisionBehavior!
private var views = [UIView]()
override func awakeFromNib() {
super.awakeFromNib()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
animator = UIDynamicAnimator(referenceView: self.contentView)
let items = [UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill")]
for item in items {
let size = Int.random(in: 75 ... 180)
self.setUpView(view: UIView(frame: CGRect(x: 100, y: 100, width: size, height: size)), image: item!, size: size/2)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func applyGravity(to item: UIView) {
gravity = UIGravityBehavior(items: [item])
animator.addBehavior(gravity)
collision = UICollisionBehavior(items: views)
collision.translatesReferenceBoundsIntoBoundary = true
animator.addBehavior(collision)
let bounce = UIDynamicItemBehavior(items: views)
bounce.elasticity = 0.3
}
private func setUpView(view: UIView, image: UIImage, size: Int) {
contentView.addSubview(view)
view.backgroundColor = image.averageColor
view.layer.cornerRadius = view.frame.height/2
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.heightAnchor.constraint(equalToConstant: CGFloat(size)),
imageView.widthAnchor.constraint(equalToConstant: CGFloat(size)),
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
views.append(view)
applyGravity(to: view)
}
}

There are two main problems.
First, the collision behavior is getting in your way. Comment out this line:
animator.addBehavior(collision)
Now at least you will see the gravity operate.
Second, you are saying
gravity = UIGravityBehavior(items: [item])
animator.addBehavior(gravity)
multiple times, once per item. That's wrong. You must have just one gravity behavior that embraces all the items.
I got a fair-to-middling result just by rearranging some of your code and changing a few of the magic numbers; this should get you started, at least (I used a table view with just one cell, quite tall in height):
class AnimateTableViewCell: UITableViewCell {
private var animator: UIDynamicAnimator!
private var gravity = UIGravityBehavior()
private var collision = UICollisionBehavior()
private var views = [UIView]()
override func awakeFromNib() {
super.awakeFromNib()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
animator = UIDynamicAnimator(referenceView: self.contentView)
let items = [UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill")]
for item in items {
let size = Int.random(in: 20...40)
self.setUpView(view: UIView(frame: CGRect(x: 100, y: 100, width: size, height: size)), image: item!, size: size)
}
animator.addBehavior(gravity)
collision.translatesReferenceBoundsIntoBoundary = true
animator.addBehavior(collision)
let bounce = UIDynamicItemBehavior(items: views)
bounce.elasticity = 0.3
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func applyGravity(to item: UIView) {
gravity.addItem(item)
collision.addItem(item)
}
private func setUpView(view: UIView, image: UIImage, size: Int) {
contentView.addSubview(view)
view.backgroundColor = .red
view.layer.cornerRadius = view.frame.height/2
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.heightAnchor.constraint(equalToConstant: CGFloat(size)),
imageView.widthAnchor.constraint(equalToConstant: CGFloat(size)),
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
views.append(view)
applyGravity(to: view)
}
}

Related

Subviews of custom cell class not recognizing tap gesture

So I have a custom cell class sitting in my table view with stack views that contain UIImageViews. I want to add tap gesture recognition to the symbols, however the gesture isn't recognized for ONLY subviews of the custom cell (it works fine for the view itself). My cell code looks like:
class MonthlyViewCell: UITableViewCell {
private let someSymbol: UIImageView = {
guard let image = UIImage(systemName: "j.circle.fill") else {return nil}
let config = UIImage.SymbolConfiguration(pointSize: 27)
let newImage = image.applyingSymbolConfiguration(config)
let view = UIImageView(image: newImage)
view.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
view.contentMode = .scaleAspectFit
view.tag = 1
view.isUserInteractionEnabled = true
return view
}()
.
.
// other symbols
private var delegate: MonthlyViewCellDelegate?
private var stackViewOne = UIStackView()
private var stackViewTwo = UIStackView()
private var stackViewThree = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func commonInit() {
bounds = CGRect(x: 0, y: 0, width: 320, height: 120)
frame = bounds
print(frame)
configureSubViews()
addSubview(stackViewOne)
addSubview(stackViewTwo)
addSubview(stackViewThree)
//addTargetToSymbols()
activateConstraints()
stackViewOne.frame = CGRect(x: 0, y: 0, width: frame.width-1, height: 15)
stackViewTwo.frame = CGRect(x: 0, y: 0, width: frame.width-1, height: 15)
stackViewThree.frame = CGRect(x: 0, y: 0, width: frame.width-1, height: 15)
let gestureRecognizer = UITapGestureRecognizer()
gestureRecognizer.addTarget(self, action: #selector(testTapped(sender:)))
stackViewTwo.addGestureRecognizer(gestureRecognizer)
}
private func configureSubViews() {
stackViewOne.translatesAutoresizingMaskIntoConstraints = false
stackViewTwo.translatesAutoresizingMaskIntoConstraints = false
stackViewThree.translatesAutoresizingMaskIntoConstraints = false
stackViewOne.axis = .horizontal
stackViewTwo.axis = .horizontal
stackViewThree.axis = .horizontal
stackViewOne.spacing = 60
stackViewTwo.spacing = 60
stackViewThree.spacing = 60
let viewsOne = [januarySymbol, februarySymbol, marchSymbol, aprilSymbol]
let viewsTwo = [maySymbol, juneSymbol, julySymbol, augustSymbol]
let viewsThree = [septemberSymbol, octoberSymbol, novemberSymbol, decemberSymbol]
let stackViews = [stackViewOne,stackViewTwo,stackViewThree]
for one in viewsOne {
stackViewOne.addArrangedSubview(one!)
}
for two in viewsTwo {
stackViewTwo.addArrangedSubview(two!)
}
for three in viewsThree {
stackViewThree.addArrangedSubview(three!)
}
/*for view in stackViews {
bringSubviewToFront(view)
}*/
}
private func activateConstraints() {
let array: [NSLayoutConstraint] = [
stackViewOne.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
stackViewTwo.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
stackViewThree.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
stackViewOne.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 25),
stackViewTwo.topAnchor.constraint(equalTo: stackViewOne.bottomAnchor, constant: 25),
stackViewThree.topAnchor.constraint(equalTo: stackViewTwo.bottomAnchor, constant: 25)
]
NSLayoutConstraint.activate(array)
}
private func addTargetToSymbols() {
let gestureRecognizer = UITapGestureRecognizer()
let symbols: [UIImageView?] = [januarySymbol, februarySymbol, marchSymbol, aprilSymbol, maySymbol, juneSymbol, julySymbol, augustSymbol, septemberSymbol, novemberSymbol, decemberSymbol]
for symbol in symbols {
guard let symbol = symbol else {return}
gestureRecognizer.addTarget(self, action: #selector(monthSymbolTapped(sender:)))
symbol.addGestureRecognizer(gestureRecognizer)
bringSubviewToFront(symbol)
}
}
#objc func testTapped(sender: UITapGestureRecognizer) {
print("tapped")
}
func setDelegate(delegate: MonthlyViewCellDelegate) {
self.delegate = delegate
}
}
And the delegate methods are in the vc. I have tried setting the frames of each subview to be contained within the frame of their superviews. I should also mention that I set a breakpoint at the target methods to be called and none of them are triggered, so it seems that the methods aren't being called.
Also isUserInteractionEnabled is set to true for the view and its subviews. translatesAutoresizingMaskIntoConstraints is set to false for all subviews but NOT for the view itself, as this conflicts with the views auto constraints with the tableview it is contained in.
One possibility is it may have something to do with the constraints I set causing the stackviews respective frames to exceed the frame of its superview, however I set the stackviews's frames AFTER the constraints are activated, so this seems unlikely as well.
Finally I should mention that that the UIViewController has its view assigned to a .UITableView property which contains the cell which is initialized in cellForRowAt.
EDIT: As mentioned in the comments it is possible that using only one UITapGestureRecognizer instance in addTargetsToSymbols()for multiple subviews was the issue, so I made the following adjustment;
private func addTargetToSymbols() {
let symbols: [UIImageView?] = [januarySymbol, februarySymbol, marchSymbol, aprilSymbol, maySymbol, juneSymbol, julySymbol, augustSymbol, septemberSymbol, novemberSymbol, decemberSymbol]
for symbol in symbols {
guard let symbol = symbol else {return}
let gestureRecognizer = UITapGestureRecognizer()
gestureRecognizer.addTarget(self, action: #selector(monthSymbolTapped(sender:)))
symbol.addGestureRecognizer(gestureRecognizer)
bringSubviewToFront(symbol)
}
}
moving the instance into the for-in loop, so that a new unique instance is used for each loop. This did not work. Additionally I imagine my attempt to test this on stackViewTwo individually would've worked if it was true.
As requested in the comments; here is the code for monthSymbolTapped()
#objc func monthSymbolTapped(sender: UIGestureRecognizer) {
print("tst")
guard let tag = sender.view?.tag else {return}
switch tag {
case 1:
delegate?.pushToYearlyVisits(month: tag)
// other cases
default:
return
}
}
NOTE: addTargetsToSymbols() was commented out while trying to see if simply adding gesture recognition to one of the stack views would yield a different result. Otherwise addTargetsToSymbols() has not been commented out on my local machine when troubleshooting this problem.
Here is a version of your custom cell class that works just fine with each image view having its own tap gesture.
I cleaned up the code a lot. I created a main stack view that contains the 3 stack views you had with each of those containing 4 of the image views.
I eliminated most of the attempts to set frames since constraints are being used.
I think your main issue was having incorrect constraints for the stack views which resulted in most of the image views being outside of the frame's cell which prevented the gestures from working. You should also be adding subviews to the cell's contentView, not the cell itself.
I also changed how the image views are created so I could get a fully working demo since your code was not complete. Obviously you can use your own code for all of the symbols if you prefer.
With the updated code below, tapping on any of the 12 images views results in the "tapped" message being printed.
You should also set your table view's rowHeight property to UITableView.automaticDimension to ensure the correct row height.
class MonthlyViewCell: UITableViewCell {
private static func monthImage(_ letter: String) -> UIImageView {
let image = UIImage(systemName: "\(letter).circle.fill")!
let config = UIImage.SymbolConfiguration(pointSize: 27)
let newImage = image.applyingSymbolConfiguration(config)
let view = UIImageView(image: image)
view.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
view.contentMode = .scaleAspectFit
view.isUserInteractionEnabled = true
return view
}
private let months: [UIImageView] = ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"].map { MonthlyViewCell.monthImage($0) }
private var mainStack = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func commonInit() {
configureSubViews()
contentView.addSubview(mainStack)
addTargetToSymbols()
activateConstraints()
}
private func configureSubViews() {
mainStack.axis = .vertical
mainStack.distribution = .equalCentering
mainStack.alignment = .fill
mainStack.spacing = 40
mainStack.translatesAutoresizingMaskIntoConstraints = false
for i in stride(from: 0, to: 12, by: 4) {
let stack = UIStackView(arrangedSubviews: Array(months[i..<i+4]))
stack.axis = .horizontal
stack.distribution = .equalSpacing
stack.alignment = .top
stack.spacing = 60
mainStack.addArrangedSubview(stack)
}
}
private func activateConstraints() {
NSLayoutConstraint.activate([
mainStack.topAnchor.constraint(equalTo: contentView.topAnchor),
mainStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
mainStack.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 25),
mainStack.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -25),
])
}
private func addTargetToSymbols() {
for symbol in months {
let gestureRecognizer = UITapGestureRecognizer()
gestureRecognizer.addTarget(self, action: #selector(monthSymbolTapped(sender:)))
symbol.addGestureRecognizer(gestureRecognizer)
}
}
#objc func monthSymbolTapped(sender: UITapGestureRecognizer) {
if sender.state == .ended {
print("tapped")
}
}
}

How to set the corner radius to 50% [duplicate]

This question already has answers here:
Set UIView's corner radius to half of the view's width automatically
(3 answers)
Closed 11 months ago.
I have an image view inside a collection view cell. I would like to set the corner radius of the image to 50% of its width (so it's a circle). How can I do this?
Here's my code so far
//
// CategoryCell.swift
// UICollectionViewDemo
//
import UIKit
final class Category3Cell: UICollectionViewCell {
private enum Constants {
// MARK: contentView layout constants
static let contentViewCornerRadius: CGFloat = 0.0
// MARK: imageView layout constants
static let imageWidth: CGFloat = 90.0
static let imageHeight: CGFloat = 90.0
// MARK: Generic layout constants
static let verticalSpacing: CGFloat = 10.0
static let horizontalPadding: CGFloat = 16.0
static let nameImagePadding: CGFloat = 20.0
}
public var categoryKey : String = "";
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFit
imageView.layer.cornerRadius = 45
imageView.layer.masksToBounds = true
return imageView
}()
private let name: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
label.font = UIFont(name: "CeraPro-Regular", size: 17);
return label
}()
override init(frame: CGRect) {
super.init(frame: .zero)
setupViews()
setupLayouts()
}
private func setupViews() {
contentView.clipsToBounds = true
contentView.layer.cornerRadius = Constants.contentViewCornerRadius
contentView.backgroundColor = .clear
contentView.isUserInteractionEnabled = true
contentView.addSubview(imageView)
contentView.addSubview(name)
}
private func setupLayouts() {
imageView.translatesAutoresizingMaskIntoConstraints = false
name.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for `imageView`
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.heightAnchor.constraint(equalToConstant: Constants.imageWidth),
imageView.heightAnchor.constraint(equalToConstant: Constants.imageHeight)
])
// Layout constraints for `usernameLabel`
NSLayoutConstraint.activate([
name.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.horizontalPadding),
name.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.horizontalPadding),
name.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: Constants.nameImagePadding)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup(image: String, nameOf: String, key: String) {
imageView.image = UIImage.init(named: image)
name.text = nameOf
categoryKey = key
}
}
extension Category3Cell: ReusableView {
static var identifier: String {
return String(describing: self)
}
}
you need first the clipsToBounds set to true and then if you know the image size, you can set its layer.cornerRadius to half of that size.
Alternatively you can use the layoutSubviews method, and in its override access the imageView bounds.height and use half of this for the corner radius.
Try this code:
imageView.layer.cornerRadius = imageView.frame.height / 2
Set width and height at first, then set imageView.layer.cornerRadius = imageView.frame.height / 2.
class ViewController: UIViewController {
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFit
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imageView.frame = CGRect(x: 100, y: 100, width: 100, height: 100);
imageView.layer.cornerRadius = 45
imageView.layer.masksToBounds = true
self.view.addSubview(imageView)
imageView.image = UIImage.init(named: "+")
}
}

How to change color only a fraction of the total in swift?

I'm doing a QR code scan. I use UIView to change the background color of the screen to translucent color. However, I want to make the background color of the place that scans the QR code transparent. What should I do?
class QRcodeScannerViewController : UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var qrcodeView: UIView!
#IBOutlet weak var header: UINavigationBar!
#IBOutlet weak var flash: UIButton!
#IBOutlet weak var qrcodeScanArea: UIImageView!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
self.qrcodeView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
view.layer.insertSublayer(previewLayer, at: 0)
view.bringSubviewToFront(flash)
view.bringSubviewToFront(header)
header.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
header.isTranslucent = true
header.backgroundColor = UIColor.clear
header.shadowImage = UIImage()
Current My QRcode ScanView
Here's the view I want:
I don't know which part I reverse, but only the color I want is black and the rest is transparent.
view.addSubview(qrcodeView)
let shape = CGRect(x: 0, y: 0, width: 200, height: 200)
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: shape, cornerRadius: 0).cgPath
maskLayer.backgroundColor = UIColor.clear.cgColor
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
qrcodeView.layer.mask = maskLayer
I made and applied the class by referring to the answers below. But it doesn't work for me.
let focusview = FocusView.init(frame: qrcodeView.frame)
focusview.areaBackground = UIColor.black
view.addSubview(focusview)
I did something similar ... actually pretty much the same thing, some time ago.
I've dug out the code I used and have posted it here for your reference
fileprivate let focusSize: CGSize = CGSize(width: 218, height: 150)
fileprivate let focusCornerRadius: CGFloat = 10.0
class FocusView: UIView {
var areaBackground: UIColor? {
set {
maskedFocusView.backgroundColor = newValue
}
get {
return maskedFocusView.backgroundColor
}
}
fileprivate let maskedFocusView: MaskedFocusView = {
return MaskedFocusView()
}()
let focusView: UIView = {
let view = UIView()
view.layer.borderColor = UIColor.white.cgColor
view.layer.borderWidth = 2
view.layer.cornerRadius = focusCornerRadius
// view.layer.shadowColor = UIColor.white.cgColor
// view.layer.shadowRadius = 5.0
// view.layer.shadowOpacity = 0.9
// view.layer.shadowOffset = CGSize.zero
view.layer.masksToBounds = true
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
backgroundColor = UIColor.darkGray.withAlphaComponent(0.5)
translatesAutoresizingMaskIntoConstraints = false
addSubview(maskedFocusView)
addSubview(focusView)
setupFocusViewConstraints()
setupMaskedFocusViewConstraints()
}
func setupFocusViewConstraints() {
NSLayoutConstraint.activate(
focusView.centerXAnchor.constraint(equalTo: centerXAnchor),
focusView.centerYAnchor.constraint(equalTo: centerYAnchor)
)
let regularFocusViewConstraints = [
focusView.widthAnchor.constraint(equalToConstant: focusSize.width),
focusView.heightAnchor.constraint(equalToConstant: focusSize.height)
]
NSLayoutConstraint.activate(regularFocusViewConstraints)
}
func setupMaskedFocusViewConstraints() {
NSLayoutConstraint.activate(
maskedFocusView.centerXAnchor.constraint(equalTo: centerXAnchor),
maskedFocusView.centerYAnchor.constraint(equalTo: centerYAnchor),
maskedFocusView.topAnchor.constraint(equalTo: topAnchor),
maskedFocusView.bottomAnchor.constraint(equalTo: bottomAnchor),
maskedFocusView.leadingAnchor.constraint(equalTo: leadingAnchor),
maskedFocusView.trailingAnchor.constraint(equalTo: trailingAnchor)
)
}
}
// MARK: - Masked focus view
fileprivate class MaskedFocusView: UIView {
let maskLayer: CAShapeLayer = CAShapeLayer()
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
backgroundColor = UIColor.darkGray.withAlphaComponent(0.5)
maskLayer.backgroundColor = UIColor.clear.cgColor
layer.mask = maskLayer
translatesAutoresizingMaskIntoConstraints = false
}
override func layoutSubviews() {
super.layoutSubviews()
let width = bounds.width
let height = bounds.height
let x = (width - focusSize.width) / 2
let y = (height - focusSize.height) / 2
let focusRect = CGRect(x: x, y: y, width: focusSize.width, height: focusSize.height)
let fullRect = CGRect(origin: bounds.origin, size: bounds.size)
let path = CGMutablePath()
path.addPath(UIBezierPath(rect: fullRect).cgPath)
path.addPath(UIBezierPath(roundedRect: focusRect, cornerRadius: focusCornerRadius).reversing().cgPath)
maskLayer.path = path
maskLayer.fillRule = .evenOdd
}
}
// MARK: - Layout constraints extension
extension NSLayoutConstraint {
/// A helper function to activate layout constraints.
static func activate(_ constraints: NSLayoutConstraint? ...) {
for case let constraint in constraints {
guard let constraint = constraint else {
continue
}
(constraint.firstItem as? UIView)?.translatesAutoresizingMaskIntoConstraints = false
constraint.isActive = true
}
}
}
extension Array where Element: NSLayoutConstraint {
func activate() {
forEach {
if !$0.isActive {
$0.isActive = true
}
}
}
func deactivate() {
forEach {
if $0.isActive {
$0.isActive = false
}
}
}
}

View with multiple layers dont relese memory

I create custom View:
final class Clock: UIView {
lazy var hourArrow: CALayer = {
let layer = CALayer()
layer.contents = #imageLiteral(resourceName: "hourArrow").cgImage
layer.contentsGravity = kCAGravityResizeAspect
return layer
}()
lazy var subLayers = [hourArrow,minuteArrow,centerDial,clockDial,clockArrow,block]
override init(frame: CGRect) {
super.init(frame: frame)
subLayers.forEach { (l) in
layer.addSublayer(l)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
subLayers.forEach({ l in
let rect = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
l.frame = rect
})
}
}
and i use it ViewController:
class ClockViewController: UIViewController {
lazy var clock: Clock = {
let clock = Clock(frame: .zero)
clock.translatesAutoresizingMaskIntoConstraints = false
return clock
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(clock)
let safeArea = view.safeAreaLayoutGuide
clock.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 40).isActive = true
clock.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 40).isActive = true
clock.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -40).isActive = true
clock.heightAnchor.constraint(equalToConstant: 100).isActive = true
// Do any additional setup after loading the view.
}
}
then i move to this view controller, memory up to 10 MB with this layers.
When i dismiss VC, memory don't release but deinit was called.
(I must remove CALayer variables to post this, all of layers are create like first)
Any Ideas?

Adding Custom UIView to TableViewCell

I have been unsuccessful in adding my custom UIView to my TableViewCell. This custom view represents a custom check mark for each table view cell. I am doing my project without a storyboard and I'm not sure how to add this custom UIView to my UITableViewCell programmatically. Here's my code:
class CheckBoxView: UIView {
var isChecked: Bool
var checkBoxImageView: UIImageView
var checkBoxChanged :() -> () = { }
required init?(coder aDecoder: NSCoder) {
self.isChecked = false
self.checkBoxImageView = UIImageView(image: nil)
super.init(coder: aDecoder)
setup()
}
func setup() {
self.layer.borderWidth = 1.0
self.isUserInteractionEnabled = true
self.checkBoxImageView.frame = CGRect(x: 2, y: 2, width: 25, height: 25)
self.addSubview(self.checkBoxImageView)
let selector: Selector = "checkBoxTapped"
let tapRecognizer = UITapGestureRecognizer(target: self, action: selector)
self.addGestureRecognizer(tapRecognizer)
}
func checkBoxTapped() {
self.checkBoxChanged()
}
func markAsChecked() {
self.checkBoxImageView.image = UIImage(named: "new_message_icon")
}
func markAsUnChecked() {
self.checkBoxImageView.image = nil
}
}
Here's my TableViewCell:
class AddContactsCell: UITableViewCell {
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
let checkBox: CheckBoxView = {
let view = UIView() as! CheckBoxView
view.backgroundColor = UIColor.blue
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 16
view.layer.masksToBounds = true
return view
}()
override func layoutSubviews() {
super.layoutSubviews()
textLabel?.frame = CGRect(x: 100, y: textLabel!.frame.origin.y - 2, width: textLabel!.frame.width, height: textLabel!.frame.height)
detailTextLabel?.frame = CGRect(x: 101, y: detailTextLabel!.frame.origin.y + 1, width: detailTextLabel!.frame.width, height: detailTextLabel!.frame.height)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(profileImageView)
self.addSubview(checkBox)
//ios 9 constraint anchors
//need x,y,width,height anchors
profileImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 50).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
checkBox.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
checkBox.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
checkBox.widthAnchor.constraint(equalToConstant: 30).isActive = true
checkBox.heightAnchor.constraint(equalToConstant: 30).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I would really appreciate if you could tell me where I'm going wrong with adding the UIView.
you should instanciate your CheckBoxView and not cast a normal UIView to your CheckBoxView
change this line:
let view = UIView() as! CheckBoxView
to this line
let view = CheckBoxView()

Resources