How to scroll my UIViewController content? - ios

I've been trying to search for a swift version of a solution to no avail. I was wondering how I can make my view controller scroll content. Right now it's just static, and there's more content below, which I'm unable to scroll.
class ProfileViewController: UIViewController, UICollectionViewDelegateFlowLayout {
let postsId = "postsId"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(profileContainerView)
view.addSubview(profileTabCollection)
view.addSubview(containerForCollectionView)
view.addSubview(scrollView)
scrollView.widthAnchor.constraintEqualToConstant(view.frame.size.width).active = true
scrollView.heightAnchor.constraintEqualToConstant(view.frame.size.height).active = true
scrollView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
scrollView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
scrollView.userInteractionEnabled = true
scrollView.scrollEnabled = true
scrollView.delegate = self
containerForCollectionView.widthAnchor.constraintEqualToConstant(scrollView.frame.size.width).active = true
containerForCollectionView.heightAnchor.constraintEqualToConstant(scrollView.frame.size.height).active = true
containerForCollectionView.topAnchor.constraintEqualToAnchor(profileTabCollection.bottomAnchor).active = true
profileContainerView.widthAnchor.constraintEqualToConstant(scrollView.frame.size.width).active = true
profileContainerView.heightAnchor.constraintEqualToConstant(250).active = true
profileContainerView.centerXAnchor.constraintEqualToAnchor(scrollView.centerXAnchor).active = true
profileContainerView.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor).active = true
profileTabCollection.widthAnchor.constraintEqualToAnchor(profileContainerView.widthAnchor).active = true
profileTabCollection.heightAnchor.constraintEqualToConstant(50).active = true
profileTabCollection.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
profileTabCollection.topAnchor.constraintEqualToAnchor(profileContainerView.bottomAnchor).active = true
}
lazy var scrollView: UIScrollView = {
let sv = UIScrollView(frame: CGRectMake(0,0,1024,768))
sv.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height * 3)
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
let containerForCollectionView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.purpleColor()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

There are numerous reasons why this won't work. Since you don't provide enough information to be sure what it is, i'd recommend reading this (especially Duncan C's answer) carefully.
how do i alter the position of a UIImage inside a imageView in SWIFT

Write these lines in override layoutSubviews() method.
scrollView.widthAnchor.constraintEqualToAnchor(view.widthAnchor).active = true
scrollView.heightAnchor.constraintEqualToAnchor(view.heightAnchor).active = true
Make sure you created UIScrollView in your UIViewController, otherwise it won't work for sure.

If you are using swift 2.0 and you have used "UIScrollViewDelegate"- you need to add this method
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imgview
}
And if you are using swift 3.0 - you need this function
func viewForZooming( in scrollView: UIScrollView) -> UIView? {
return imgview
}

Related

How to dismiss the keyboard on a UITextView on a modally presented UITableView

Good Evening,
I am trying to dismiss a keyboard on a modally presented UITableView.
The UITextView is Created as a UINib and registered in the UITableView.
I tried to set the keyboard as "Dismiss On Drag" in storyboard, and nothing worked. I also wrote the following code and connected the UITextFieldDelegate on the UINib.
Here is the code in the UITextView Nib.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
View Did Load:
override func awakeFromNib() {
super.awakeFromNib()
textView.delegate = self
}
Use scroll view, this is an example because I don't understand well what do you to do in your project... First set the controller that include your textView to UIScrollViewDelegate, after that declare your objects and their attributes:
class YourController: UIViewController, UIScrollViewDelegate {
let scrollView2: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let textV: UITextView = {
let v = UITextView()
v.backgroundColor = .yourColor
v.isUserInteractionEnabled = true
v.textColor = .white
v.font = .systemFont(ofSize: 20, weight: .regular)
v.translatesAutoresizingMaskIntoConstraints = false
v.heightAnchor.constraint(equalToConstant: 1000).isActive = true // assign here the text view height to allow your scrollview to scroll
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
scrollView2.delegate = self
setupConstraints() // call the function for constraints
}
below viewDidLoad write constraint function:
fileprivate func setupConstraints() {
view.addSubview(scrollView2)
scrollView2.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView2.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView2.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView2.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView2.addSubview(textV)
textV.leadingAnchor.constraint(equalTo: scrollView2.leadingAnchor).isActive = true
textV.trailingAnchor.constraint(equalTo: scrollView2.trailingAnchor).isActive = true
textV.topAnchor.constraint(equalTo: scrollView2.topAnchor).isActive = true
textV.bottomAnchor.constraint(equalTo: scrollView2.bottomAnchor).isActive = true
// this is important for scrolling
textV.widthAnchor.constraint(equalTo: scrollView2.widthAnchor).isActive = true
}
now call scrollViewWillBeginDragging and assign interactive mode to your scrollview keyboardDismissMode:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
scrollView2.keyboardDismissMode = .interactive
}
This is the result

