UINavigationBar in UINavigationController rotations not acting as expected (items shifted up) - ios

I've been working on a set of iOS action extensions I seem to be having a problem with the way that the UINavigationBar is being displayed in the app if it is controlled by a UINavigationController.
The problem, in short is that the navigation bar gets shifted up when you rotate from portrait, to landscape, and back to portrait again. The problem is exacerbated if you rotate all the way around. I wish I could provide images, but I cannot given the rules of StackOverflow.
Regardless, this was tested using a only slightly modified Action App Extension (modified to lower target OS, silence a warning about the NSExtensionActiviationRule, etc), both with a UINavigationBar embedded in a UINavigationController and not embedded, just as a view in the ActionViewController. When not in an UINavigationController, the rotations look as expected, but retains a white patch around the status bar (and the navigation bar is slightly wider in the landscape orientation), but when put into a UINavigationController, the bar button items are shifted up.
This was tested pretty thoroughly in 8.2, and even now running the slightly modified app, it looks as one would expect. Does anyone know exactly going on here? Why does the button get shifted up when I use a UINavigationController in 8.3 and not 8.2? I can't seem to find any documentation on a change in 8.3 that would affect it, and no documentation that UINavigationController shouldn't be used in an Action Extension, etc. Any help would be greatly appreciated.
Here is the code, if you are interested, for the ViewController. Its not particularly enlightening, as it's basically what Apple gives you:
import UIKit
import MobileCoreServices
class ActionViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Get the item[s] we're handling from the extension context.
// For example, look for an image and place it into an image view.
// Replace this with something appropriate for the type[s] your extension supports.
var imageFound = false
for item: AnyObject in self.extensionContext!.inputItems {
let inputItem = item as! NSExtensionItem
for provider: AnyObject in inputItem.attachments! {
let itemProvider = provider as! NSItemProvider
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
// This is an image. We'll load it, then place it in our image view.
weak var weakImageView = self.imageView
itemProvider.loadItemForTypeIdentifier(kUTTypeImage as String, options: nil, completionHandler: { (url, error) in
let imageURL = url as! NSURL?;
if imageURL != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
if let imageView = weakImageView {
let image = UIImage(contentsOfFile: imageURL!.path!);
imageView.image = image;
}
}
}
})
imageFound = true
break
}
}
if (imageFound) {
// We only handle one image, so stop looking for more.
break
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func done() {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
self.extensionContext!.completeRequestReturningItems(self.extensionContext!.inputItems, completionHandler: nil)
}
}
Thanks in advance!

Related

How to show FullScreenSlideshowViewController on ViewDidLoad

