I have multiple custom pop up views that I call (to set it up, animate the pop up and animate the dismiss) but I want to have one variable that I can replace in the multiple functions that can be set by a switch statement with one variable. So I don't have 4+ of every function with the same code but different uiView names. How is that done please?
var myPopupView:NotePopUpView!
var myInformationPopUpView:InformationPopUpView!
var myDatePopUpView:DatePopUpView!
var myPainDiagramView:PainDiagramPopUpView!
func notePopUpCalled() {
if (myPopupView != nil) {
self.myPopupView.view.removeFromSuperview()
}
// making different size pop up for different screens
var popUpWidth = GlobalConstants.ScreenStats.screenWidth - 60
if UIScreen.mainScreen().bounds.size.width > 320 {
if UIScreen.mainScreen().scale == 3 {
popUpWidth = 320
} else {
popUpWidth = 320
}
} else {
popUpWidth = 283
}
self.myPopupView = NotePopUpView(frame: CGRect(x: 20, y: 350, width: popUpWidth, height: 334))
myPopupView.center = self.view.center
self.view.addSubview(myPopupView)
showAnimate()
}
Generally when it is necessary to apply one method on different types recommended to use generics.
func setupView<T: UIView>(view: T) {
//Make all setups
}
Also you can use as? operator to downcasting your view types to UIView.
Hope it helps.
Related
How to remove subviews?
I am trying to integrate GIF by creating UIView and UIImageView programmatically.
It works fine to show GIF but when the function of hiding if is called, there is no response.
Here are the codes of both functions.
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
lazy var transparentView: UIView = {
let transparentView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
transparentView.backgroundColor = viewColor.withAlphaComponent(setAlpha)
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.center = transparentView.center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
self.addSubview(self.transparentView)
self.transparentView.addSubview(self.gifImage)
self.transparentView.bringSubview(toFront: self.gifImage)
UIApplication.shared.keyWindow?.addSubview(transparentView)
}
func hideLoaderView() {
self.transparentView.removeFromSuperview()
}
}
A couple of thoughts:
I’d suggest you add a breakpoint or a logging statement in hideLoaderView and make sure you’re getting to that line.
You should make the init method to this class private to make sure you’re not calling hideLoaderView on some separate instance. When dealing with singletons, you want to make sure you can’t accidentally create another instance.
But I tested your code, and it works fine. Your problem probably rests with where and how you call this (and making init private, you might find where you might be using it inappropriately).
In the comments below, you said:
I simply call the function "CustomLoader().hideLoaderView()" Both are being called technically. What do you mean by "where I using it inappropriately?"
That is the root of the problem.
The CustomLoader() of CustomLoader().hideLoaderView() will create a new instance of CustomLoader with its own transparencyView, etc., which is precisely what the problem is. You’re not hiding the old view that was presented earlier, but trying to hide another one that you just created and was never displayed.
If you instead use that static, e.g. CustomLoader.instance.showLoaderView() and CustomLoader.instance.hideLoaderView(), then the problem will go away. Then you will be hiding the same view that your previously showed.
By the way, a few other unrelated observations:
If this is a singleton or shared instance, the convention would be to call that static property shared, not instance.
By the way, you aren’t using this CustomLoader as a UIView, so I’d not make it a UIView subclass. Don’t make it a subclass of anything.
You would obviously eliminate that self.addSubview(transparentView) line, too.
The bringSubview(toFront:) call is unnecessary.
You should avoid referencing UIScreen.main.bounds. You don’t know if your app might be in multitasking mode (maybe this isn’t an issue right now, but it’s the sort of unnecessary assumption that will cause problems at some later date). Just refer to the bounds of the UIWindow to which you’re adding this. You should also update this frame when you show this view, not when you create it (in case you changed orientation in the intervening time, or whatever).
By the way, using keyWindow is discouraged in iOS 13 and later, so you might eventually want to remove that, too.
When adding the gifImage (which I’d suggest renaming to gifImageView because it’s an image view, not an image), you should not reference the center of its superview. That’s the coordinate of the transparent view in its super view’s coordinate system, which could be completely different than the transparent view’s own coordinate system. In this case, it just happens to work, but it suggests a fundamental misunderstanding of view coordinate systems. Reference the bounds of the transparentView, not its center.
If you’re going to expose viewColor and setAlpha, you should pull the setting of the transparentView’s color out of the lazy initializer and into showLoaderView, at the very least. Right now, if you show the loader once, and then change the color, and try to show it again, you won’t see the new color.
The same issue applies with the gif image. So, I’d move that to the didSet observer.
Thus, pulling this all together:
class CustomLoader{
static let shared = CustomLoader()
private init() { }
var dimmingColor: UIColor = .black
var dimmingAlpha: CGFloat = 0.5
var gifName: String = "" { didSet { gifImage.loadGif(name: gifName) } }
lazy var transparentView: UIView = {
let transparentView = UIView()
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImageView: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.isUserInteractionEnabled = false
return gifImage
}()
func showLoaderView() {
guard let window = UIApplication.shared.keyWindow else { return }
transparentView.frame = window.bounds
transparentView.backgroundColor = dimmingColor.withAlphaComponent(dimmingAlpha)
gifImageView.center = CGPoint(x: transparentView.bounds.midX, y: transparentView.bounds.midY)
transparentView.addSubview(gifImageView)
window.addSubview(transparentView)
}
func hideLoaderView() {
transparentView.removeFromSuperview()
}
}
Why you are using transparentView while you are have a CustomLoader instance view
Try to use this
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
init() {
super.init(frame: UIScreen.main.bounds)
backgroundColor = viewColor.withAlphaComponent(setAlpha)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.backgroundColor = .red
gifImage.contentMode = .scaleAspectFit
gifImage.center = center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
addSubview(self.gifImage)
UIApplication.shared.keyWindow?.addSubview(self)
}
func hideLoaderView() {
removeFromSuperview()
}
}
I've just started to code my app in Swift 2 and avoiding the use of XIBs and storyboards.
However, I am unable to replicate the following feature. It's exactly what I wanted.
I've tried creating a UIView to perform the following using .backgroundColor and it works, however, I am unable to link it to my UIViewControllers. Just wondering how is it done? How do I link my UIView to my UIViewController?
Codes:
let subFrame : CGRect = CGRectMake(0,screenHeight*1/2.75,screenWidth,screenHeight)
var loginView = SignUpViewController()
let signUpView: UIView = UIView(frame: subFrame)
signUpView.backgroundColor = UIColor.redColor()
//Controls what each segment does
switch segmentView.indexOfSelectedSegment {
case 0:
self.view.addSubview(signUpView)
case 1:
self.view.addSubview(loginView)
default:
break;
}
I'm not even sure if .view.addSubview(xxx) overwrites/replaces the original subview if it is not this way. Is this the right way to do it?
Do not just start coding an app if you are not familiar with simple things of the OOP (Object-Oriented-Programming) language like Swift. This is not the way how to learn a programming language. Sure you could learn while experimenting but it is better to understand the book first before starting with more complex stuff. Read a few more pages of the Swift book from Apple. Most classes for iOS development are still Objective-C wrapped classes (reference type because the top superClass is probably NSObject; keep this in mind).
Here is the code example you wanted:
class ViewController: UIViewController {
let firstView = UIView()
let secondView = UIView()
let segmentedControlView = UISegmentedControl(items: ["firstView", "secondView"])
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor() // we need this for the playground
/* setup your view here */
/* add your sigment logic somewhere */
self.view.addSubview(self.segmentedControlView)
self.view.addSubview(self.firstView)
self.view.addSubview(self.secondView)
self.segmentedControlView.frame = CGRect(x: 0, y: 20, width: self.view.frame.width, height: 44)
self.segmentedControlView.selectedSegmentIndex = 0 // enable the first segment
self.segmentedControlView.addTarget(self, action: "segmentIndexChanged:", forControlEvents: UIControlEvents.ValueChanged)
/* add your own frame calculation here */
/* I prefer AutoLayout, but for the example static frames will be fine */
self.firstView.frame.origin = CGPoint(x: 0, y: self.segmentedControlView.frame.origin.y + self.segmentedControlView.frame.height)
self.firstView.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.height - self.segmentedControlView.frame.origin.y)
// to prevent same code, we just copy the same frame from the firstView
// both will sit in the same place
self.secondView.frame = self.firstView.frame
/* lets add some colors so we'll see our views */
self.firstView.backgroundColor = UIColor.blueColor()
self.secondView.backgroundColor = UIColor.redColor()
self.secondView.hidden = true // when intializer the secondView is not visible
}
func segmentIndexChanged(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
self.firstView.hidden = false
self.secondView.hidden = true
case 1:
self.firstView.hidden = true
self.secondView.hidden = false
default:
break;
}
}
}
If you do not understand a function, should should look up its definition in the developer docs. (Like: addSubview)
I am trying to make UIAlertController that looks like this:
How can we customize the UIAlertController to get the result something same as this picture ?
What you are trying to do is a popover, for current versions of iOS you can achieve the same effect for both iPad and iPhone.
1.- Start by building your design on Storyboard or a xib. and then reference it.
2.- then present it as a popover.
3.- maybe you will want to implement popoverdelegates to avoid wrong positions when rotating the device.
for example:
private static func presentCustomDialog(parent: UIViewController) -> Bool {
/// Loads your custom from its xib or from Storyboard
if let rateDialog = loadNibForRate() {
rateDialog.modalPresentationStyle = UIModalPresentationStyle.Popover
rateDialog.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
let x = parent.view.center
let sourceRectX : CGFloat
let maximumDim = max(UIScreen.mainScreen().bounds.height, UIScreen.mainScreen().bounds.width)
if maximumDim == 1024 { //iPad
sourceRectX = x.x
}else {
sourceRectX = 0
}
rateDialog.popoverPresentationController?.sourceView = parent.view
rateDialog.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.allZeros
rateDialog.popoverPresentationController?.sourceRect = CGRectMake(sourceRectX, x.y, 0, 0)
rateDialog.popoverPresentationController?.popoverLayoutMargins = UIEdgeInsetsMake(0, 0, 0, 0)
rateDialog.popoverPresentationController?.delegate = parent
rateDialogParent = parent
dispatch_async(dispatch_get_main_queue(), {
parent.presentViewController(rateDialog, animated: true, completion: nil)
})
return true
}
return false
}
Update: to achieve, point 3... on your parent UIViewController.
public class MyParentViewController: UIViewController, UIPopoverPresentationControllerDelegate {
/**
This function guarantees that the CustomDialog is always centered at parent, it locates the Dialog view
*/
public func popoverPresentationController(popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer<CGRect>, inView view: AutoreleasingUnsafeMutablePointer<UIView?>) {
let x = popoverPresentationController.presentingViewController.view.center
let newRect = CGRectMake(x.x, x.y, 0, 0)
rect.initialize(newRect)
}
}
I did the custom popup windows as #Hugo posted, after a while i found a library that is done in a very neat and magnificent way which can be used to implement custom popup views with less effort:
here is the link for the library on Github:
https://github.com/m1entus/MZFormSheetPresentationController
It is written in Objective c, as well as there is swift example included in the library's samples.
to include it into swift project you will need to use something called Bridging-header
In iOS 9, is it possible to detect when an app is running in iOS 9's Slide Over or Split View mode?
I've tried reading through Apple's documentation on iOS 9 multitasking, but haven't had any luck with this…
I ask because I might have a feature in my app that I'd like to disable when the app is opened in a Slide Over.
Just check if your window occupies the whole screen:
BOOL isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
If this is false, then you're running in a split view or a slide over.
Here is the code snipped which will automatically maintain this flag irrespective of rotation
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
Just another way to repackage all of this
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !window.frame.equalTo(window.screen.bounds)
}
}
then you can just
UIApplication.shared.isSplitOrSlideOver in Swift
UIApplication.sharedApplication.isSplitOrSlideOver in Objective-C
Note that, in Swift, the window object is a double optional... WTF!
For iOS 13+ (note, I haven't tested the iOS 13 code myself yet)
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let window = self.windows.filter({ $0.isKeyWindow }).first else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
I'm late to the party, but if you want a property that works independent of the orientation, try this one:
extension UIApplication
{
func isRunningInFullScreen() -> Bool
{
if let w = self.keyWindow
{
let maxScreenSize = max(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let minScreenSize = min(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let maxAppSize = max(w.bounds.size.width, w.bounds.size.height)
let minAppSize = min(w.bounds.size.width, w.bounds.size.height)
return maxScreenSize == maxAppSize && minScreenSize == minAppSize
}
return true
}
}
Like the solution by Dan Rosenstark, but changed to work on the new iPad Pro's that seem to report a different frame and screen.bounds height based on if it's ran directly on the device through Xcode, or if it is compiled and released through TestFlight or App Store. The height would return 980 when through AS or TF, rather than 1024 as it was supposed to like through Xcode causing it to be impossible to return true.
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
I recently had to determine display style of an application based including, not only if it changed to split view or slide-over, but also what portion of the screen was being utilized for the application (full, 1/3, 1/2, 2/3). Adding this to a ViewController subclass was able to solve the issue.
/// Dismisses this ViewController with animation from a modal state.
func dismissFormSheet () {
dismissViewControllerAnimated(true, completion: nil)
}
private func deviceOrientation () -> UIDeviceOrientation {
return UIDevice.currentDevice().orientation
}
private func getScreenSize () -> (description:String, size:CGRect) {
let size = UIScreen.mainScreen().bounds
let str = "SCREEN SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
private func getApplicationSize () -> (description:String, size:CGRect) {
let size = UIApplication.sharedApplication().windows[0].bounds
let str = "\n\nAPPLICATION SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
func respondToSizeChange (layoutStyle:LayoutStyle) {
// Respond accordingly to the change in size.
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
private func determineLayout () -> LayoutStyle {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
return .iPadFullscreen
}
// Set a range in case there is some mathematical inconsistency or other outside influence that results in the application width being less than exactly 1/3, 1/2 or 2/3.
let lowRange = screenWidth - 15
let highRange = screenWidth + 15
if lowRange / 2 <= appWidth && appWidth <= highRange / 2 {
return .iPadHalfScreen
} else if appWidth <= highRange / 3 {
return .iPadOneThirdScreen
} else {
return .iPadTwoThirdScreeen
}
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
respondToSizeChange(determineLayout())
}
Here is a simpler and less fragile way with no constants, that I use in an iPhone/iPad iOS app.
This code also distinguishes between slide over and split view.
I'm returning String values here for clarity, feel free to use enum values and to merge the two cases of fullscreen as suits your app.
func windowMode() -> String {
let screenRect = UIScreen.main.bounds
let appRect = UIApplication.shared.windows[0].bounds
if (UIDevice.current.userInterfaceIdiom == .phone) {
return "iPhone fullscreen"
} else if (screenRect == appRect) {
return "iPad fullscreen"
} else if (appRect.size.height < screenRect.size.height) {
return "iPad slide over"
} else {
return "iPad split view"
}
}
You can watch both -willTransitionToTraitCollection:withTransitionCoordinator: for the size class and viewWillTransitionToSize:withTransitionCoordinator: for the CGSize of your view. Hardcoding in size values isn't recommended though.
The horizontal size class will be compact when in slide over or 33% split view. I don't think you can detect once you go to 50% or 66% though.
I made an edit to #Michael Voccola solution which fixed the problem for orientation
I used this way in my situation to detect all iPad split screen state and handling layout
Just call determineLayout() to get current layoutStyle
private func getScreenSize() -> CGRect {
let size = UIScreen.main.bounds
return size
}
private func getApplicationSize() -> CGRect {
let size = UIApplication.shared.windows[0].bounds
return size
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
func determineLayout() -> LayoutStyle {
if UIDevice.current.userInterfaceIdiom == .phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
// full screen
return .iPadFullscreen
}
let persent = CGFloat(appWidth / screenWidth) * 100.0
if persent <= 55.0 && persent >= 45.0 {
// The view persent between 45-55 that's mean it's half screen
return .iPadHalfScreen
} else if persent > 55.0 {
// more than 55% that's mean it's 2/3
return .iPadTwoThirdScreeen
} else {
// less than 45% it's 1/3
return .iPadOneThirdScreen
}
}
extension UIApplication {
func isRunningInFullScreen() -> Bool {
if let w = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
.first?.windows
.filter({$0.isKeyWindow}).first {
let maxScreenSize = max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
let minScreenSize = min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
let maxAppSize = max(w.bounds.size.width, w.bounds.size.height)
let minAppSize = min(w.bounds.size.width, w.bounds.size.height)
return maxScreenSize == maxAppSize && minScreenSize == minAppSize
}
return true
}
}
here is the code for those who don't want to see swift lint complaints for deprecated keyWindow
After much 'tinkering', I have found a solution for my App that may work for you:
In AppDelegate.swift, create the following variable:
var slideOverActive: Bool = false
Then, in ALL of your view controllers, add the UIApplicationDelegate to the Class definition, create an appDelegate variable, and then add the below traitCollectionDidChange function:
class myViewController: UIViewController, UIApplicationDelegate {
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
let screenWidth = UIScreen.mainScreen().bounds.width
if previousTraitCollection != nil {
let horizontalSizeClass: Int = previousTraitCollection!.horizontalSizeClass.rawValue
if screenWidth == 1024 || screenWidth == 768 { // iPad
if horizontalSizeClass == 2 { // Slide Over is ACTIVE!
appDelegate.slideOverActive = true
} else {
appDelegate.slideOverActive = false
}
}
}
}
}
Then, wherever in your code you wish to check whether the slide-over is active or not, simply check:
if appDelegate.slideOverActive == true {
// DO THIS
} else {
// DO THIS
}
It's a bit of a workaround, but it works for me at the moment.
Happy trails!
Adding to #Tamas's answer:
Here is the code snippet that will automatically maintain this flag irrespective of rotation.
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
Trying [UIScreen mainScreen].bounds,
self.window.screen.bounds,
self.window.frame,
UIApplication.sharedApplication.keyWindow.frame and so on, the only working solution was deprecated method
CGRect frame = [UIScreen mainScreen].applicationFrame;
Which I fixed this way
CGRect frame = [UIScreen mainScreen].applicationFrame;
frame = CGRectMake(0, 0, frame.size.width + frame.origin.x, frame.size.height + frame.origin.y);
self.window.frame = frame;
And I'm really late to the party! But nonetheless, here's a simple, swifty solution to the problem. Using let width = UIScreen.mainScreen().applicationFrame.size.width we can detect the width of my app's window, and then have things occur when it is smaller than a certain number (i.e. on iPhone screen or in split view), useful to make different things happen on smaller screens. To have the computer check the width over and over again, we can run an NSTimer every hundredth of a second, then do stuff if the width is higher/lower than something.
Some measurements for you (you have to decide what width to make stuff occur above/below):
iPhone 6S Plus: 414.0mm
iPhone 6S: 375.0mm
iPhone 5S: 320.0mm
iPad (portrait): 768.0mm
iPad (1/3 split view): 320.0mm
iPad Air 2 (1/2 split view): 507.0mm
iPad (landscape): 1024.0mm
Here's a code snippet:
class ViewController: UIViewController {
var widthtimer = NSTimer()
func checkwidth() {
var width = UIScreen.mainScreen().applicationFrame.size.width
if width < 507 { // The code inside this if statement will occur if the width is below 507.0mm (on portrait iPhones and in iPad 1/3 split view only). Use the measurements provided in the Stack Overflow answer above to determine at what width to have this occur.
// do the thing that happens in split view
textlabel.hidden = false
} else if width > 506 {
// undo the thing that happens in split view when return to full-screen
textlabel.hidden = true
}
}
override func viewDidAppear(animated: Bool) {
widthtimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "checkwidth", userInfo: nil, repeats: true)
// runs every hundredth of a second to call the checkwidth function, to check the width of the window.
}
override func viewDidDisappear(animated: Bool) {
widthtimer.invalidate()
}
}
I hope this can help anyone who comes peeking!
By using below code you can check splitViewController is Collapsed or Not
if splitViewController?.isCollapsed ?? false {
// splitview collapsed
} else {
// splitview not collapsed
}
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)
}
}
}