FSCalendar content getting squished when I switch scope to month

My FSCalendar's content shrinks when I switch its scope from week to month if there is a view that's constrained to its bottom anchor.
Here is a quick gif to show what exactly is happening
I have tried everything at this point. Using calendar.setScope() instead of calendar.scope =, constraining attachedToCalendarView.topAnchor to calendar.bottomAnchor calendar.contentView.bottomAnchor, and calendar.daysContainer.bottomAnchor, even turning attachedToCalendarView 's constraints on and off depending on whether it's week scope or scope month.
Not sure what else to try. Here is the code:
import UIKit
import FSCalendar
class TestController : UIViewController, FSCalendarDataSource, FSCalendarDelegate, FSCalendarDelegateAppearance {
fileprivate weak var calendar: FSCalendar!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setUp()
}
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
self.calendar.scope = FSCalendarScope.week
} else {
self.calendar.scope = FSCalendarScope.month
}
}
func setUp(){
let calendar = FSCalendar()
calendar.dataSource = self
calendar.delegate = self
self.calendar = calendar
self.calendar.scope = .week
self.calendar.locale = Locale(identifier: "en_EN")
self.calendar.calendarHeaderView.calendar.locale = Locale(identifier: "en_EN")
self.calendar.adjustsBoundingRectWhenChangingMonths = true
let testingView = UIView()
testingView.backgroundColor = .red
let attachedToCalendarView = UIView()
attachedToCalendarView.backgroundColor = .blue
view.addSubview(calendar)
view.addSubview(testingView)
view.addSubview(attachedToCalendarView)
self.calendar.translatesAutoresizingMaskIntoConstraints = false
self.calendar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
self.calendar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
self.calendar.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
self.calendar.heightAnchor.constraint(equalToConstant: 300).isActive = true
testingView.translatesAutoresizingMaskIntoConstraints = false
testingView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
testingView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
testingView.heightAnchor.constraint(equalToConstant: 20).isActive = true
attachedToCalendarView.translatesAutoresizingMaskIntoConstraints = false
attachedToCalendarView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Attaching this view's topAnchor to the calendar's bottom anchor
attachedToCalendarView.topAnchor.constraint(equalTo: self.calendar.contentView.bottomAnchor).isActive = true
attachedToCalendarView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
attachedToCalendarView.heightAnchor.constraint(equalToConstant: 20).isActive = true
// Title and button to toggle the calendar scope
self.navigationItem.title = "Test"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Toggle", style: .done, target: self, action: #selector(switchCalendarScope))
}
}
Well, I couldn't figure out how to fix the problem itself but I did find a workaround. I placed the calendar inside of an empty container view (just a simple UIView) and attached attachedToCalendarView to the container's bottomAnchor instead of the calendar's itself.
Do note however that using setScope, which animates the transition, still causes the same issue. For it to work you have to set it manually like calendar.scope = x
example:
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
// self.calendar.setScope(FSCalendarScope.week, animated: true) // this will cause the calendar to be squished again
self.calendar.scope = .week
movingConstraint.constant = view.safeAreaLayoutGuide.layoutFrame.size.height * -0.20
} else {
// self.calendar.setScope(FSCalendarScope.month, animated: true)
self.calendar.scope = .month
movingConstraint.constant = 0
}
}

Admob Native Advanced Not Clickable

