Embedding subview in main view Swift - ios

If you look at this image (I don't want to upload it, as it does not belong to me), you will see what appears to be a uiview inside the main uiview controller in the settings app of an iPad. My question is, how do I replicate this programatically? In other words, how do I embed a UIView in another?
Here's what I know and have done:
Based on my research, this is called "subviews". Is this correct?
I have created a UIView with the proper elements that I want in interface builder. It is currently not a subview, but at the same level hierarchically as my main view.
I found the same exact question except in Obj-C, not my language (Swift).
Here's what I need:
How do I programatically spawn a subview in swift once a button is clicked?
As this subview is pretty complex in terms of UIelements, I want to be able to design it in interface builder.
Here's what I have so far:
#IBAction func buttonPressedSpawnSubview(sender: AnyObject) {
//Open a subview from Interface builder.
}
#IBAction func closeButtonPressedSpawnSubview(sender: AnyObject) {
//kill the subview.
Can someone help me figure out how fill in the commented lines?

What you are seeing is a modal UIView on top of another view.
An example from within your visible ViewController would be:
self.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal // Choose whatever transition you want
self.modalPresentationStyle = .CurrentContext // Display on top of current UIView
self.presentViewController(yourNewViewObject, animated: true, completion: nil)

Related

Swift - Present a UIView (not a ViewController)

I have two UIViewControllers and I would like to .present the gray UIView like you would usually present a ViewController (appearing bottom up and user can slide down to dismiss).
Screenshots for a better understanding:
The bottomBar is a ContainerView and should not change by switching between the VC's, only the gray UIView which you can see in the 2nd picture.
I know I could just .present ViewControllerB without an animation and then just let the UIView appear from out of the screen. But if I do it like this the user is not able to drag down the UIView to dismiss it.
This is how I present ViewControllerB ("wishlistViewController) at the moment.
let wishlistViewController = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as! WishlistViewController
wishlistViewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.navigationController?.present(wishlistViewController, animated: false)
}
Main Problem is that my UIView is not fullscreen. Otherwise I could just .present ViewControllerB. However, with my design the background image would be animated by .presenting or .dismissing as well and I do not want that.
There is probably a very easy solution for this but I couldn't find on presenting ONLY a UIView.
Grateful for any tips :)
Well, there are two answers I can think of:
1) If you want, you could just animate it in from the bottom using UIView.animate() on a y-position constraint it has, or on its layer's y-position attribute. You could then attach a UISwipeGestureRecognizer to it so that if you swipe down on it, it will animate back down.
2) Make your views that you'd like to present viewControllers and have them be presented with a custom modal animation. This sounds like more work than you wanna do though. Here's a cool tutorial on youtube that walks you through how to create a custom animation transition between 2 view controllers:
https://www.youtube.com/watch?v=B9sH_VxPPo4
However, as far as I know, those .dismiss and .present methods are only meant for view controllers. Hopefully one of the two options I gave were helpful!

Where is the back button graphic Apple uses in the navigation stack?

I want to add code to the back button presented in UINavigationController. But, I want the back button to look identical to what Apple presents.
I have this all setup to use my graphic in the leftBarButton, but am unable to get the graphic to look perfect. To that end, is there a way that I can use the internal iOS back button in my own custom button?
Unfortunately the navigation bar's back button is not very customizable. There is no simple way to access this image.
If you're familiar with Sketch or Photoshop, I suggest you take a screenshot of the back button and trace your image over the exact location.
If you can't do this, you technically can access the back buttons image through some minor UI manipulation. You'll have to first have a back button which is on screen. Once you know it's on screen, such as in your viewDidAppear, you'll want to look through the subviews of your leftBarButtonItem. One way you can do this is by calling navigationBar.subviews and navigating until the view. Another way is to expose the items target view.
extension UIBarButtonItem {
var targetView: UIView? {
guard let view = value(forKey: "view") as? UIView else {
return nil
}
return view
}
}
Now you can call leftBarButtonItem.targetView.subviews. Your for-loop would look something like,
for subview in leftBarButtonItem.targetView.subviews {
if let imageView = subview as? UIImageView {
self.image = imageView?.image
}
}
All of this is pseudo code and untested. Typically UIKit will use the standard classes (such as UIImageView) when building their views. However in older classes, they have been known to draw images manually. So if there is no image, you can always resort to taking a snapshot of the view with the arrow.
view.snapshotView(afterScreenUpdates: false)
Once you have your image / view, you'll save it in a property (most likely in your custom navigation controller) and then you'll have access to it whenever you push new view controllers.

