Makpit Memory Leak - ios

I tried a lot of solutions but even in actual device, memory consumption is very high. When secondview is dismissed first time, memory doesnot decrease like its increases. The memory usage after dismissing secondview depends on how much area user discovered in map.
Here is my code.
First view controller
import UIKit
import CoreLocation
class ViewController: UIViewController {
let manager = CLLocationManager()
private let currentColor = UIColor(red: 234/255.0, green: 128/255.0, blue: 16/255.0, alpha: 1.0)
override func viewDidLoad(){
super.viewDidLoad()
manager.requestWhenInUseAuthorization()
let button = buttonLoader(y: 7*self.view.frame.height/16, text: "trigger")
self.view.addSubview(button)
}
private func buttonLoader(y:CGFloat,text:String)->UIButton{
let Label = UIButton(frame: CGRect(x: 0, y: y , width: self.view.frame.width, height: self.view.frame.height/8))
Label.setTitle(text, for: .normal)
Label.addTarget(self, action: #selector(mapOpener), for: .touchUpInside)
Label.setTitleColor(currentColor, for: .normal)
self.view.addSubview(Label)
return Label
}
#objc func mapOpener(){
let newView = SecondViewController()
newView.view.backgroundColor = .white
self.present(newView, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
SecondViewController
import UIKit
import MapKit
class SecondViewController: UIViewController {
var map :MKMapView!
var saverButton : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
saveButtonLoader()
mapLoader()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapLoader(){
map = MKMapView(frame: CGRect(x: 0, y: 64, width: view.frame.size.width, height: self.view.frame.size.height-64))
map.mapType = MKMapType.standard
map.isZoomEnabled = true
map.isScrollEnabled = true
map.showsUserLocation = true
map.userTrackingMode = .follow
view.addSubview(map)
}
func saveButtonLoader() {
saverButton = UIButton(frame: CGRect(x: 4*view.frame.size.width/5, y: 20, width: view.frame.size.width/5, height: 44))
saverButton.setTitle("Finish", for: .normal)
saverButton.addTarget(self, action: #selector(finishButtonAction), for: UIControlEvents.touchUpInside)
saverButton.backgroundColor = UIColor.blue
saverButton.isEnabled = true
self.view.addSubview(saverButton)
}
override func viewWillDisappear(_ animated:Bool){
super.viewWillDisappear(animated)
self.applyMapViewMemoryFix()
}
func applyMapViewMemoryFix(){
map.delegate = nil
removeOldAnnotations()
map.removeFromSuperview()
map = nil
}
func removeOldAnnotations(){
for annotation in map.annotations{
map.removeAnnotation(annotation)
}
}
#objc func finishButtonAction(sender:UIButton!) {
self.dismiss(animated: true, completion: {
})
}
}

Related

How to reload the data from UI picker view and close the Picker view with transition

class MainVC: UIViewController {
var datasourceForUsers :UserPicker!
var userPicker :UIPickerView!
var toolBar: UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
userPicker = UIPickerView()
toolBar = UIToolbar()
datasourceForUsers = UserPicker()
}
#IBAction func onMoreTapped(){
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
#IBAction func showRegisteredUsers(_ sender: Any) {
createUserPicker()
configureToolBar(toolBar)
}
func createUserPicker() {
userPicker.dataSource = datasourceForUsers
userPicker.delegate = datasourceForUsers
//Customizations
userPicker.backgroundColor = .black
userPicker.frame = CGRect.init(x: 0.0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 300)
self.view.addSubview(userPicker)
}
func configureToolBar(_ toolBar:UIToolbar ){
toolBar.sizeToFit()
toolBar.barTintColor = .black
toolBar.tintColor = .white
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(MainVC.dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.frame = CGRect.init(x: 0.0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 50)
self.view.addSubview(toolBar)
}
override func viewWillAppear(_ animated: Bool) {
userPicker.reloadAllComponents()
toolBar.reloadInputViews()
}
#objc func cancelDatePicker(){
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
self.resignFirstResponder()
}
}
I used above code to show some data on UIPickerView .It works fine.But When I change the datasource(Add new Data) where the data is stored it does not reflect on the UI picker.In order to do that I have to run the application again.Second thing is I couldn't close the UI picker with transition.

common Navigation Button Action another class

I have made common Navigation bar For all View Controllers. But I need to button action on that which calls I am calling the common navigation bar
#objc extension UIViewController {
#objc func setBarButtonItem(titleLabel: String)
{
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 200 + 4, height: 38))
let Nextbtn = UIButton(type: .custom)
Nextbtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
Nextbtn.addTarget(self, action: #selector(NextButtonClicked), for: .touchUpInside)
view.addSubview(Nextbtn)
self.navigationItem.setLeftBarButton(UIBarButtonItem(customView: view), animated: true)
/*
#objc func NextButtonClicked()
{
}*/
}
}
Calling Controller---> setBackBarButton("hello...")
Button is called if I made button action method on this class. But I want to make button action method func NextButtonClicked() on this calling Calling Controller class,Or Any approach we can access the button action on this class.
#objc func NextButtonClicked()
{
}
You can just Create BaseViewController Instead extension and allow all ViewControllers to inherit From this BaseViewController
BaseViewController:
import UIKit
class BaseViewController: UIViewController {
typealias CompletionBarButtonClicked = (()-> Void)
var completionBarItemClicked:CompletionBarButtonClicked?
override func viewDidLoad() {
super.viewDidLoad()
}
#objc func setBarButtonItem(titleLabel: String, completion :#escaping CompletionBarButtonClicked)
{
self.completionBarItemClicked = completion
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 200 + 4, height: 38))
let Nextbtn = UIButton(type: .custom)
Nextbtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
Nextbtn.addTarget(self, action: #selector(NextButtonClicked), for: .touchUpInside)
view.addSubview(Nextbtn)
self.navigationItem.setLeftBarButton(UIBarButtonItem(customView: view), animated: true)
}
#objc func NextButtonClicked()
{
if let completionHandler = completionBarItemClicked{
completionHandler()
}
}
}
Example ViewController:
import UIKit
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setBarButtonItem(titleLabel: "test") {
/// your action There
}
}
}