I have created a custom View Class that inherits from GADNativeContentAdView Class. When I receive an advertisement and the delegate is called, I fill my custom view with the data as shown below.
Everything looks fine but the problem is that it is not clickable at all. I tried to set the actionbutton userinteraction to false, but still won't work.
I also tried to register using following:
-(void)registerAdView:(UIView *)adView
clickableAssetViews:(NSDictionary *)clickableAssetViews
nonclickableAssetViews:
(NSDictionary *)nonclickableAssetViews;
Any idea how to get it to work?
- (void)setNativeContent:(GADNativeContentAd *)nativeContent
{
self.nativeContentAd = nativeContent;
headlineLabel.text = nativeContent.headline;
bodyLabel.text = nativeContent.body;
advertiserImage.image = ((GADNativeAdImage *)nativeContent.images.firstObject).image;
[actionButton setTitle:nativeContent.callToAction forState:UIControlStateNormal];
if (nativeContent.logo && nativeContent.logo.image)
{
advertiserLogo.image = nativeContent.logo.image;
}
else
{
advertiserLogo.image = advertiserImage.image;
}
NSDictionary *clickableArea = #{GADNativeContentHeadlineAsset:headlineLabel, GADNativeContentImageAsset:advertiserImage, GADNativeContentCallToActionAsset:actionButton};
NSDictionary *nonClickableArea = #{GADNativeContentBodyAsset:bodyLabel};
[nativeContent registerAdView:self clickableAssetViews:clickableArea nonclickableAssetViews:nonClickableArea];
}
I finally figured out a way to make the entire native ad clickable without using a .xib. I subclassed GADNativeContentAdView and created a tappableOverlay view that I assigned to an unused asset view in its superclass. In this case, it was the callToActionView. Then I used the not-so-documented GADNativeContentAd.registerAdView() method:
- (void)registerAdView:(UIView *)adView
clickableAssetViews:(NSDictionary<GADNativeContentAdAssetID, UIView *> *)clickableAssetViews
nonclickableAssetViews: (NSDictionary<GADNativeContentAdAssetID, UIView *> *)nonclickableAssetViews;
Here's a Swift 4 example:
class NativeContentAdView: GADNativeContentAdView {
var nativeAdAssets: NativeAdAssets?
private let myImageView: UIImageView = {
let myImageView = UIImageView()
myImageView.translatesAutoresizingMaskIntoConstraints = false
myImageView.contentMode = .scaleAspectFill
myImageView.clipsToBounds = true
return myImageView
}()
private let myHeadlineView: UILabel = {
let myHeadlineView = UILabel()
myHeadlineView.translatesAutoresizingMaskIntoConstraints = false
myHeadlineView.numberOfLines = 0
myHeadlineView.textColor = .black
return myHeadlineView
}()
private let tappableOverlay: UIView = {
let tappableOverlay = UIView()
tappableOverlay.translatesAutoresizingMaskIntoConstraints = false
tappableOverlay.isUserInteractionEnabled = true
return tappableOverlay
}()
private let adAttribution: UILabel = {
let adAttribution = UILabel()
adAttribution.translatesAutoresizingMaskIntoConstraints = false
adAttribution.text = "Ad"
adAttribution.textColor = .white
adAttribution.textAlignment = .center
adAttribution.backgroundColor = UIColor(red: 1, green: 0.8, blue: 0.4, alpha: 1)
adAttribution.font = UIFont.systemFont(ofSize: 11, weight: UIFont.Weight.semibold)
return adAttribution
}()
override var nativeContentAd: GADNativeContentAd? {
didSet {
if let nativeContentAd = nativeContentAd, let callToActionView = callToActionView {
nativeContentAd.register(self,
clickableAssetViews: [GADNativeContentAdAssetID.callToActionAsset: callToActionView],
nonclickableAssetViews: [:])
}
}
}
init() {
super.init(frame: CGRect.zero)
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .white
isUserInteractionEnabled = true
callToActionView = tappableOverlay
headlineView = myHeadlineView
imageView = myImageView
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
addSubview(myHeadlineView)
addSubview(myImageView)
addSubview(adAttribution)
addSubview(tappableOverlay)
}
// override func updateConstraints() {
// ....
// }
}
Just be sure to pin the tappableOverlay to its superview edges so that they're the same size...in updateConstraints().
Inside the method simply you can create and place Ad in view hierarchy.
GADNativeContentAdView *contentAdView = [[NSBundle mainBundle] loadNibNamed:#"NativeAdView" owner:nil options:nil].firstObject;
After assigning the properties, associate the content Ad view with the content ad object. This is required to make the ad clickable.
contentAdView.nativeContentAd = nativeContentAd;
Only AdMob whitelisted publishers can use the registerAdView API :)
All publishers can use xib to create an ad view.
Don't forget to link custom GADUnifiedNativeAdView outlets to your UILabels, UIButtons and ImageViews, so GADUnifiedNativeAdView will know what to interact with
In my case it was cause I created my views without xib.
In this case just set mediaView property to your GADNativeAdView
here the minimum working code
final class EndBannerController: UIViewController {
private let adId: String
private let adView = GADNativeAdView()
private let mediaView = GADMediaView()
private var adLoader: GADAdLoader?
init(adId: String) {
self.adId = adId
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { return nil }
override func viewDidLoad() {
super.viewDidLoad()
adView.frame = view.bounds
view.addSubview(adView)
mediaView.frame = view.bounds
adView.mediaView = mediaView
adView.addSubview(mediaView)
let loader = GADAdLoader(
adUnitID: adId,
rootViewController: self,
adTypes: [.native],
options: nil
)
loader.delegate = self
self.adLoader = loader
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.loadBannerAd()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
adView.frame = view.bounds
mediaView.frame = view.bounds
}
private func loadBannerAd() {
let request = GADRequest()
request.scene = view.window?.windowScene
self.adLoader?.load(request)
}
}

UIFocusGuide UITableView and UIButton

I am having a hard time creating a UIFocusGuide that will jump from the UITableView to a UIButton. Here is the debugger context screenshot:
And here is the implementation:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
// add the focus guide
self.view.addLayoutGuide(focusGuide)
// add the anchors
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.button.leftAnchor).active = true
self.focusGuide.topAnchor.constraintEqualToAnchor(self.tableView.topAnchor).active = true
self.focusGuide.widthAnchor.constraintEqualToAnchor(self.button.widthAnchor).active = true
self.focusGuide.heightAnchor.constraintEqualToAnchor(self.tableView.heightAnchor).active = true
}
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
guard let nextFocusedView = context.nextFocusedView else { return }
switch nextFocusedView {
case self.button:
self.focusGuide.preferredFocusedView = self.button
case self.tableView:
self.focusGuide.preferredFocusedView = self.tableView
default:
self.focusGuide.preferredFocusedView = nil
}
}
The didUpdateFocusInContext function is never getting called when I am at the middle item of the UITableView or the end of the UITableView.
Add the focus guide to the button not self.view. You don't need override didUpdateFocusInContext For example:
var focusGuide = UIFocusGuide()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
// add the focus guide
self.button.addLayoutGuide(focusGuide)
// add the anchors
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.button.leftAnchor).active = true
self.focusGuide.topAnchor.constraintEqualToAnchor(self.tableView.topAnchor).active = true
self.focusGuide.widthAnchor.constraintEqualToAnchor(self.button.widthAnchor).active = true
self.focusGuide.heightAnchor.constraintEqualToAnchor(self.tableView.heightAnchor).active = true
}

