i'm trying to set custom image on page control dots. I have tried a custom class for this ,
class CustomPageControl: UIPageControl {
#IBInspectable var currentPageImage: UIImage?
#IBInspectable var otherPagesImage: UIImage?
override var numberOfPages: Int {
didSet {
updateDots()
}
}
override var currentPage: Int {
didSet {
updateDots()
}
}
override func awakeFromNib() {
super.awakeFromNib()
pageIndicatorTintColor = .clear
currentPageIndicatorTintColor = .clear
clipsToBounds = false
}
private func updateDots() {
for (index, subview) in subviews.enumerated() {
let imageView: UIImageView
if let existingImageview = getImageView(forSubview: subview) {
imageView = existingImageview
} else {
imageView = UIImageView(image: otherPagesImage)
imageView.center = subview.center
subview.addSubview(imageView)
subview.clipsToBounds = false
}
imageView.image = currentPage == index ? currentPageImage : otherPagesImage
}
}
private func getImageView(forSubview view: UIView) -> UIImageView? {
if let imageView = view as? UIImageView {
return imageView
} else {
let view = view.subviews.first { (view) -> Bool in
return view is UIImageView
} as? UIImageView
return view
}
}
}
After running this code i got this result in my VC,
enter image description here
I want this result from this,
enter image description here
Related
I'm trying to present full images in UIPageViewController. Each image is in separate view controller. I set contentMode to scaleAspectFill. The problem is that when I scroll back, two images merge like this https://i.imgur.com/CSgJLWq.png. I also tried to give width to every image but nothing changes
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "onboarding-concert")
imageView.contentMode = .scaleAspectFill
//imageView.frame.size.width = 400
return imageView
}()
private func setUI() {
view.addSubview(imageView)
imageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
view.addSubview(label)
label.snp.makeConstraints { make in
make.top.equalToSuperview().inset(90)
make.left.equalToSuperview().inset(40)
}
}
UIPageViewController code
class OnboardingViewController: UIPageViewController {
var pages = [UIViewController]()
let pageControl = UIPageControl()
let initialPage = 0
var pageControlBottomAnchor: NSLayoutConstraint?
override func viewDidLoad() {
setup()
style()
layout()
}
func setup() {
dataSource = self
delegate = self
pageControl.addTarget(self, action: #selector(pageControlTapped(_:)), for: .valueChanged)
let page1 = FirstPageViewController()
let page2 = SecondPageViewController()
let page3 = ThirdPageViewController()
pages.append(page1)
pages.append(page2)
pages.append(page3)
setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
}
func style() {
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = .black
pageControl.pageIndicatorTintColor = .systemGray2
pageControl.numberOfPages = pages.count
pageControl.currentPage = initialPage
pageControl.isUserInteractionEnabled = false
}
func layout() {
view.addSubview(pageControl)
pageControl.snp.makeConstraints { make in
make.width.equalToSuperview()
make.height.equalTo(20)
make.centerX.equalToSuperview()
}
pageControlBottomAnchor = view.bottomAnchor.constraint(equalToSystemSpacingBelow: pageControl.bottomAnchor, multiplier: 2)
pageControlBottomAnchor?.isActive = true
}
#objc func pageControlTapped(_ sender: UIPageControl) {
setViewControllers([pages[sender.currentPage]], direction: .forward, animated: true, completion: nil)
}
}
I'm struggling with problem in rotating image in a UIScrollView, my view hierarchy is described by:
I've an UIScrollView with an UIView as subview. The UIView has an UIImageView as subview.
I would be able to rotate the imageView inside it and then recalculate the contentSize of the scrollView in order to be able to pan over all of it.
I'm using this code:
//
// TestView.swift
//
import UIKit
class TestView: UIView {
// MARK: - properties
private var imageView: UIImageView?
private var zoomingView: UIView?
private var scrollView: UIScrollView?
private let kDecayRotation = "kDecayRotation"
var image: UIImage? {
didSet {
setupImageView(image)
}
}
// MARK: - init
convenience init(frame: CGRect, image: UIImage) {
self.init(frame: frame)
setupImageView(image)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.blueColor()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - methods
private func setupImageView(image: UIImage?) {
guard let image = image else { return }
imageView = UIImageView(image: image)
zoomingView = UIView(frame: imageView!.frame)
zoomingView?.addSubview(imageView!)
scrollView = UIScrollView(frame: frame)
scrollView?.backgroundColor = UIColor.redColor()
scrollView?.contentSize = zoomingView!.frame.size
scrollView?.addSubview(zoomingView!)
addSubview(scrollView!)
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
rotationGesture.delegate = self
scrollView?.addGestureRecognizer(rotationGesture)
}
#objc private func handleRotation(gestureRecognizer: UIRotationGestureRecognizer) {
adjustAnchorPointForGestureRecognizer(gestureRecognizer)
guard let targetView = zoomingView else { return }
if gestureRecognizer.state == .Began { }
else if gestureRecognizer.state == .Changed {
targetView.transform = CGAffineTransformRotate(targetView.transform, gestureRecognizer.rotation)
gestureRecognizer.rotation = 0
scrollView?.contentSize = targetView.frame.size
var targetFrame = targetView.frame
let targetBounds = targetView.bounds
targetFrame.origin.x = 0
targetFrame.origin.y = 0
targetView.frame = targetFrame
targetView.bounds = targetBounds
scrollView?.setNeedsDisplay()
} else if gestureRecognizer.state == .Cancelled || gestureRecognizer.state == .Ended { }
}
// MARK: - gesture helper
private func adjustAnchorPointForGestureRecognizer(gestureRecognizer: UIGestureRecognizer) {
guard let zoomingView = zoomingView else { return }
if gestureRecognizer.state == .Began {
let locationInView = gestureRecognizer.locationInView(zoomingView)
let locationInSuperview = gestureRecognizer.locationInView(zoomingView.superview)
zoomingView.layer.anchorPoint = CGPointMake(locationInView.x / zoomingView.bounds.size.width, locationInView.y / zoomingView.bounds.size.height)
zoomingView.center = locationInSuperview
}
}
}
extension TestView: UIGestureRecognizerDelegate {
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
The problem are:
The View doesn't rotate around the location of the finger (although I implemented the method to calculate the new anchorPoint)
The contentSize if recalculated correctly (only if I comment adjustAnchorPointForGestureRecognizer(gestureRecognizer) in handleRotation(_:)), but the image move across the scrollview in an horrible way. How can I get the effect of the image remain fixed (and rotate) and the contentSize being recalculated under of that in a 'silence' manner?
Edit 1
Here's the test image:
The code to use the TestView is:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
let sampleImage = UIImage(named: "testImage")
let testView = TestView(frame: view.frame, image: sampleImage!)
view.addSubview(testView)
}
}
I want to add a NSVisualEffectView inside my NSTextView, however when I add it, the text is below the NSVisualEffectView, so, how can i add the NSVisualEffectView below the text?
My code for OS X:
class myTextView: NSTextView {
override func awakeFromNib() {
let visualEffectView = NSVisualEffectView(frame: NSMakeRect(20, 20, 30, 18))
visualEffectView.material = NSVisualEffectMaterial.Dark
visualEffectView.blendingMode = NSVisualEffectBlendingMode.BehindWindow
self.addSubview(visualEffectView)
}
}
Maybe this will help you:
class MyTextView: NSTextView {
lazy var visualEffectView: NSVisualEffectView = {
let visualEffectView = NSVisualEffectView()
visualEffectView.material = NSVisualEffectMaterial.Dark
visualEffectView.blendingMode = NSVisualEffectBlendingMode.BehindWindow
return visualEffectView
}()
override func awakeFromNib() {
super.awakeFromNib()
self.drawsBackground = false
}
override func viewDidMoveToSuperview() {
super.viewDidMoveToSuperview()
if let containerView = self.superview?.superview?.superview {
containerView.addSubview(self.visualEffectView, positioned: .Below, relativeTo: self)
}
}
override func resizeSubviewsWithOldSize(oldSize: NSSize) {
super.resizeSubviewsWithOldSize(oldSize)
if let superview = self.superview?.superview {
self.visualEffectView.frame = superview.frame
}
}
}
It's a text view in a scroll view and clip view so you have to get its superview.
self.sendSubviewToBack(visualEffectView)
But the visual effect needs to be applied to the parent container, not the text view itself, so if I'm understanding your code correctly you need to move this whole thing up to whatever the parent view is.
I was trying to make a program where you click an image and it segues to show it on full screen, scaled so that there is no whitespace around (CS 193P assignment 4 task 7 for anyone who knows it). View controllers are embedded in navigation controller. It turns out that the program crashes with EXC_BAD_ACCESS (code = EXC_I386_GPFLT) when I hit the back button in the navigation bar.
I found out that I can prevent this crash by scrolling to the top of the image and then going back. Another way to completely prevent it is to comment out the line that sets scrollView's zoomScale. Here's the code I used for loading and handling the image:
import UIKit
class ImageViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView! {
didSet {
scrollView.contentSize = imageView.frame.size
scrollView.delegate = self
scrollView.minimumZoomScale = 0.03
scrollView.maximumZoomScale = 5.0
}
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
var imageURL: NSURL? {
didSet {
image = nil
if view.window != nil {
fetchImage()
}
}
}
private func fetchImage() {
if let url = imageURL {
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos, 0)) {
let imageData = NSData(contentsOfURL: url)
dispatch_async(dispatch_get_main_queue()) {
if url == self.imageURL {
if imageData != nil {
self.image = UIImage(data: imageData!)
} else {
self.image = nil
}
}
}
}
}
}
private var imageView = UIImageView()
private var image: UIImage? {
get { return imageView.image }
set {
imageView.image = newValue
imageView.sizeToFit()
scrollView?.contentSize = imageView.frame.size
scrollViewDidScrollOrZoom = false
autoScale()
}
}
private var scrollViewDidScrollOrZoom = false
private func autoScale() {
if scrollViewDidScrollOrZoom {
return
}
if let sv = scrollView {
if image != nil {
sv.zoomScale = max(sv.bounds.size.width / image!.size.width, sv.bounds.size.height / image!.size.height)
sv.contentOffset = CGPoint(x: (imageView.frame.size.width - sv.frame.size.width) / 2, y: (imageView.frame.size.height - sv.frame.size.height) / 2)
scrollViewDidScrollOrZoom = false
}
}
}
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView!, atScale scale: CGFloat) {
scrollViewDidScrollOrZoom = true
}
func scrollViewDidScroll(scrollView: UIScrollView) {
scrollViewDidScrollOrZoom = true
}
override func viewDidLoad() {
super.viewDidLoad()
scrollView.addSubview(imageView)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if image == nil {
fetchImage()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
autoScale()
}
}
I tried to make a simple application which segues into ImageViewController from a View Controller that has only a button and is embedded in navigation controller, and the crash is still the same. Here's the code I used in the first view controller:
class FirstViewController: UIViewController {
let url = NSURL(string: "https://developer.apple.com/swift/images/swift-og.png")
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Show Full Image" {
let ivc = segue.destinationViewController as! ImageViewController
ivc.imageURL = url
}
}
}
What causes this and am I doing something wrong here? I'm using Xcode 6.3 beta 3.
It seems that the problem is actually with the scrollView's delegate, as I was able to fix this with adding
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
scrollView.delegate = nil
}
I have correctly set up a UIPageViewController.
I would like to check the itemIndex of each views and display content accordingly. Here's what the code looks like:
var itemIndex: Int = 0 {
didSet {
if itemIndex == 1 {
println("Character is Lela!")
}
if itemIndex == 0 {
println("Character is John!")
}
}
}
What I tried
I run the app
Console Output: "Character is John!"
I swipe forward (to the right)
Console Output: "Character is Lela!"
I swipe back (to the left)
There is no output!
I swipe forward
There is no output! Again!
What is this due to? Here's the full code:
import UIKit
var currentIndex: Int = 0
var nextIndex: Int = 0
class ProView: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController?
let characterImages = ["character1", "character2", "character1", "character2", "character1", "character2", "character1", "character2"]
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
character = 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Forward, check if this IS NOT the last controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
// Check if there is another view
if itemController.itemIndex+1 < characterImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
// Check if this IS NOT the first controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex < 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < characterImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = characterImages[itemIndex]
return pageItemController
}
return nil
}
func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if characterImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController?.didMoveToParentViewController(self)
}
func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return characterImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
// BETA
func pageViewController(PageItemController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers pageViewController: [AnyObject],
transitionCompleted completed: Bool)
{
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
return;
}
// This is where you would know the page number changed and handle it appropriately
}
}
class PageItemController: UIViewController {
#IBOutlet weak var imageCharacterChoose: UIImageView!
var itemIndex: Int = 0 {
didSet {
if itemIndex == 1 {
println("Character is Lela!")
}
if itemIndex == 0 {
println("Character is John!")
}
}}
var imageName: String = "" {
didSet {
if let imageView = imageCharacterChoose {imageCharacterChoose.image = UIImage(named: imageName)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
imageCharacterChoose!.image = UIImage(named: imageName)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
didSet and willSet will only get called when you change the value of the corresponding variable, so in your case you should somewhere in your code do itemIndex = 1, or some other value to trigger the functions.