iOS 11 - Unable to change Navigation Bar height

I am working on an application and I just upgraded to Xcode 9 / Swift 4 and also upgraded my iPhone to iOS 11.
The application was installed when I installed iOS 11 and all seemed OK until I run it from Xcode. Now I am stuck with the default NavBar height.
The code I was using to change the height is no longer working:
class CustomNavControllerVC: UINavigationController
{
let navBarHeight : CGFloat = 64.0
let navbarBackButtonColor = UIColor(red: 247/255, green: 179/255, blue: 20/255, alpha: 1)
override func viewDidLoad()
{
super.viewDidLoad()
print("CustomNavControllerVC > viewDidLoad")
}
override func viewDidLayoutSubviews()
{
print("CustomNavControllerVC > viewDidLayoutSubviews")
super.viewDidLayoutSubviews()
navigationBar.frame.size.height = navBarHeight
navigationBar.tintColor = navbarBackButtonColor
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// In my VCs
override func viewDidLoad()
{
customizeNavBar()
}
func customizeNavBar()
{
let navbarBackItem = UIBarButtonItem()
navbarBackItem.title = "Înapoi"
navigationItem.backBarButtonItem = navbarBackItem
let navbarImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 55, height: 20))
navbarImageView.contentMode = .scaleToFill
let navbarLogo = UIImage(named: "NavBarLogo.png")
navbarImageView.image = navbarLogo
navigationItem.titleView = navbarImageView
}
So far the only thing I could find on this issue is this:
iOS 11 navigation bar height customizing
iOS11 customize navigation bar height
How to correctly set UINavigationBar height in iOS 11
But the info provided does not help, unfortunately.
Any ideas / suggestions?
Updated 2017.10.6
I had the same problem. Below is my solution. I assume that height size is 66.
My solution is working fine iOS 10, 11.
Please choose my answer if it helps you.
Create NavgationBar.swift
import UIKit
class NavigationBar: UINavigationBar {
//set NavigationBar's height
var customHeight : CGFloat = 66
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: customHeight)
}
override func layoutSubviews() {
super.layoutSubviews()
frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: customHeight)
// title position (statusbar height / 2)
setTitleVerticalPositionAdjustment(-10, for: UIBarMetrics.default)
for subview in self.subviews {
var stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarBackground") {
subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: customHeight)
subview.backgroundColor = .yellow
}
stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarContent") {
subview.frame = CGRect(x: subview.frame.origin.x, y: 20, width: subview.frame.width, height: customHeight - 20)
subview.backgroundColor = UIColor(red: 20/255, green: 20/255, blue: 20/255, alpha: 0.4)
}
}
}
}
Set Storyboard
Set Custom NavigationBar class
Add TestView + Set SafeArea
ViewController.swift
import UIKit
class ViewController: UIViewController {
var navbar : UINavigationBar!
#IBOutlet weak var testView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
//update NavigationBar's frame
self.navigationController?.navigationBar.sizeToFit()
print("NavigationBar Frame : \(String(describing: self.navigationController!.navigationBar.frame))")
}
//Hide Statusbar
override var prefersStatusBarHidden: Bool {
return true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(false)
//Important!
if #available(iOS 11.0, *) {
//Default NavigationBar Height is 44. Custom NavigationBar Height is 66. So We should set additionalSafeAreaInsets to 66-44 = 22
self.additionalSafeAreaInsets.top = 22
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Create BackButton
var backButton: UIBarButtonItem!
let backImage = imageFromText("Back", font: UIFont.systemFont(ofSize: 16), maxWidth: 1000, color:UIColor.white)
backButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(SecondViewController.back(_:)))
self.navigationItem.leftBarButtonItem = backButton
self.navigationItem.leftBarButtonItem?.setBackgroundVerticalPositionAdjustment(-10, for: UIBarMetrics.default)
}
override var prefersStatusBarHidden: Bool {
return true
}
#objc func back(_ sender: UITabBarItem){
self.navigationController?.popViewController(animated: true)
}
//Helper Function : Get String CGSize
func sizeOfAttributeString(_ str: NSAttributedString, maxWidth: CGFloat) -> CGSize {
let size = str.boundingRect(with: CGSize(width: maxWidth, height: 1000), options:(NSStringDrawingOptions.usesLineFragmentOrigin), context:nil).size
return size
}
//Helper Function : Convert String to UIImage
func imageFromText(_ text:NSString, font:UIFont, maxWidth:CGFloat, color:UIColor) -> UIImage
{
let paragraph = NSMutableParagraphStyle()
paragraph.lineBreakMode = NSLineBreakMode.byWordWrapping
paragraph.alignment = .center // potentially this can be an input param too, but i guess in most use cases we want center align
let attributedString = NSAttributedString(string: text as String, attributes: [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: color, NSAttributedStringKey.paragraphStyle:paragraph])
let size = sizeOfAttributeString(attributedString, maxWidth: maxWidth)
UIGraphicsBeginImageContextWithOptions(size, false , 0.0)
attributedString.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Yellow is barbackgroundView. Black opacity is BarContentView.
And I removed BarContentView's backgroundColor.
That's It.

UIButton not working on a subview of UIWindow

I'm trying to create a custom one-button AlertViewController in Swift, I use window.addSubview() to show the AlertView, but when touch the button on the AlertView, the func buttonTapped() is not working, below is my code, please tell me what's wrong here, thanks.
MyAlertViewController.swift
class MyAlertViewController: UIViewController {
var button: UIButton!
var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp(){
contentView = UIView(frame: CGRectMake(0,0,200,300))
contentView.center = view.center
contentView.backgroundColor = UIColor.whiteColor()
contentView.layer.cornerRadius = 10
button = UIButton(type: .System)
button.frame = CGRectMake(50, 150, 100, 40)
button.setTitle("button", forState: UIControlState.Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(contentView)
contentView.addSubview(button)
view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1)
view.userInteractionEnabled = true
}
func buttonTapped(sender: AnyObject?){
print("button tapped")
}
func show(){
let window = UIApplication.sharedApplication().keyWindow! as UIWindow
view.frame = window.bounds
window.addSubview(view)
}
}
ParentViewController.swift (is the rootViewController of my window)
class ParentViewController: UIViewController {
var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
button = UIButton(type: .System)
button.frame = CGRectMake(110,269,100,30)
button.setTitle("show", forState: .Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: .TouchUpInside)
view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonTapped(sender: AnyObject?){
MyAlertViewController().show()
}
}
I found out if I change ParentViewController.swift as below, the button on the alert view can work correctly.
class ParentViewController: UIViewController {
var button: UIButton!
var vc: MyAlertViewController!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.cyanColor()
button = UIButton(type: .System)
button.frame = CGRectMake(110,269,100,30)
button.setTitle("show", forState: .Normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), forControlEvents: .TouchUpInside)
view.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonTapped(sender: AnyObject?){
vc = MyAlertViewController()
vc.show()
}
}
But I intended to show this alert view each time the user get new messages,so it would show up at any time and on any ViewController , so I don't want to declare it in each ViewController, so How can I solve this?
TLDR;
You have to retain an instance of MyAlertViewController().
Change to this and it will work (as you've already done):
// keep some reference
var alert: MyAlertViewController?
func buttonTapped(sender: AnyObject?){
alert = MyAlertViewController()
alert?.show()
}
More explanation
The button.addTarget(self, ...) that is called inside MyAlertViewController does not retain self.
The last line of the doc of addTarget function said that:
// ... the action cannot be NULL. Note that the target is not retained.*
So there will be no self to send action to after leaving of this function:
func buttonTapped(sender: AnyObject?){
MyAlertViewController().show()
}
Another option,
is to keep self variable in MyAlertViewController:
// retain self manually
var mySelf: MyAlertViewController?
func setUp(){
...
// reference to self
mySelf = self
button.addTarget(mySelf, action: #selector(buttonTapped(_:)), forControlEvents: UIControlEvents.TouchUpInside)
}

How to make a function in swift that addresses all UIButtons that will ever be on the ViewController

I have a longer code that spawns UIButtons onto the UIViewController every certain amount of time. But now what I am trying to do is turn their backgroundColor from a random color to UIColor.whiteColor(). Hw could I make a function that will do this to the UIButton that is clicked on but will work for every single UIButton I "spawn" in. Or is it easier to use UIViews opposed to UIButtons and take a different path? Thank you.
import UIKit
func randomPoint() -> CGPoint {
let randomPoint: CGPoint = CGPoint(x:CGFloat(arc4random()%320),y: CGFloat(arc4random()%528))
return randomPoint
}
class ViewController: UIViewController {
#IBOutlet weak var startGameButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class ViewTwo : UIViewController {
var timer = NSTimer()
func randomColor() -> UIColor {
let red = CGFloat(drand48())
let green = CGFloat(drand48())
let blue = CGFloat(drand48())
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
func spawnEnemy() {
let enemy: UIButton = UIButton(frame: CGRect(x: 320, y: 320, width: 25, height: 25))
enemy.backgroundColor = randomColor()
enemy.center = randomPoint()
enemy.addTarget(self, action: Selector("buttonPressed"), forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(enemy)
}
// or my buttonPressed func goes but either way the app crashes when i click on one of the UIButtons
override func viewDidLoad() {
super.viewDidLoad()
func buttonPressed(sender: UIButton) {
sender.backgroundColor = UIColor.whiteColor()
}
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
When you're creating your buttons to spawn them you can add a target like this:
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
And then add the corresponding function to change the background color to white like this:
func buttonClicked(sender: UIButton) {
sender.backgroundColor = UIColor.whiteColor()
}
This will work for all buttons you spawn.

Resources