I am fairly new to iOS programming and am building my app in Swift for iOS8 and also implementing the Mapbox-ios-sdk as I want my user to be able to download offline map areas.
I've been able to download offline map areas in the cache, however it seems the cache is not accessed when I turn of Wifi and cellular data. The Map Tiles are not coming up. Only the Map Tiles that were automatically cached are showing.
I see that the tiles are being downloaded (RMTileCache.db is growing, the RMTileCacheBackgroundDelegate methods didBeginBackgroundCacheWithCount, didBackgroundCacheTile and tileCacheDidFinishBackgroundCache are being called) and, when looking directly in the db file I also see that the correct tiles have been downloaded.
But, when I then turn off Wifi and Cellular data, the map tiles are not loaded on the screen. I've tried several options with adding additional RMDatabase Caches etc, but nothing seems to work. I'm hoping that someone out there ran into the same problem and can help me out.
My code is roughly based on this piece here: http://mapbox.com/mapbox-ios-sdk/#tile_caching__class
Class Level relevant variables:
var tileSource: RMMapboxSource = RMMapboxSource()
#IBOutlet var mapView: RMMapView!
viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
let fullPath: String = NSBundle.mainBundle().pathForResource("mapbox", ofType: "json")!
var errorInFullPath:NSErrorPointer? = NSErrorPointer()
let tileJSON: String? = String(contentsOfFile: fullPath, encoding:NSUTF8StringEncoding, error: errorInFullPath!)
tileSource = RMMapboxSource(tileJSON: tileJSON)
initializeMap()
}
The Map initialization:
func initializeMap() {
mapView = RMMapView(frame: self.view.bounds)
mapView.tileSource = tileSource
tileSource.cacheable = true
mapView.adjustTilesForRetinaDisplay = true
mapView.userInteractionEnabled = true
mapView.zoom = 9
self.view.addSubview(mapView)
mapView.delegate = self
}
The download operation:
func startDownload() {
mapView.tileCache.backgroundCacheDelegate = self
mapView.tileCache.beginBackgroundCacheForTileSource(tileSource, southWest: sw, northEast: ne, minZoom: 1, maxZoom: zoomLevel)
}
sw, ne and zoomLevel have been defined elsewhere in the code and their values are good when I print them out just before the beginBackgroundCacheForTileSource command.
I'm not sure what I'm doing wrong here. Probably something really stupid, but I've been trying to find it for 2 days now. Hope someone can help me out.
Thanks in advance!
OK, so I got it working thanks to the tip of incanus. Turns out it was two problems which made it more difficult to analyze and find the root cause.
1st problem was indeed that I had some layout things going on in viewDidLoad which somehow prevented cashed map tiles to be loaded.
2nd problem was that the area I was downloading was slightly different from the area I thought I was downloading. So I just need to tweak the pixelToCoordinate method of RMMapView which I'm using to get this right.
For those of you interested. I created a separate clean swift file without any layout in it, just to follow up on Incanus' comment. This worked for me!
Thanks to Incanus. Hope someone else will find this useful too.
import UIKit
class ViewOfflineMapAreaViewController: UIViewController, RMMapViewDelegate {
var tileSource: RMMapboxSource = RMMapboxSource()
#IBOutlet var mapView: RMMapView!
override func viewDidLoad() {
super.viewDidLoad()
let fullPath: String = NSBundle.mainBundle().pathForResource("mapbox", ofType: "json")!
var errorInFullPath:NSErrorPointer? = NSErrorPointer()
let tileJSON: String? = String(contentsOfFile: fullPath, encoding:NSUTF8StringEncoding, error: errorInFullPath!)
tileSource = RMMapboxSource(tileJSON: tileJSON)
tileSource.retryCount = 3
initializeMap()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func initializeMap() {
mapView = RMMapView(frame: self.view.bounds)
mapView.tileSource = tileSource
tileSource.cacheable = true
mapView.adjustTilesForRetinaDisplay = true
mapView.userInteractionEnabled = true
mapView.zoom = 9
mapView.delegate = self
//These two lines are just to resize the mapView upon device rotation
mapView.setTranslatesAutoresizingMaskIntoConstraints(true)
mapView.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
}
override func viewDidAppear(animated: Bool) {
self.view.addSubview(mapView)
}
}
You are saving the TileJSON locally and using it for instantiation, which is needed when you are operating offline. But I think your problem may be in doing layout during -viewDidLoad. Try this in -viewWillAppear: or later and I think you'll see different results.
Related
I have several different ViewControllers set up in my project. The first one acts as a 'landing' page and and on pressing a button the view is directed to a following ViewController.
There are various buttons and labels on there that I want to use to provide information and run a method. The buttons all work ok, but I have followed every tutorial I can to get the labels to update based on a method, and I can't seem to get them to do so.
I know the methods are called correctly as I have put print statements in there to check.
Basic idea is, program plays a series of beeps separated by a delay (bleep_time), this changes each level (bleep_level) and there are several steps (bleep_step) in each level. Ive simplified the arrays containing this data to save space.
I have successfully created a number of tutorial projects and the labels all update correctly, but they only use one ViewController.
Is the issue I am facing due to using 2 ViewControllers?
ViewController 1
import UIKit
class Bleep_Test_Menu: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
View Controller 2
import UIKit
import AVFoundation
class Bleep_Test_Level_1: UIViewController {
// Global Variables
var audioPlayer: AVAudioPlayer?
// Bleep test delays
var bleep_time = [1,2,3,4]
// Levels of bleep test
var bleep_level = [1,2,3,4]
// Level Label
#IBOutlet weak var bleepTestLevel1Level: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// Function to run repeated bleeps
#IBAction func bleepTestLevel1Start(_ sender: Any) {
var i = 0
tracker = false
let length = bleep_time.count
while i < length {
var bleeplevel = bleep_level[i]
bleepTestLevel1Level.text = "\(bleeplevel)"
playSaveSound()
sleep(UInt32(bleep_time[i]))
i += 1
}
}
// Function to play sounds
func playSaveSound(){
let path = Bundle.main.path(forResource: "Sound1.wav", ofType: nil)!
let url = URL(fileURLWithPath: path)
do {
//create your audioPlayer in your parent class as a property
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.play()
} catch {
print("couldn't load the file")
}
}
}
The only error I keep getting in the console is this;
"Tests[53616:891478] - changing property contentsGravity in transform-only layer, will have no effect"
But I can't seem to find anything that relates to my issue.
I have done live streaming for a single platform like facebook using LFLivekit. When I am trying for two platforms it's not happening.
Code:
import UIKit
import LFLiveKit
class VideoViewController: UIViewController {
var sessionO: LFLiveSession =
{
let audioConfiguration = LFLiveAudioConfiguration.defaultConfiguration(for: LFLiveAudioQuality.high)
let videoConfiguration = LFLiveVideoConfiguration.defaultConfiguration(for: LFLiveVideoQuality.low3)
let session = LFLiveSession(audioConfiguration: audioConfiguration, videoConfiguration: videoConfiguration)
return session!
}()
override func viewDidLoad() {
super.viewDidLoad()
self.streamUrl = "live-api-s.facebook.com:80/rtmp/"
self.streamName = "17XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
self.sessionO.delegate = self
self.sessionO.preView = self.view
self.sessionO.running = true
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
let stream = LFLiveStreamInfo()
stream.url = "rtmp://\(self.streamUrl!)/LiveApp/\(self.streamName!)"
self.sessionO.startLive(stream)
}
}
Note: If I create one more session and stream also for youtube it is not working.
We need to create separate RTMP connection and create the listener.
fbRtmpStream = RTMPStream(connection: fbRtmpConnection)
ytRtmpStream = RTMPStream(connection: ytRtmpConnection)
self.fbRtmpConnection1.addEventListener(Event.RTMP_STATUS, selector:#selector(self.fbRtmpStatusHandler1(_:)), observer: self)
self.fbRtmpConnection1.connect("rtmp://live-api-s.facebook.com:80/rtmp/")
self.fbStreamName1 = "xxxxxx"
self.ytRtmpConnection.addEventListener(Event.RTMP_STATUS, selector:#selector(self.ytRtmpStatusHandler(_:)), observer: self)
self.ytRtmpConnection.connect("rtmp://a.rtmp.youtube.com/live2/LiveApp/")
self.ytStreamName = "xxxxxxxxx"
This might not be the solution you're looking for source
Multiple server upload main problem is the error handling more complex, for example, the way out of the problem,this has not yet been good how to deal with. source
So! LFLiveKit does not support multiple server
I am walking through the basic iOS guide to add a map to my App here: https://developers.arcgis.com/ios/10-2/swift/guide/develop-your-first-map-app.htm
When running the app, it displays the basemap I have added but it does not seem to respond to any actions so I can not pane/zoom.
Here is my exact Swift controller code:
import UIKit
import ArcGIS
class ViewController: UIViewController, AGSMapViewLayerDelegate {
#IBOutlet var mapView: AGSMapView!
override func viewDidLoad() {
super.viewDidLoad()
let tiledLayer = AGSLocalTiledLayer(name: "Norfolk")
self.mapView.addMapLayer(tiledLayer, withName: "Norfolk")
mapView.locationDisplay.startDataSource()
}
}
I have tested on both an iPad and iPhone and the behavior is the same.
I am using ArcGIS 10.2.5
Where is your map url?
let url = NSURL(string: "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer")
let tiledLayer = AGSTiledMapServiceLayer(URL: url)
self.mapView.addMapLayer(tiledLayer, withName: "Basemap Tiled Layer")
And have you added Privacy - Location When In Use Usage Description into info.plist of your app? You should do that if you call
mapView.locationDisplay.startDataSource()
I am not sure what I did incorrectly the first time, but I removed the view and re added it and it worked the 2nd time.
I am trying to load a pdf using web view using swift. It can load only one page of the pdf, cannot scroll down more than one page. What can i do?
import UIKit
class ViewController: UIViewController,UIWebViewDelegate {
#IBOutlet var webViews: UIWebView!
var path = ""
override func viewDidLoad() {
super.viewDidLoad()
path = NSBundle.mainBundle().pathForResource("ibook", ofType: "pdf")!
let url = NSURL.fileURLWithPath(path)
/*webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true*/
webViews.delegate = self
self.webViews.loadRequest(NSURLRequest(URL: url!
))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func webViewDidStartLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = true
println("webViewDidStartLoad")
}
func webViewDidFinishLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = [enter image description here][1]false
webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true
println("webViewDidFinishLoad")
}
}
I've bumped into the similar problem while trying to display external pdf (not the bundled one), but I suppose you can use the same fix.
In your webViewDidFinishLoad, check if the url is actually a pdf one. Because in my case I know what I'm expecting, I used simple dumb checking. If url links to a pdf, you need to reload the web view to show it correctly and hence be able to scroll.
Here is a bit simplified code in objective C. It should be quite similar in Swift. Try something like this:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
static BOOL isPdfReloaded = NO;
if (!isPdfReloaded && [webView.request.URL.absoluteString containsString:#".pdf"])
{
[webView reload];
isPdfReloaded = YES;
}
else
{
isPdfReloaded = NO;
}
}
The Best solution is to migrate from UIWebView to WkWebView
I am new at ios development and I am trying to create an app in swift that will list all the files in the directory (all the files are PDF) and the user to be able to open them.
I have googling this for a the past two days and I am super confused. Can anyone suggest a tutorial or steps I would need to get this to work.
I have started with this in my ViewController.swift file:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
class func defaultManager()->NSFileManager{
}
}
I just don't know what to do next, very sad I know. I would appreciate any or all help.
Thanks,
J
let manager = NSFileManager.defaultManager()
var array = manager.contentsOfDirectoryAtPath(_ path: String,
error error: NSErrorPointer) -> [AnyObject]?
Swift 3.0 version
let manager = FileManager.default
let installed_files = try manager.contentsOfDirectory(atPath: "/Applications/")
Here is a more complete example and updated for Swift 2.
Add your files to a Folder (not a Group) in your project. Then use the following code to get a list of file names.
private func getListOfFileNames() -> Array<String> {
let docsPath = NSBundle.mainBundle().resourcePath! + "/DirectoryName"
let fileManager = NSFileManager.defaultManager()
let docsArray: Array<String>
do {
docsArray = try fileManager.contentsOfDirectoryAtPath(docsPath)
} catch {
print(error)
}
return docsArray
}
Maybe I am late to answer for you, but the answer may help others. I've found one library on github. This is a way to get access very easely to browse files. At here is example:
let fileBrowser = FileBrowser()
present(fileBrowser, animated: true, completion: nil)