I am new to iOS development, I have created android app where app gets sets of images (sometimes over 100 images) from urls and loads into imageView with zoomEnabled Inside ViewPager.
Now I want to create same app for iOS, I have found this lib ImageSliderShow. Problem with this is it shows fullscreen ONLY when user did tap on selected image. I have been struggling to presentFullScreenController on viewDidLoad with no lock.
I only want image to be shown in fullScreen, example:
Select Category A From CategoryVC -> Loads ImageSlideVC, gets set of images from server, show in FullScreenView.
How can i achieve this? Adding this:
slideshow.presentFullScreenController(from: self)
on viewDidLoad didn't work:
#IBOutlet var slideshow: ImageSlideshow!
let kingfisherSource = [KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
override func viewDidLoad() {
super.viewDidLoad()
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
I dont get any error, output is same as before (shows slideshow in smallview with no zoom)
Please help
EDIT
by doing in viewDidAppear, after split second view is loaded its loads into fullscreen. First its smallview then shows in fullscreen. I think i have to do something inside setImageInputs
this is what its looks like :
open func setImageInputs(_ inputs: [InputSource]) {
self.images = inputs
self.pageControl.numberOfPages = inputs.count
// in circular mode we add dummy first and last image to enable smooth scrolling
if circular && images.count > 1 {
var scImages = [InputSource]()
if let last = images.last {
scImages.append(last)
}
scImages += images
if let first = images.first {
scImages.append(first)
}
self.scrollViewImages = scImages
} else {
self.scrollViewImages = images
}
reloadScrollView()
layoutScrollView()
layoutPageControl()
setTimerIfNeeded()
}
try to do it in viewDidAppear
#IBOutlet var slideshow: ImageSlideshow!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
Presenting viewController in ViewDidLoad is a bad practice.

Share Extension - Remove Textfield

Is there a way to remove the TextView- and PreviewPart, marked in RED, from my ShareExtention?
Im using the default:
class ShareViewController: SLComposeServiceViewController
As ViewController
I want to just show the Destination Selector like below (I know how to create that)
I know that you can create your own ViewController but it would be very hard to rebuild the default one.
The Second thing is:
What method would you recommend to Transfer/Copy Data/Files and which to read the Main App's Document Directory?
I want to transfer/copy the selected Document to the DocumentsDirectory of my Main App. Both are in the same AppGroup. Bc if I just save the Url of the current Document in UserDefault, then I guess the Main App can't access it.
I need to read the Main App's Document Directory, because I need the file Hierachy so I can select the saving location.
Note: Just because there was some confusion in the comments for whatever reason: This is my own ShareExtention and not smo else's in UIActivityController
Not the best, but here's what I've got based on the docs:
override func viewDidLoad() {
super.viewDidLoad()
textView.isHidden = true
}
But then there's blank space. Perhaps it's better to put some placeholder text in and prevent user input. There are other options in the docs you can use for that, or you can mess with textView (which is a UITextView)...
Edit: Here's a complete example that shows the URL in the text field, grey and non-user-interactible.
class ShareViewController: SLComposeServiceViewController {
override func viewDidLoad() {
super.viewDidLoad()
textView.isUserInteractionEnabled = false
textView.textColor = UIColor(white: 0.5, alpha: 1)
textView.tintColor = UIColor.clear // TODO hack to disable cursor
getUrl { (url: URL?) in
if let url = url {
DispatchQueue.main.async {
// TODO this is also hacky
self.textView.text = "\(url)"
}
}
}
}
override func isContentValid() -> Bool {
// Do validation of contentText and/or NSExtensionContext attachments here
return true
}
func getUrl(callback: #escaping ((URL?) -> ())) {
if let item = extensionContext?.inputItems.first as? NSExtensionItem,
let itemProvider = item.attachments?.first as? NSItemProvider,
itemProvider.hasItemConformingToTypeIdentifier("public.url") {
itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil) { (url, error) in
if let shareURL = url as? URL {
callback(shareURL)
}
}
}
callback(nil)
}
override func didSelectPost() {
getUrl { (url: URL?) in
if let url = url {
DispatchQueue.main.async {
print("url: \(url)")
}
}
}
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
}
What worked for me is to use a custom solution instead of SLComposeServiceViewController. It looks the same, and can add any element I want (see image at the end and here's the code at the commit when it was implemented).
Steps:
(code) Change ShareViewController to simple UIViewController
(code) Add blur effect to ShareViewController
(storyboard) Add container view to ShareViewController
(storyboard) Add navigation controller
(storyboard) Embed navigation controller in ShareViewController's container view
Customize the view controllers in the navigation controller (see this SO thread for example)
Step 1. Change ShareViewController to simple UIViewController
import UIKit
class ShareViewController: UIViewController {
// ^^^^^^^^^^^^^^^^
Step 2. Add blur effect to ShareViewController
// ShareViewController continued from Step 1.
override func viewDidLoad() {
super.viewDidLoad()
// https://stackoverflow.com/questions/17041669/creating-a-blurring-overlay-view/25706250
// only apply the blur if the user hasn't disabled transparency effects
if UIAccessibilityIsReduceTransparencyEnabled() == false {
view.backgroundColor = .clear
let blurEffect = UIBlurEffect(style: .dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
//always fill the view
blurEffectView.frame = self.view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.insertSubview(blurEffectView, at: 0)
} else {
view.backgroundColor = .black
}
// Do any additional setup after loading the view.
}
Step 3. Add container view to ShareViewController
Drag a Container View from the Object Library into the ShareViewController on the storyboard, and adjust dimension. For example:
Step 4. Add navigation controller
Drag a Navigation Controller from the Object Library to the storyboard.
Step 5. Embed navigation controller in ShareViewController's container view
Control-drag from the container view of ShareViewController to the navigation controller, select "Embed" from the menu. Should look similar to this:
Step 6. Customize the view controllers in the navigation controller (see this SO thread for example)
My result:
You can't.
When you use the share extension you will afterwards select an app to share to, thus plugging yourself to the share extension of said app.
Your app does not and cannot have any access of what said share extension does, those are totally separated from the main app runtime.
Also this allow users to customise what they share.
For the second part of your post you need to elaborate, who wants to transfer a document, you as the developper or your end-user?

Slide UIInputView with UIViewController like Slack

I'd like to use the UIViewController's input accessory view like this:
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView! {
return self.bar
}
but the issue is that I have a drawer like view and when I slide the view open, the input view stays on the window. How can I keep the input view on the center view like Slack does it.
Where my input view stays at the bottom, taking up the full screen (the red is the input view in the image below):
There are two ways to do this exactly like Slack doing it, Meiwin has a medium post here A Stickler for Details: Implementing Sticky Input Field in iOS to show how he managed to do this which he actually puts an empty UIView as an inputAccessoryView then track it’s coordinates on screen to know where to put his custom view in relation with the empty view, this way can be helpful if you are going to support SplitViewController on iPad, but if you are not interested in this way, you can see how I managed to do this like this image
Here is before swiping
Here is after
All I did was actually taking a snapshot from the inputAccessoryView window and putting it on the NavigationController of the TableViewController
I am using SideMenu from Jon Kent and it’s pretty easy to do it with the UISideMenuNavigationControllerDelegate
var isInputAccessoryViewEnabled = true {
didSet {
self.inputAccessoryView?.isHidden = !self.isInputAccessoryViewEnabled
if self.isInputAccessoryViewEnabled {self.becomeFirstResponder()}
}
}
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
let inputWindow = UIApplication.shared.windows.filter({$0.className == "UITextEffectsWindow"}).first
self.inputAccessoryViewSnapShot = inputWindow?.snapshotView(afterScreenUpdates: false)
if let snapShotView = self.inputAccessoryViewSnapShot, let navView = self.navigationController?.view {
navView.addSubview(snapShotView)
}
self.isInputAccessoryViewEnabled = false
}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
self.inputAccessoryViewSnapShot?.removeFromSuperview()
self.isInputAccessoryViewEnabled = true
}
I hope that helps :)

Crash while adding UIImage from code

I'm a Swift beginner and I'm writing an app that will show a list of things. When you click on one of them, you'll get detailed information about it and a photo. However, I've got one problem - I've got code to show the image, but when I click in simulator it crashes.
Text that is on the bottom of xcode when the app crashes:
fatal error: unexpectedly found nil while unwrapping an Optional value
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var detail = segue.destinationViewController as! detailSkinsViewController
detail.skinNameLabel = self.skin
if (self.skin == "AK47 | Wasteland Rebel") {
detail.skinImg.image = UIImage(named: "s495fn")
}
}
Thanks!
You should look at the crash stack to see the actual line. There are a couple of places you could have trouble:
var detail = segue.destinationViewController as! detailSkinsViewController
This requests a crash if destinationViewController is not of class detailSkinsViewController. (Swift classes should always begin with a capital letter. This should also be a let, not var. You never modify it.) Using if-let here would be much safer.
detail.skinImg.image = UIImage(named: "s495fn")
It's very unclear what these are, but if skinImg is a UIImageView!, then you would expect this to crash if it the destination NIB has not been loaded yet (which is likely). You generally should never reach into other objects IBOutlets for exactly this reason. (This is also a good reason to use ? rather than ! for IBOutlets. That would lead to just "nothing happens" rather than a crash.)
Rather than messing with another view controllers outlets, you should create a UIImage? property on the view controller itself. In its didSet, update the UIImageView if the view is loaded (isViewLoaded). During viewDidLoad, initialize the UIImageView using the property. This way, you have a clear API for others to set the image that doesn't expose your internal subviews (which are implementation details).
As an example:
class ViewController: UIViewController {
#IBOutlet private var imageView: UIImageView?
var image: UIImage? {
didSet(newImage) {
self.imageView?.image = newImage
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.imageView?.image = self.image
}
}
You should check your optional values,
i did not see your skin variable definition but i think that code below will solve your problem
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let detail = segue.destinationViewController as! detailSkinsViewController {
detail.skinNameLabel = self.skin
if (self.skin == "AK47 | Wasteland Rebel") {
detail.skinImg.image = UIImage(named: "s495fn")
}
}
}
This Error comes normally when trying to unwrap a value of an optional variable which has no value at all.
First check your optional variables and debug line by line to see if any of the Optional variables has no value at all which you had been trying to unwrap

How to make Image page viewer for images swift ios

I wanna display a couple of images from the assets folders, into my application. so I wanna make a page view.
First, images will be inside collection view, then on click, the image will be full screen. Then the user can slide between the images by swiping right and left as shown in the following photo:
I found this tutorial:
PhotosGalleryApp
Updated:
I have this in my storyboard:
Now in GaleryViewController I show the images in cells
when user click on it, I open the image in fullscreen in PhotoViewController.
PhotoViewController.swift :
import UIKit
class PhotoViewController: UIViewController{
#IBOutlet weak var imageView: UIImageView!
var index: Int = 0;
var pageViewController : UIPageViewController?
#IBAction func btnCancelClicked(sender: AnyObject) {
self.navigationController?.popToRootViewControllerAnimated(true);
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
initUI();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
self.navigationController?.hidesBarsOnTap = true;
self.navigationController?.hidesBarsOnSwipe = true;
displayPhoto()
}
func initUI() -> Void {
// pageViewController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: nil)
// pageViewController!.dataSource = self
}
func displayPhoto() {
self.imageView.image = UIImage(named: Constants.Statics.images[index])
}
I have the images in static structure so i can access them anywhere:
class Constants {
struct Statics {
static let images = ["1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","7.jpg","8.jpg"]
}
}
My problem is: I want to add the feature that allow me to swipe between pictures inside the PhotoViewController.
Any ideas?
Thank you
You can do one of the following two things to achieve your goal :
Make a modal segue of the cell to the next UICollectionViewController where the images are showed in full screen, and in the prepareForSegue pass all the data (images) you need to show and where exactly you are in this moment (indexOfImage).
You can watch the didSelectItemAtIndexPath and then pass all the data to one instance of the next UICollectionViewController you want to show with the presentViewController func.
But it's very important that in the next UICollectionViewController you have set pageEnabled = true in code or in the Interface Builder (I though is much easy to do.)
UPDATE:
Very good tutorials on how to make a slide show of images using an UIScrollView or an UICollectionView :
How To Use UIScrollView to Scroll and Zoom Content
Creating a Paged Photo Gallery With a UICollectionView
I hope this help you.

Resources