My code below is causing a run time error. This works If I code a lazy var as a scrollview. But this does not work if I am just trying to add a object to the view or superview. I have connected nothing from the storyboard and do not want to.
var FIRE: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
playSound()
view.addSubview(FIRE)
FIRE.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
FIRE.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
FIRE.widthAnchor.constraint(equalToConstant: 400).isActive = true
FIRE.heightAnchor.constraint(equalToConstant: 80).isActive = true
}
Change
var FIRE: UIImageView!
To
let FIRE = UIImageView()
Related
I'm trying to find out why am I getting the following message in Xcode:
Instance will be immediately deallocated because property 'delegate' is 'weak'
Here's a code:
import ARKit
class OtherViewController: NSObject, ARSCNViewDelegate { }
class ViewController: UIViewController, ARSCNViewDelegate {
var arView = ARSCNView(frame: .zero)
let otherVC = OtherViewController()
override func viewDidLoad() {
super.viewDidLoad()
// I'M USING ONE OF THESE
arView.delegate = self // works fine
arView.delegate = otherVC // works fine
arView.delegate = ViewController() // PRINTS WARNING
}
}
Question:
I understand that the delegate is a weak and optional:
weak var delegate: ARSCNViewDelegate? { get set }
However, why arView.delegate = self or arView.delegate = otherVC works FINE but arView.delegate = ViewController() prints a warning?
By setting arView.delegate = ViewController() you’re creating a new instance of ViewController that isn’t retained anywhere so it’s deallocated as soon as the function returns.
I am using the jonkykong SideMenu in a Swift 5 xcode project. The following code worked fine before 6.4.8.
import UIKit
import SideMenu
class SideMenuViewController: UITableViewController {
#IBOutlet weak var headerView: UIView!
#IBOutlet weak var driverNameLabel: UILabel!
#IBOutlet weak var vehicleRegoLabel: UILabel!
var menuItems = [Dictionary<String, String>]()
override func viewDidLoad() {
super.viewDidLoad()
// Config options for the Slide in side menu
SideMenuManager.default.leftMenuNavigationController?.presentationStyle = .menuSlideIn
SideMenuManager.default.leftMenuNavigationController?.presentationStyle.onTopShadowOpacity = 1
// hides the 1px bottom border on the nav bar so the header seemless merges to the navbar
self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
... rest of controller code
After upgrading to 6.4.8 the presentationStyle has no effect. The presentationStyle is always the default of viewSlideOut regardless of what I set in the above code.
I am unable to find anything in the README about changes to this in the latest version. Any assistance would be greatly appreciated.
Please set presentationStyle in SideMenuSettings and assign it to SideMenuNavigationController.
func makeSettings() -> SideMenuSettings{
var settings = SideMenuSettings()
settings.allowPushOfSameClassTwice = false
settings.presentationStyle = .menuSlideIn
settings.statusBarEndAlpha = 0
return settings
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let sideMenuNavigationController = segue.destination as? SideMenuNavigationController else { return }
sideMenuNavigationController.settings = makeSettings()
}
I am using a UISearchController search bar as the title view of my navigation bar in a modal view controller. I have it set up like this:
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.showsCancelButton = false
navigationItem.titleView = searchController.searchBar
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// for whatever reason, it's necessary to make the search controller first responder
// on the main queue. reference: https://stackoverflow.com/a/41657181/2335677
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
Everything is working great, except dismiss() and swipe-to-dismiss the modal aren't working when searchController.isActive = true
I can get around the dismiss() issue by setting it to inactive first:
#IBAction private func done(_ sender: UIBarButtonItem) {
searchController.isActive = false
dismiss(animated: true)
}
But I can't swipe down to dismiss the view controller as I mentioned. And I can't think of a workaround. I tried:
Setting isModalInPresentation = false (not an option because my app target is iOS12)
Playing with UIAdaptivePresentationControllerDelegate methods and trying to set searchController.isActive = false in some of those (didn't work)
Setting definesPresentationContext = true (doesn't make a difference)
Does anyone have any other ideas?
If you want a separate screen just for searching and you just need a search bar in your navigation bar title like I did, it's easier to use a UISearchBar instead:
var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchBar = UISearchBar()
searchBar.showsCancelButton = false
navigationItem.titleView = searchBar
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchBar.becomeFirstResponder()
}
It looks the same and you get all the default behavior now since UISearchController isn't hijacking your view controller.
Coordinator pattern is an old topic with many libraries trying to solve it and I am learning it in simple example app.
My current set up is 3 rootViewControlers: LoadingStateCoordinator, WelcomeCoordinator, TabBarCoordinator but missing connection between UIKit and coordinators. I am trying to implement it with a UINavigationController but the button is not being called. I need a way to connect to back button and a reusable coordinator that I could push to and dellocate accordingly (that is without RxSwift).*Set up Welcome screen as the parent/main navigation and always be able to come back to it.**
So after user selects a form from modal view (vertical flow) presented I show on a push a TabBarCoordinator (horizontal). All viewControllers have empty.storyboard, UIViewController and Coordinator exept the TabBar.Here I only have a coordinator due to the set up of child tab coordinators and the magic needs to happen on a back button tap. Currenly this only being called when user comes from LoadingStateCoordinator. There I need to send the user back to the Welcome screen so they can change the onboarding set up. Here is the first code for LoadingStateCoordinator:
final class LoadingStateCoordinator: NSObject, Coordinator {
*// MARK: - Inputs required*
var childCoordinators: [Coordinator]
var presenter: UINavigationController
private let window: UIWindow
*// MARK: - Initialization*
init(window: UIWindow) {
self.window = window
childCoordinators = []
presenter = UINavigationController()
}
*// MARK: - Coordinator*
func start() {
let controller: LoadingStateViewController = LoadingStateViewController.instantiate()
window.rootViewController = controller
controller.delegate = self
}
}
*// MARK: - LoadingViewControllerDelegate*
extension LoadingStateCoordinator : LoadingViewControllerDelegate {
func performScreenSwitch() {
if UserDefaults.standard.userWasHere == false {
let tabCoordinator: TabBarCoordinator = TabBarCoordinator(window: window, tabBarController: UITabBarController())
window.rootViewController = presenter
addChildCoordinator(tabCoordinator)
tabCoordinator.start()
presenter.pushViewController(tabCoordinator.tabBarController!, animated: true)
} else {
let welcomeCoordinator = WelcomeCoordinator(window: window, presenter: presenter)
window.rootViewController = welcomeCoordinator.presenter
addChildCoordinator(welcomeCoordinator)
welcomeCoordinator.start()
}
}
}
And here is the TabBarCoordinator that need to perform back to Welcome screen action. When I present popToRootfunction it pushes the Welcome screen but all the button there are disbled. I guess to be retain cycle issue. Do I need funadametally another set up? Is there a way to popToRoot(vc) in this set up? What I tryed ended with runtime error "poping to non existing controller".
TabBarCoordinator code that need to perform this:
final class TabBarCoordinator: NSObject, Coordinator {
internal var presenter: UINavigationController
internal var tabBarController: UITabBarController?
internal var childCoordinators: [Coordinator]
var parentCoordinator: LoadingStateCoordinator?
lazy var leftBtn: UIBarButtonItem = {
let button = UIButton(type: .system)
button.setImage(UIImage(systemName: "arrow.turn.up.left"), for: .normal)
button.sizeToFit()
button.addTarget(self,
action: #selector(self.popToRoot(_:)),
for: .touchUpInside)
return UIBarButtonItem(customView: button)
}()
init(window: UIWindow, tabBarController: UITabBarController) {
self.tabBarController = tabBarController
childCoordinators = []
self.presenter = UINavigationController()
}
func start() {
performGetTabBar()
self.presenter.delegate = self
}
private func performGetTabBar() {
let coordinators: [Coordinator] = generateTabCoordinators()
coordinators.forEach({ coordinator in
coordinator.start()
addChildCoordinator(coordinator)
})
let presenters: [UIViewController] = coordinators.map({ coordinator -> UIViewController in
return coordinator.presenter
})
leftBtn.style = .plain
tabBarController?.navigationItem.leftBarButtonItem = leftBtn
tabBarController?.setViewControllers(presenters, animated: false)
selectTab(type: SurfTripCoordinator.self)
}
private func generateTabCoordinators() -> [Coordinator] {
let calculatorCoordinator: CalculatorCoordinator = CalculatorCoordinator(presenter: UINavigationController())
let tripCoordinator: SurfTripCoordinator = SurfTripCoordinator(presenter: UINavigationController())
let sellCoordinator: SavedTripsCoordinator = SavedTripsCoordinator(presenter: UINavigationController())
return [calculatorCoordinator, tripCoordinator, sellCoordinator]
}
*//this is not being called when coming from vertical flow*
#objc func popToRoot(_ sender: UIBarButtonItem) {
let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
tabBarController?.navigationController?.pushViewController(controller, animated: true)
}
}
extension TabBarCoordinator: UINavigationControllerDelegate {
func selectTab<T: Coordinator>(type _: T.Type) {
guard let index = childCoordinators.firstIndex(where: { coordinator in
coordinator is T
}) else {
return
}
tabBarController?.selectedIndex = index
}
}
and here is the current WelcomeCoordinator set up
class WelcomeCoordinator: NSObject, Coordinator {
internal var presenter: UINavigationController
var childCoordinators: [Coordinator]
init(window: UIWindow, presenter: UINavigationController) {
self.presenter = presenter
childCoordinators = []
}
func start() {
let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
controller.delegate = self
presenter.pushViewController(controller, animated: true)
}
}
extension WelcomeCoordinator : WelcomeViewControllerDelegate {
func performAddLevel() {
let addLevelCoordinator: AddLevelViewCoordinator = AddLevelViewCoordinator(presenter: UINavigationController())
addLevelCoordinator.start()
addChildCoordinator(addLevelCoordinator)
addLevelCoordinator.presenter.modalPresentationStyle = .fullScreen
presenter.present(addLevelCoordinator.presenter, animated: true, completion: nil)
}
}
sorry for the long post I wish there was more reaktive native way to do this...
Ok so I found partlly a solution the back button solution for my case: not using pushViewController or show because it comes with back button. presenter.setViewControllers([tabCoordinator.tabBarController!], animated: true) and there setting the navBar to hidden. I made my own navItem button to navigate to rootVC. Next step to allocate and remove all child tabBar coordinators on back tap recognized.
I've created an NSViewController with corresponding XIB in IB.
FWIW, I have the following window/view hierarchy:
class RxDemoController: NSWindowController {
#IBOutlet weak var statusPaneView: NSView!
#IBOutlet weak var annunciatorPaneView: NSView!
#IBOutlet weak var trigaStatusPaneView: NSView!
#IBOutlet weak var rxTabView: NSView!
override var windowNibName :String? { return "RxDemoController" }
override func windowDidLoad() {
super.windowDidLoad()
let statusPaneViewController = StatusPaneViewController()
statusPaneView.addSubview( statusPaneViewController.view )
let annunciatorPaneViewController = AnnunciatorPaneViewController()
annunciatorPaneView.addSubview( annunciatorPaneViewController.view )
let trigaStatusPaneController = TrigaStatusPaneController()
trigaStatusPaneView.addSubview( trigaStatusPaneController.view )
let rxTabController = RxTabController()
rxTabView.addSubview( rxTabController.view )
}
}
We can ignore the statesPaneViewController, annunciatorPaneViewController, and trigaStatusPaneController as they are all working properly. The rxTabController object is a tab view, defined in RXTabController.swift as follows:
class RxTabController: NSTabViewController {
#IBOutlet weak var rxDisplay1TabView: NSView!
override var nibName :String? { return "RxTabController" }
override func viewDidLoad() {
super.viewDidLoad()
let rxDisplay1ViewController = RxDisplay1()
rxDisplay1TabView.addSubview( rxDisplay1ViewController.view )
}
RxDisplay1.xib currently contains two objects: a bar graph object (custom component: LinearBarGraph_t which has been working fine in other projects and works fine on the view controller for the first tab), the second object is an NSSlider. The NSSlider is giving me problems. Here's the code for RxDisplay1:
class RxDisplay1: NSViewController {
#IBOutlet weak var linearPwrBG: LinearBarGraph_t!
override func viewDidLoad() {
super.viewDidLoad()
linearPwrBG.pctInRange = 50
}
#IBAction func linearPwrSlider(sender: NSSlider) {
}
}
I have right-dragged from the bar graph object to produce the #IBOutlet weak var linearPwrBG: LinearBarGraph_t! outlet; note that in viewDidLoad I set the pctInRange field to 50. This works fine when the app starts up (the bar graph is filled to the 50% mark).
The problem is with the slider. I've right dragged from the slider to the RxDisplay1 class to produce the linearPwrSlider action function. The connection seems to be fine (little filled dot next to the function declaration and right-clicking on the slider shows the action going to this function).
When I run the code, the display (and tab) properly appear. My bar graph shows 50% power. However, the moment I touch the slider with the mouse the system crashes. I get a break in AppDelegate complaining about "Bad Access".
I've used sliders all the time in past projects. However, this is the first time I've used a tabbed controller so I suspect there is something wrong with the way I've connected the tabbed controller to the rest of the system.