How to fix highlighted state UIButton delay in UIPageViewController page?

I have a screen in which I have a UIPageViewController in each page I have a UIButton. The problem is that there is a delay of the pressed/highlighted state of the button for about a half second when the user presses the button. both state images are set to the button using the storyboard.
This happens in the Simulator as well as on a real device.
Now from my Google searches, I came across a few posts that describe this issue, for example:
UIButton delayed state change
and:
UIbutton only looks clicked (highlighted) on longPress?
In all posts, the solution is to use the delaysContentTouches setting and set it to false.
The problem is: I didn't found how would I apply this in my case of a UIPageViewController. most of the posts talk this issue in a UIScrollView or a UITableView.
So, the question is: how would I do that in case of a UIPageViewController? I didn't see that UIPageViewController has this setting and didn't find any other way to apply it.
Found a solution to this issue, This piece of code will fix the button highlighted click delay but will prevent the pager scroll on the button itself.
public override func viewDidAppear(_ animated: Bool) {
for view in self.view.subviews {
if view is UIScrollView {
(view as? UIScrollView)!.delaysContentTouches = false
}
}
}
The reason I didn't find this in UIPageViewController is that UIPageViewController is not a subclass of UIScrollView as I expected but it contains it as a subview.

Adding a custom transition causes xib to load for the wrong screen size

I'm trying to add a custom transition to a UIViewController which has a xib. I have tried a few approaches but they all have the same issue, the view displays for the wrong screen size.
My current example is based on the following tutorial: Custom UIViewController transition in iOS with Swift
I have made no changes to the TransitionDelegate or Animators.
Here's the view in the xib and in the simulator:
The white UIView is set to centre, 80% width with a 1:1 ratio.
My UIViewController is loaded using the following:
let thirdViewController = ThirdViewController()
thirdViewController.transitioningDelegate = viewTransitionDelegate
thirdViewController.modalPresentationStyle = .custom
navigationController?.present(thirdViewController, animated: true, completion: nil)
If I comment out setting the delegate the view will load 100% perfectly:
Finally, I have another UIViewController which has no xib and the constraints are set programmatically. This view is loaded with the same transition and loads perfectly:
What do I need to change to get this working with XIBs?
I spent ages looking for an answer to this and then found it from one of the related questions in the sidebar.
All I needed to do was add the following to my animator:
toViewController.view.frame = fromViewController.view.frame
I hope this helps someone one day.

adding a uipageviewcontroller to a uistackview

