I add an image to my view by the following code if the count is zero and remove it otherwise:
var coverImageView = UIImageView()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if count == 0 {
let coverImage = UIImage(named: "AddFirstRecord")!
coverImageView = UIImageView(image: coverImage)
coverImageView.frame = CGRect(x: 20, y: 5, width: tableView.frame.width-20, height: 100)
view.addSubview(coverImageView)
} else {
DispatchQueue.main.async {
self.coverImageView.removeFromSuperview()
}
}
}
The problem is that it adds the image to the view, but removeFromSuperview does not work. (I made sure that it reaches to the else condition by debugging). I did the process in the main queue as well to be sure that the problem does not relate to threads. I wonder where is the origin of the issue?
In viewWillAppear the view still is not prepared completely to view. So removingFromSuperview does not have any effects. Instead, we should do the action inside viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if fetchedResultsController.fetchedObjects?.count == 0 {
let coverImage = UIImage(named: "AddFirstRecord")!
coverImageView.image = coverImage
coverImageView.frame = CGRect(x: 20, y: 5, width: tableView.frame.width-20, height: 100)
view.addSubview(coverImageView)
} else {
coverImageView.removeFromSuperview()
}
}
From Apple Documentation :
viewDidLayoutSubviews()
Called to notify the view controller that its
view has just laid out its subviews.
Your view controller can override this method to make changes after
the view lays out its subviews. The default implementation of this
method does nothing.
Related
In the following code, I want to draw a progress bar that grows. what should I call at the line marked
// what goes here?
Assume the code is run on the main thread.
class View: UIView {
func updateProgressBar(){
var boxFrame = CGRect(x:10, y:10: width:100, height: 100)
for _ in 0...<10 {
let box = UIView(frame: boxFrame)
box.backgroundColor = .blue
addSubview(box)
// What goes here?
Thread.sleep(forTimeInterval: 2)
boxFrame.origin.x = boxFrame.width
}
}
}
What will be the answer from the following option and why?
setNeedsDisplay()
layer.draw(in: UIGraphicsGetCurrentConntext()!)
draw(bounds)
This approach is incorrect. It should be implemented in another way. such as with DispatchQueue.asyncAfter()
I just wanted to create a view and when it shown then the whole background will be dimmed like an alert view controller. If it is possible then please guide me and if possible then provide me code.
Thank you
The simplest way for doing that is to add a semi-transparent background (e.g. black with alpha less than 1.0) view, which contains the alert view. The background view should cover all other views in the view controller.
You can also use a modal view controller which has such a background view as its view, and presenting this controller with presentation style Over Full Screen.
// Here is the wrapper code i use in most of my project now a days
protocol TransparentBackgroundProtocol {
associatedtype ContainedView
var containedNib: ContainedView? { get set }
}
extension TransparentBackgroundProtocol where ContainedView: UIView {
func dismiss() {
containedNib?.superview?.removeFromSuperview()
containedNib?.removeFromSuperview()
}
mutating func add(withFrame frame: CGRect, toView view: UIView, backGroundViewAlpha: CGFloat) {
containedNib?.frame = frame
let backgroundView = configureABlackBackGroundView(alpha: backGroundViewAlpha)
view.addSubview(backgroundView)
guard let containedNib = containedNib else {
print("No ContainedNib")
return
}
backgroundView.addSubview(containedNib)
}
private func configureABlackBackGroundView(alpha: CGFloat) -> UIView {
let blackBackgroundView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
blackBackgroundView.backgroundColor = UIColor.black.withAlphaComponent(alpha)
return blackBackgroundView
}
}
// Sample View shown like alertView
class LogoutPopUpView: UIView, TransparentBackgroundProtocol {
// MARK: Variables
weak var containedNib: LogoutPopUpView?
typealias ContainedView = LogoutPopUpView
// MARK: Outlets
// MARK: Functions
class func initiate() -> LogoutPopUpView {
guard let nibView = Bundle.main.loadNibNamed("LogoutPopUpView", owner: self, options: nil)?[0] as? LogoutPopUpView else {
fatalError("Cann't able to load nib file.")
}
return nibView
}
}
// where u want to show pop Up
logOutPopup = LogoutPopUpView.instanciateFromNib()
let view = UIApplication.shared.keyWindow?.rootViewController?.view {
logOutPopup?.add(withFrame: CGRect(x: 30, y:(UIScreen.main.bounds.size.height-340)/2, width: UIScreen.main.bounds.size.width - 60, height: 300), toView: view, backGroundViewAlpha: 0.8)
}
// for dismiss
self.logOutPopup?.dismiss()
I got UINavigationController inside my app with a rootVC "VC1", VC1 containts a collectionview with 2images inside every cell. When user select cell, navigationcontroller will pass images from cell to new vc "VC2" and then push it to the top of navigationcontroller. My problem is when I take down VC2 via popviewcontroller, VC2 is deallocated correctly but memory stays at the same higher level (after pushing new vc it increases from 60mb to 130mb). I've trying set image to nil, and imageview also but none of this work. Here's some of my code:
class VC1: UIViewController {
var selectedUserPollDetails : VC2?
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! AppUserCell
selectedUserPollDetails = VC2()
selectedUserPollDetails?.leftPhoto = cell.leftImageNode.image
selectedUserPollDetails?.rightPhoto = cell.rightImageNode.image
navigationController?.pushViewController(selectedUserPollDetails !, animated: true)
}
}
class VC2: UIViewController {
lazy var arrow : ArrowBack = {
let arrow = ArrowBack()
return arrow
}()
weak var leftPhoto: UIImage?
weak var rightPhoto: UIImage?
var leftPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFill
imageview.layer.cornerRadius = 5
imageview.layer.masksToBounds = true
return imageview
}()
var rightPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFit
imageview.layer.cornerRadius = 5
imageview.clipsToBounds = true
return imageview
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(leftPhotoImageview)
view.addSubview(rightPhotoImageview)
view.addSubview(arrow)
arrow.addTarget(self, action: #selector(handleArrowBack), for: .touchUpInside)
}
func handleArrowBack(){
navigationController?.popViewController(animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
leftPhotoImageview.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
rightPhotoImageview.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
if leftPhoto != nil, rightPhoto != nil{
leftPhotoImageview.image = leftPhoto
rightPhotoImageview.image = rightPhoto
}
}
deinit{
leftPhoto = nil
rightPhoto = nil
leftPhotoImageview.image = nil
rightPhotoImageview.image = nil
}
I've even added deinit at the end to make sure photos are deallocated. So basically when i'm trying to push VC2 again (after pop) amount of memory is doubled again (260mb) and so on... what causing this problem? what am i doing wrong?
btw. i've omitted less important func and vars
You defo have a memory leak. I believe everytime
you push a new view via the nav controller it creates a brand new view i.e. the view is completely new and isn't reused. If you have strong references within the view you've pushed to they won't be released unless you deinit as they have a strong ref to view you've pushed so they linger around. You mentioned you deinit those items. Have you also tried marking leftPhotoImageView and rightPhotoImageView also as weak properties? Something is holding onto those images it would seem.
You could also change deinit to leftPhotoImageview = nil and rightPhotoImageView = nil rather than setting the imageview.image property to nil if that makes sense.
Ok, i think i found a solution, i don't know why i haven't trying this one, so i marked my imageview as lazy, and now memory is decreasing after popping vc
In my main UIViewController embed in UINavigationController I have add an UILabel to a navigationBar using that code:
if let navigationBar = self.navigationController?.navigationBar {
let frameDomanda = CGRect(x: navigationBar.frame.width/2 - domandaN.frame.width/2, y: -10, width: domandaN.frame.width, height: navigationBar.frame.height)
domandaN.frame = frameDomanda
let secondLabel = UILabel(frame: secondFrame)
secondLabel.text = "Second"
navigationBar.addSubview(domandaN)
}
But when I change Controller the UILabel is fixed. It doesn't disappear so I've added that code:
override func viewDidDisappear(animated: Bool) {
domandaN.removeFromSuperview()
}
It works but I want it to disappear immediately after the press of the back button. Not like this image:
(The "example" text goes away later)
Just add it in viewWillDisappear instead this:
override func viewWillDisappear(animated: Bool) {
domandaN.removeFromSuperview()
}
For the animation parameter:
If true, the disappearance of the view is being animated.
You can use viewWillDisappear, and don't forget to call super:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// you code here
}
There are several questions like this floating around, but no answer that works.
I'm adding new CALayers to a UIView like so:
func placeNewPicture() {
let newPic = CALayer()
newPic.contents = self.pictureDragging.contents
newPic.frame = CGRect(x: pictureScreenFrame.origin.x - pictureScreenFrame.width/2, y: pictureScreenFrame.origin.y - pictureScreenFrame.height/2, width: pictureScreenFrame.width, height: pictureScreenFrame.height)
self.drawingView.layer.addSublayer(newPic)
}
and trying to remove them with:
func deleteDrawing() {
for layer in self.drawingView.layer.sublayers {
layer.removeFromSuperlayer()
}
}
This successfully removes the images, but the app crashes the next time the screen is touched, with a call to main but nothing printed in the debugger. There are several situations like this out there, where apps will crash a short time after removing sublayers.
What is the correct way to remove CALayers from their parent View?
I think the error is you delete all sublayers,not the ones you added.
keep a property to save the sublayers you added
var layerArray = NSMutableArray()
Then try
func placeNewPicture() {
let newPic = CALayer()
newPic.contents = self.pictureDragging.contents
newPic.frame = CGRect(x: pictureScreenFrame.origin.x - pictureScreenFrame.width/2, y: pictureScreenFrame.origin.y - pictureScreenFrame.height/2, width: pictureScreenFrame.width, height: pictureScreenFrame.height)
layerArray.addObject(newPic)
self.drawingView.layer.addSublayer(newPic)
}
func deleteDrawing() {
for layer in self.drawingView.layer.sublayers {
if(layerArray.containsObject(layer)){
layer.removeFromSuperlayer()
layerArray.removeObject(layer)
}
}
}
Update with Leo Dabus suggest,you can also just set a name of layer.
newPic.name = "1234"
Then check
func deleteDrawing() {
for layer in self.drawingView.layer.sublayers {
if(layer.name == "1234"){
layerArray.removeObject(layer)
}
}
}