View with two UIWebViews problems with height calculation?

I created a UIViewController having a view with two UIWebViews. My structure looks like this (I also use auto layout with SnapKit):
class MyIntViewController: UIViewController, UIWebViewDelegate {
private var scrollView: UIScrollView!
private var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
setupView()
}
private func setupView() {
scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
contentView = UIView()
contentView.backgroundColor = UIColor.greenColor()
scrollView.addSubview(contentView)
contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
make.width.equalTo(self.view.bounds.width)
}
// first webView
let contentWebView1 = UIWebView()
contentWebView1.scalesPageToFit = false
contentWebView1.delegate = self
contentWebView1.scrollView.bounces = false
contentWebView1.scrollView.scrollEnabled = false
contentView.addSubview(contentWebView1)
contentWebView1.snp_makeConstraints { (make) -> Void in
make.top.equalTo(contentView.snp_top)
make.left.equalTo(self.view.snp_left)
make.right.equalTo(self.view.snp_right)
make.height.equalTo(1)
}
// second webView
let contentWebView2 = UIWebView()
contentWebView2.scalesPageToFit = false
contentWebView2.delegate = self
contentWebView2.scrollView.bounces = false
contentWebView2.scrollView.scrollEnabled = false
contentView.addSubview(contentWebView2)
contentWebView2.snp_makeConstraints { (make) -> Void in
make.top.equalTo(contentWebView1.snp_bottom)
make.left.equalTo(self.view.snp_left)
make.right.equalTo(self.view.snp_right)
make.height.equalTo(1)
make.bottom.equalTo(self.contentView.snp_bottom)
}
// load content for contentWebView1 and contentWebView2
let path: String = NSBundle.mainBundle().bundlePath
let baseURL: NSURL = NSURL.fileURLWithPath(path)!
let myFileHtml: String = "HTWML for contentWebView1""
let myFileHtmlPlusCss = MMCssHelper.appendCustomCSS(myFileHtml)
contentWebView1.loadHTMLString(myFileHtmlPlusCss, baseURL: baseURL)
let path: String = NSBundle.mainBundle().bundlePath
let baseURL: NSURL = NSURL.fileURLWithPath(path)!
let myFileHtml: String = "HTWML for contentWebView2""
let myFileHtmlPlusCss = MMCssHelper.appendCustomCSS(myFileHtml)
contentWebView2.loadHTMLString(myFileHtmlPlusCss, baseURL: baseURL)
}
// MARK: UIWebViewDelegate
func webViewDidFinishLoad(webView: UIWebView) {
let webViewContentHeight: CGFloat = webView.scrollView.contentSize.height
println("webViewDidFinishLoad webViewContentHeight: \(webViewContentHeight)")
webView.snp_updateConstraints { (make) -> Void in
make.height.equalTo(webViewContentHeight)
}
}
}
When I have only one UIWebView on the page everything works fine the content of the UIWebView is calculated correctly in webViewDidFinishLoad. The problem occurs with two UIViewViews as shown above. The console output is this:
webViewDidFinishLoad webViewContentHeight: 1.0
webViewDidFinishLoad webViewContentHeight: 17.0
You can see that one webViewContentHeight is calculated correct while the other seems to be not calculated at all.
How do I get the height of a UIWebView calculated correctly when there are more than one UIWebViews on the page?

Resources