I'm trying to add a sliding photo gallery functionality to the top portion of a view.
To give context, a user taps on a button or row or something. Then i load a scrollview with a uistackview in it. organized vertically, i had an image, and then another stack view with some text in it. Now, i want that image to become part of a larger "gallery". My research told me to implement UIPageviewcontroller and add the other images to a childVC.
i used this as a tutorial (the first example): http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift
the only relevant deviation from the tutorial my app has is that it creates things programmatically.
With my proof of concept for the gallery functionality, i wanted to integrate it with the previously mentioned stack view. my plan was to first add the pageviewcontroller stuff into the overall stack view with the original image view right below it and then simply remove the original image view to leave me the final product.
i was able to add the pageviewcontroller.view to the stackview, but the gallery doesn't show. taking a look at the UI Inspector, i can see that the gallery is kinda loaded, but it's messed up.
it's as if the uiview has a frame of 0 height and so the other stack view items don't respect the images that the pageviewcontroller is trying to show.
I think it could be that stack views can only handle specific views, not stuff as complicated as pageviewcontrollers.
also note: my implementation is all programmatic, no storyboards, and so for no xibs. so maybe i missed something here.
here is some code, if it helps:
note the constrain functions you see are from the "cartography" pod
this adds the "gallery" to the stack view, it's a delegate function from my view
func addZoomStuff(sender: UIStackView) {
let zoomer = PageBaseViewController()
addChildViewController(zoomer)
zoomer.view.translatesAutoresizingMaskIntoConstraints = false
zoomer.view.tag = 5
sender.addArrangedSubview(zoomer.view)
zoomer.didMoveToParentViewController(self)
}
this is what creates the scrollview, image view, etc for the gallery items:
override func viewDidLoad() {
super.viewDidLoad()
//MARK: - Zoom View Elements
// prep
scrollView = UIScrollView(frame: self.view.frame)
scrollView.delegate = self
self.view.addSubview(scrollView)
constrain(scrollView, view) { view, view2 in
view.edges == view2.edges
}
self.view.setNeedsUpdateConstraints()
// 1
let image = UIImage(named: imageName)!
imageView = UIImageView(image: image)
// 2
scrollView.addSubview(imageView)
constrain(imageView){ view in
view.edges == view.superview!.edges
}
scrollView.contentSize = image.size
i tried adding the constraints like this but there was no effect
func addZoomStuff(sender: UIStackView) {
let zoomer = PageBaseViewController()
addChildViewController(zoomer)
view.addSubview(zoomer.view)
constrain(zoomer.view, view) { view, view2 in
view.width == view2.width
view.height == view2.height * 2 / 3
view.leading == view2.leading
view.top == view2.top
}
zoomer.view.setNeedsUpdateConstraints()
zoomer.view.removeFromSuperview()
zoomer.view.translatesAutoresizingMaskIntoConstraints = false
zoomer.view.tag = 5
print("sender.subviews: \(sender.subviews)")
sender.addArrangedSubview(zoomer.view)
zoomer.didMoveToParentViewController(self)
print("sender.subviews: \(sender.subviews)")
}
if this method isn't going to work, can i do a nested horizontal stack view instead of the pageviewcontroller and somehow get that same scrolling/snap effect to see on image view at a time?
TLDR;
Create a subclass of UIPageViewController, make it it's own delegate.
Initialize the subclass with a plain UIViewController, only set a backgroundcolor.
In the pageviewcontroller subclass, implement the two delegate callbacks for a next and previous viewcontroller: create a plain viewcontrolller, with some random backgroundcolor.
If this works, replace the plain viewcontroller by your actual contentviewcontroller.
Long version:
Have you seen this: Maybe this link will help: https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/PageViewControllers.html
It might help, as it explains the details of UIPageViewController. Basically, you need to create a viewController (not a view!), that shows one page of the gallery. So this VC has a stackview, and manages the content of it. The pageviewcontroller is initialized with your first contentviewController. If you create a subclass of the uipageviewcontroller, you can set self of that subclass as the delegate of it. Implement the delegate callbacks that return the next or previous viewController and thats it. For this last part, it is convenient to have a property on the contentviewcontroller from which the subclasses of the pageviewcontroller can figure out what data to set on the next or previousviewcontroller.
Your title seeks to hint at some confusion: its not possible to add a viewcontroller to a view. You can only add other views to a (stack)view. A viewcontroller owns and manages a viewhierarchy. A pageviewcontroller has no content, but manages the insertion and removal of viewcontrollers. as the pageviewcontroller is a containerviewcontroller, it will als take the contentViewcontrollers' views and place them in the viewhierarchy. But this is not something your code has to do when you subclass UIPageViewControlller and implement it's delegates on itself (and don't forget to assign self to be the delegate).

Resources